March 09, 2018

sieve.go 代码分析

说明

代码来源于 golang.org/doc/play/sieve.go ,主要演示了 goroutine 和 channel 的用法,用来计算前 N 个质数

代码

// A concurrent prime sieve

package main

import "fmt"

// Send the sequence 2, 3, 4, ... to channel 'ch'.
func Generate(ch chan<- int) {
	for i := 2; ; i++ {
		ch <- i // Send 'i' to channel 'ch'.
	}
}

// Copy the values from channel 'in' to channel 'out',
// removing those divisible by 'prime'.
func Filter(in <-chan int, out chan<- int, prime int) {
	for {
		i := <-in // Receive value from 'in'.
		if i%prime != 0 {
			out <- i // Send 'i' to 'out'.
		}
	}
}

// The prime sieve: Daisy-chain Filter processes.
func main() {
	ch := make(chan int) // Create a new channel.
	go Generate(ch)      // Launch Generate goroutine.
	for i := 0; i < 10; i++ {
		prime := <-ch
		fmt.Println(prime)
		ch1 := make(chan int)
		go Filter(ch, ch1, prime)
		ch = ch1
	}
}

分析

一共经过 10 层循环,每次的循环生成一个新的 goroutine,消费之前的 channel,生成新的 channel ch1

  • 打印第一个质数 2
  • Routine0 从原始 ch(2,3,4,5,6,7,8…) 中过滤出不能被 2 整除地数据写入 ch1(3,5,7,9,…)
  • 打印第二个质数 3
  • Routine1 从 ch (5,7,9,11…) 中过滤出不能被被 3 整除的数据写入 ch1 。此时 n 不能被 2 整除也不能被 3 整除
  • 打印第三个质数 5
  • Routine2 从 ch (7,11,13,17…) 中过滤出不能被 5 整除的数据写入 ch1。此时 n 不能被 2,3,5 整除
  • (略)
  • 当 10 层循环结束时,打印了前 10 个质数,此时退出时仍有 11 个 channel 和 10 个 goroutine 存在

原理

  1. 每个大于 1 的整数,都可以被分解为有限个质数的乘积
  2. 判定一个数是否为质数,只需要看它能否为小于它的质数整除

朴素版实现

package main

import "fmt"

func main() {
    prime := []int{2}
    x := 3
    for len(prime) < 10 {
        for _, v := range prime {
            if x%v == 0 {
                goto NEXT
            }
        }
        prime = append(prime, x)
    NEXT:
        x++
    }

    fmt.Println(prime)
}