英文:
Correct the implemention of my goroutines
问题
背景:我正在尝试实现一个逻辑,找到一个能够被1到20之间所有数字整除的最小正数。我已经实现了一个顺序版本,并得到了答案232792560。
问题:当我尝试在这个问题上构建一些并发性(参见未注释的代码块)时,程序可以运行,但从未显示任何结果。你们中的任何人可以指导我在哪里出错了吗?
注意:我对golang非常陌生;我知道这不是并发性的最佳问题,因为不能保证我会得到最小的正数作为第一个结果。然而,出于好奇,我还是尝试了一下。
package main
import (
"fmt"
"sync"
)
func divide(num int) bool {
for i := 1; i <= 20; i++ {
if num%i != 0 {
return false
}
}
return true
}
func main() {
num := 0
//简单函数
/*for {
num++;
result := divide(num)
if result {
fmt.Println("Smallest number is: ", num)
break
}
}*/
//go-routine
var wg sync.WaitGroup
for {
num++
wg.Add(1)
go func(x int) {
result := divide(x)
if result {
fmt.Println("Smallest number is: ", x)
defer wg.Done()
}
}(num)
}
wg.Wait()
fmt.Println("End.")
}
英文:
Background: I am trying to implement a logic which find the smallest positive number that is evenly divisible by all of the numbers from 1 to 20. I have implemented a sequential version and got an answer as 232792560.
Question: When I try to build some concurrency on this problem ( see uncommented block of code), it does run but never shows any result. Can anyone of you guide me on where I am going wrong?
Note: I am very much new to golang; and I am aware that, this is not the best problem for concurrency since there is no guarantee that I will have the smallest positive number as first result. However, I tried it out of my curiosity.
package main
import(
"fmt"
)
func divide(num int) bool {
for i := 1; i <= 20; i++ {
if num % i != 0 {
return false
}
}
return true
}
func main() {
num:=0
//simple function
/*for {
num++;
result := divide(num)
if result {
fmt.Println("Smallest number is: ", num)
break
}
}*/
//go-routine
//go-routine
var wg sync.WaitGroup
for {
num++;
wg.Add(1)
go func(x int) {
result := divide(x)
if result {
fmt.Println("Smallest number is: ", x)
defer wg.Done()
}
}(num)
}
wg.Wait()
fmt.Println("End.")
}
答案1
得分: 1
启动无限数量的goroutine是没有意义的-它的性能会比非并发解决方案差。你可以尝试使用"worker pool"模式并批量计算。
package main
import (
"fmt"
"runtime"
)
func divide(num int) bool {
for i := 1; i <= 20; i++ {
if num%i != 0 {
return false
}
}
return true
}
const step = 100000
func main() {
result := make(chan int)
jobs := make(chan int)
for w := 0; w < runtime.NumCPU(); w++ {
go func() {
for n := range jobs {
for i := n; i < n+step; i++ {
if divide(i) {
result <- i
}
}
}
}()
}
num := 1
for {
select {
case jobs <- num:
num += step
case x := <-result:
fmt.Println("最小的数是:", x)
return
}
}
}
英文:
It doesn't make sense to launch unlimited number of goroutines - it will be performing worse, than non-concurrent solution. You can try to use a "worker pool" pattern and batch the calculations.
<!-- language: go -->
package main
import (
"fmt"
"runtime"
)
func divide(num int) bool {
for i := 1; i <= 20; i++ {
if num%i != 0 {
return false
}
}
return true
}
const step = 100000
func main() {
result := make(chan int)
jobs := make(chan int)
for w := 0; w < runtime.NumCPU(); w++ {
go func() {
for n := range jobs {
for i := n; i < n+step; i++ {
if divide(i) {
result <- i
}
}
}
}()
}
num := 1
for {
select {
case jobs <- num:
num += step
case x := <-result:
fmt.Println("Smallest number is:", x)
return
}
}
}
答案2
得分: 1
对你的问题采用不同的方法是使用过滤器。这个想法是通过使用通道将一系列的goroutine链接起来,并让它们过滤掉不满足某个条件的所有值。在你的情况下,你想要过滤掉不能被某个数整除的数字。代码如下:
package main
import (
"fmt"
)
func main() {
in := make(chan int)
tmp := in
// 创建过滤器goroutine
for i := 1; i <= 20; i++ {
tmp = makeDivisor(i, tmp)
}
running := true
// 现在输入所有的数字...
for i := 1; running; i++ {
select {
// 检查数字是否通过了所有的过滤器。
case k := <-tmp:
fmt.Printf("Answer is %d\n", k)
close(in)
running = false
// 否则输入下一个数字。
case in <- i:
}
}
}
func makeDivisor(n int, c chan int) chan int {
cc := make(chan int)
go func() {
for i := range c {
if i%n == 0 {
cc <- i
}
}
close(cc)
}()
return cc
}
你可以在playground上查看它。(请注意,playground无法处理20个过滤器,但你也可以在本地尝试。)
请注意,前面的过滤器要做的工作比后面的过滤器要多得多。你可以启动更多的第一个过滤器来解决这个问题,尽管整数除法非常快,但整个过程可能受到通道通信的限制。
它有效吗?
答案是232792560
是的。
英文:
A different approach to your problem would be filters. The idea is to chain a bunch of goroutines with channels and make them filter all values that don't pass some test. In your case you want to filter numbers that are not evenly divisible by some number. This is how it looks:
package main
import (
"fmt"
)
func main() {
in := make(chan int)
tmp := in
// Create filter-goroutines
for i := 1; i <= 20; i++ {
tmp = makeDivisor(i, tmp)
}
running := true
// Now feed in all the numbers...
for i := 1; running; i++ {
select {
// Check if a number passed all filters.
case k := <-tmp:
fmt.Printf("Answer is %d\n", k)
close(in)
running = false
// Otherwise feed in the next.
case in <- i:
}
}
}
func makeDivisor(n int, c chan int) chan int {
cc := make(chan int)
go func() {
for i := range c {
if i%n == 0 {
cc <- i
}
}
close(cc)
}()
return cc
}
Here it is on the playground. (Note that the playground won't work with 20 filters, but try it locally too.)
Note that the first filters have much more work to do than the ones further into the chain. You could launch more of the first filters to solve this, although the integer-division is pretty fast and this whole thing might be limited by channel communication at the moment.
Does it work?
> Answer is 232792560
Yes.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论