1
0
mirror of https://github.com/golang/go synced 2024-11-21 14:14:40 -07:00

Include an Eratosthenesque concurrent prime sieve to go along with the "naive" version.

R=r
CC=golang-dev
https://golang.org/cl/218046
This commit is contained in:
Anh Hai Trinh 2010-02-24 16:21:16 +11:00 committed by Rob Pike
parent ca3bee3df5
commit 16192c2d10
2 changed files with 174 additions and 2 deletions

View File

@ -4,8 +4,9 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// This version generates up to 100 and checks the results.
// With a channel, of course.
// Generate primes up to 100 using channels, checking the results.
// This sieve consists of a linear chain of divisibility filters,
// equivalent to trial-dividing each n by all primes p ≤ n.
package main

171
test/chan/sieve2.go Normal file
View File

@ -0,0 +1,171 @@
// $G $D/$F.go && $L $F.$A && ./$A.out
// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Generate primes up to 100 using channels, checking the results.
// This sieve is Eratosthenesque and only considers odd candidates.
// See discussion at <http://blog.onideas.ws/eratosthenes.go>.
package main
import (
"container/heap"
"container/ring"
"container/vector"
)
// Return a chan of odd numbers, starting from 5.
func odds() chan int {
out := make(chan int, 50)
go func() {
n := 5
for {
out <- n
n += 2
}
}()
return out
}
// Return a chan of odd multiples of the prime number p, starting from p*p.
func multiples(p int) chan int {
out := make(chan int, 10)
go func() {
n := p * p
for {
out <- n
n += 2 * p
}
}()
return out
}
type PeekCh struct {
head int
ch chan int
}
// Heap of PeekCh, sorting by head values.
type PeekChHeap struct {
*vector.Vector
}
func (h *PeekChHeap) Less(i, j int) bool {
return h.At(i).(*PeekCh).head < h.At(j).(*PeekCh).head
}
// Return a channel to serve as a sending proxy to 'out'.
// Use a goroutine to receive values from 'out' and store them
// in an expanding buffer, so that sending to 'out' never blocks.
func sendproxy(out chan<- int) chan<- int {
proxy := make(chan int, 10)
go func() {
n := 16 // the allocated size of the circular queue
first := ring.New(n)
last := first
var c chan<- int
var e int
for {
c = out
if first == last {
// buffer empty: disable output
c = nil
} else {
e = first.Value.(int)
}
select {
case e = <-proxy:
last.Value = e
if last.Next() == first {
// buffer full: expand it
last.Link(ring.New(n))
n *= 2
}
last = last.Next()
case c <- e:
first = first.Next()
}
}
}()
return proxy
}
// Return a chan int of primes.
func Sieve() chan int {
// The output values.
out := make(chan int, 10)
out <- 2
out <- 3
// The channel of all composites to be eliminated in increasing order.
composites := make(chan int, 50)
// The feedback loop.
primes := make(chan int, 10)
primes <- 3
// Merge channels of multiples of 'primes' into 'composites'.
go func() {
h := &PeekChHeap{new(vector.Vector)}
min := 15
for {
m := multiples(<-primes)
head := <-m
for min < head {
composites <- min
minchan := heap.Pop(h).(*PeekCh)
min = minchan.head
minchan.head = <-minchan.ch
heap.Push(h, minchan)
}
for min == head {
minchan := heap.Pop(h).(*PeekCh)
min = minchan.head
minchan.head = <-minchan.ch
heap.Push(h, minchan)
}
composites <- head
heap.Push(h, &PeekCh{<-m, m})
}
}()
// Sieve out 'composites' from 'candidates'.
go func() {
// In order to generate the nth prime we only need multiples of
// primes ≤ sqrt(nth prime). Thus, the merging goroutine will
// receive from 'primes' much slower than this goroutine
// will send to it, making the buffer accumulate and block this
// goroutine from sending, causing a deadlock. The solution is to
// use a proxy goroutine to do automatic buffering.
primes := sendproxy(primes)
candidates := odds()
p := <-candidates
for {
c := <-composites
for p < c {
primes <- p
out <- p
p = <-candidates
}
if p == c {
p = <-candidates
}
}
}()
return out
}
func main() {
primes := Sieve()
a := []int{2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97}
for i := 0; i < len(a); i++ {
if x := <-primes; x != a[i] {
panic(x, " != ", a[i])
}
}
}