生成Golang中的幂集的代码给出了错误的结果

huangapple go评论70阅读模式
英文:

Code to generate powerset in Golang gives wrong result

问题

下面是修复错误并以Go语言惯用方式编写的代码:

package main

import (
	"fmt"
)

func main() {
	set := []string{"A", "B", "C", "D", "E"}
	powerSet := PowerSet(set)
	for _, s := range powerSet {
		fmt.Println(s)
	}
}

func PowerSet(set []string) [][]string {
	var powerSet [][]string
	powerSet = append(powerSet, []string{})
	for _, element := range set {
		length := len(powerSet)
		for i := 0; i < length; i++ {
			newSet := make([]string, len(powerSet[i]))
			copy(newSet, powerSet[i])
			newSet = append(newSet, element)
			powerSet = append(powerSet, newSet)
		}
	}
	return powerSet
}

修复的代码会正确生成幂集,并且使用了Go语言的惯用方式。修复的关键在于在生成新集合时,需要创建一个新的切片并复制现有集合的元素,以避免共享底层数组导致结果错误。

英文:

Next code in Golang to generate powerset produces wrong result on input {&quot;A&quot;, &quot;B&quot;, &quot;C&quot;, &quot;D&quot;, &quot;E&quot;}. I see [A B C E E] as the last generated set.

<!-- language: go -->

package main

import (
    &quot;fmt&quot;
)

func main() {
	for _, s := range PowerSet([]string{&quot;A&quot;, &quot;B&quot;, &quot;C&quot;, &quot;D&quot;, &quot;E&quot;}) {
		fmt.Println(s)	
	}	
}

func PowerSet(set []string) [][]string {
	var powerSet [][]string
	powerSet = append(powerSet, make([]string, 0))
	for _, element := range set {
		var moreSets [][]string
		for _, existingSet := range powerSet {
			newSet := append(existingSet, element)
			moreSets = append(moreSets, newSet)
		}
		powerSet = append(powerSet, moreSets...)
	}
	return powerSet
}

How to fix it? How to write it idiomatically in Go?

答案1

得分: 3

你的程序问题不在于算法本身,而是这一行代码:

newSet := append(existingSet, element)

你不应该使用append并将其赋值给另一个变量。

正如文档所述(我强调一下),"append内置函数将元素追加到切片的末尾。如果切片有足够的容量,目标切片将被重新切片以容纳新元素。如果没有足够的容量,将分配一个新的底层数组。"。

因此,可能会出现newSet := append(existingSet, element)实际上修改了existingSet本身的情况,这会破坏你的逻辑。

如果你将其改为创建一个新的数组并向其中追加,它将按你的预期工作。

newSet := make([]string, 0)
newSet = append(newSet, existingSet...) 
newSet = append(newSet, element)
英文:

The problem with your program is not the algorithm itself but this line:

newSet := append(existingSet, element)

You should not append and assign to a different variable.

As the documentation states (emphasis mine), "The append built-in function appends elements to the end of a slice. If it has sufficient capacity, the destination is resliced to accommodate the new elements. If it does not, a new underlying array will be allocated.".

So, there might be cases where newSet := append(existingSet, element) will actually modify existingSet itself, which would break your logic.

If you change that to instead create a new array and append to that one, it works as you expect it.

newSet := make([]string, 0)
newSet = append(newSet, existingSet...) 
newSet = append(newSet, element)

答案2

得分: 1

例如,您可以使用像这个算法一样的算法:https://stackoverflow.com/a/2779467/3805062。

func PowerSet(original []string) [][]string {
    powerSetSize := int(math.Pow(2, float64(len(original))))
    result := make([][]string, 0, powerSetSize)

    var index int
    for index < powerSetSize {
        var subSet []string

        for j, elem := range original {
            if index& (1 << uint(j)) > 0 {
                subSet = append(subSet, elem)
            }
        }
        result = append(result, subSet)
        index++
    }
    return result
}
英文:

For instance, you can use algorithm like this one: https://stackoverflow.com/a/2779467/3805062.

func PowerSet(original []string) [][]string {
    powerSetSize := int(math.Pow(2, float64(len(original))))
    result := make([][]string, 0, powerSetSize)

    var index int
    for index &lt; powerSetSize {
	    var subSet []string

	    for j, elem := range original {
		    if index&amp; (1 &lt;&lt; uint(j)) &gt; 0 {
			    subSet = append(subSet, elem)
		    }
	    }
	    result = append(result, subSet)
	    index++
    }
    return result
}

答案3

得分: 1

以下是翻译好的内容:

详细说明了 @eugenioy 的答案。
请查看线程。这是一个可工作的示例:https://play.golang.org/p/dzoTk1kimf

func copy_and_append_string(slice []string, elem string) []string {
    // 错误:return append(slice, elem)
    return append(append([]string(nil), slice...), elem)
}

func PowerSet(s []string) [][]string {
    if s == nil {
        return nil
    }
    r := [][]string{[]string{}}
    for _, es := range s {
        var u [][]string
        for _, er := range r {
            u = append(u, copy_and_append_string(er, es))
        }
        r = append(r, u...)
    }
    return r
}
英文:

Elaborating on @eugenioy's answer.
Look at this thread. Here is a working example : https://play.golang.org/p/dzoTk1kimf

func copy_and_append_string(slice []string, elem string) []string {
	// wrong: return append(slice, elem)
	return append(append([]string(nil), slice...), elem)
}

func PowerSet(s []string) [][]string {
	if s == nil {
		return nil
	}
	r := [][]string{[]string{}}
	for _, es := range s {
		var u [][]string
		for _, er := range r {
			u = append(u, copy_and_append_string(er, es))
		}
		r = append(r, u...)
	}
	return r
}

huangapple
  • 本文由 发表于 2017年7月24日 01:30:14
  • 转载请务必保留本文链接:https://go.coder-hub.com/45267983.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定