发布时间:2025-12-09 11:49:10 浏览次数:2
在学习 go 并发的过程中,意外看到了菊花链的代码,一开始基本上对于整体流程摸不着头脑,经过查阅资料和自己苦思,最终找到了两个关键点,捋清了整个执行过程
代码:
func TestFunc5(t *testing.T) {/* * 利用信道菊花链筛法求某一个整数范围的素数 * 筛法求素数的基本思想是:把从1开始的、某一范围内的正整数从小到大顺序排列, * 1不是素数,首先把它筛掉。剩下的数中选择最小的数是素数,然后去掉它的倍数。 * 依次类推,直到筛子为空时结束 */const max = 100 // 找出100以内的所有素数nums := xrange() // 初始化一个整数生成器number := <-nums // 从生成器中抓一个整数(2), 作为初始化整数for number <= max { // number作为筛子,当筛子超过max的时候结束筛选fmt.Println(number) // 打印素数, 筛子即一个素数nums = filter(nums, number) //筛掉number的倍数number = <-nums // 更新筛子}}func filter(in chan int, number int) chan int {// 输入一个整数队列,筛出是number倍数的, 不是number的倍数的放入输出队列// in: 输入队列out := make(chan int)go func() {for {i := <-in // 从输入中取一个if i%number != 0 {out <- i // 放入输出信道}}}()return out}func xrange() chan int { // 从2开始自增的整数生成器var ch chan int = make(chan int)go func() { // 开出一个goroutinefor i := 2; ; i++ {ch <- i // 直到信道索要数据,才把i添加进信道}}()return ch}代码是从网络上复制,然后放在本地测试文件中进行调试的,自己运行的话可以把 TestFunc 改成 main
对于菊花链的代码,从逻辑上来说并不复杂:
- 采用的是埃式筛,将 2-n 的整数按照从小到大的顺序排列,以 2 为开始的素数,3/2 不能整除是素数,5/2 且 5/3 不能整除是素数,以此类推,不断从小到大除以已经得出的素数,都不能整除,意味着它没有可因式分解的余地,也就是素数;代码也有改进的空间,即使用欧式筛,去除不必要的筛选(这个我还没研究,感兴趣的可以自己看看);
对于菊花链,对我个人而言,最关键的两个点在于:
下面即是我捋出来的整个执行过程:
nums = filter(nums, number) 将 xrange channel 和 2 传递给 filter;nums = out,然后 number = <- nums 更新 number 为 3;两个关键点卡住了我很长时间,第一点很基础但一时忽略了,而第二点尤为重要,让我对整个执行过程总是隔着一层雾。还是需要更多的学习与实践,记录下分析结果,与君共勉。