algorithm - 递归算法中的 Go channel 导致重复值

标签 algorithm go recursion concurrency

起点

我有一个 Heap's algorithm 的实现在 Go 中创建集合的所有排列:

package main

import "fmt"

func main() {
  set := []string{"a", "b", "c"}
  Permutation(set, len(set))
}

func Permutation(a []string, k int) {
  if k == 1 {
    fmt.Println(a)
  }
  for i := 0; i < k; i++ {
    Permutation(a, k-1)
    if k%2 == 0 {
      a[i], a[k-1] = a[k-1], a[i]
    } else {
      a[0], a[k-1] = a[k-1], a[0]
    }
  }
}

输出是:

[a b c]
[b a c]
[c a b]
[a c b]
[b c a]
[c b a]

它打印出输入集的正确六个排列,所以一切正常。

问题

但现在我想将排列返回给调用者,而不是在函数中打印出来。

我尝试通过将 channel 传递给函数然后将每个排列发送到 channel 而不是打印它来做到这一点:

package main

import "fmt"

func main() {
  set := []string{"a", "b", "c"}
  c := make(chan []string)

  go Permutation(set, len(set), c)

  for s := range c {
    fmt.Println(s)
  }
}

func Permutation(a []string, k int, c chan []string) {
  if k == 1 {
    c <- a          // <<<========= Main change
  }
  for i := 0; i < k; i++ {
    Permutation(a, k-1, c)
    if k%2 == 0 {
      a[i], a[k-1] = a[k-1], a[i]
    } else {
      a[0], a[k-1] = a[k-1], a[0]
    }
  }
  if k == len(a) {
    close(c)
  }
}

但是现在输出是这样的:

[b a c]
[b a c]
[a c b]
[a c b]
[c b a]
[c b a]

仍然有六个排列,但有重复的相同排列。

为什么会这样?

从起点来看,算法似乎是正确的,主要变化是 a 的值被发送到 channel ,而不是在指定的行中打印出来Permutation 函数体。

最佳答案

这样,您将 slice a 发送到主 goroutine:

  if k == 1 {
    c <- a
  }

当 main goroutine 正在努力打印它时,您开始修改 a。所以 slice 在您打印时正在修改。

您可以发送一份副本:

if k == 1 {
   x := make([]string, len(a))
   copy(x,a)
   c <- x
}

关于algorithm - 递归算法中的 Go channel 导致重复值,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58326271/

相关文章:

java - 排列数组元素最大化距离 vector Java

具有超指数运行时间的算法?

pdf - 使用 golang 从 PDF 中提取单词?

python - 埃拉托色尼递归筛不返回任何内容

javascript - 递归循环对象的对象

java - GraphStream 中更具可读性的图形 - JAVA

c++ - 这种模式有什么优雅的解决方案?多级搜索

c++ - 为什么阶乘递归函数比普通阶乘函数效率低?

Go:CSV NewReader 未获取正确的字段数

不提供 http 服务时的 golang 客户端负载均衡器