Go Programming: Generating Combinations

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

Go Programming: Generating Combinations

问题

这是一份作业。

我正在做一个项目,其中一个非常小的部分(一旦我完成这个部分,它基本上就是项目其余部分的先决条件)是使用Go协程生成一些组合。

我目前的代码如下:

package bruteforce

// GenerateCombinations是一个迭代函数。给定一个字母表和一个长度,它将生成字母表中指定长度的所有可能组合。

// 它可以通过使用range语句来消费:
//
//	for combination := range GenerateCombinations(alphabet, length) {
//		process(combination)
//	}
//
func GenerateCombinations(alphabet string, length int) <-chan string {

    if length == 0 {
        ch := make(chan string)
        close(ch)
        return ch
    }

    ch := make(chan string)

    go func() {
        defer close(ch)
        for _, char := range alphabet {
            for subCombination := range GenerateCombinations(alphabet, length-1) {
                ch <- string(char) + subCombination
            }
        }
    }()

    return ch
}

我真的完全不明白这个代码。你可以看到,那里有一些由讲师提供的伪代码,但是它的实现让我很困惑。

示例输入/输出可能是这样的:

如果字母表是{0, 1},密码长度为2,那么它需要生成{0, 1, 00, 01, 10, 11}。

我感谢所有的建议,但请理解“初学者”这个词远不能描述我在Go方面的能力。像“使用通道”这样的说法对我一点帮助都没有。解释才是我的朋友...除了“使用通道”之外,我从我的教授那里并没有得到太多帮助。

英文:

This is homework

I'm working on a project, and a very small (very small, once I get this working...it's basically the pre-req for the rest of the project) part of it is to generate some combinations using a Go routine.

The code I have is thusly:

package bruteforce

// GenerateCombinations is an iterator function.  Given an alphabet and a
// length, it will generate every possible combination of the letters in
// alphabet of the specified length.
//
// It is meant to be consumed by using the range clause:
//
//	for combination := range GenerateCombinations(alphabet, length) {
//		process(combination)
//	}
//
func GenerateCombinations(alphabet string, length int) &lt;-chan string {

GenerateCombinations(alphabet, length):
if length == 0:
    yield &quot;&quot;
else:
    for i := range alphabet{
        for j := range GenerateCombinations(alphabet, length-1){
            i + j
        }
    }

    return nil
}

I seriously do not get this at all. As you can see, there is some instructor-provided pseudo code there, but the implementation of it is frying my brain.

Example I/O would be something like this:

If the alphabet is {0, 1} and the password was length 2, then it would need to generate {0, 1, 00, 01, 10, 11}.

I appreciate all suggestions, but please recognize that the term "beginner" doesn't begin to describe my competency with go. Saying things like "use channels" doesn't help me at all. Explanations are my friend... something that I haven't had a lot of luck getting out of my professor aside from "use channels."

答案1

得分: 8

你的老师已经暗示你应该使用通道而不是返回一个大数组。通过这种方式解决问题,你不需要存储包含所有组合的大块数据,而是将不同的迭代传递给函数,并逐个处理它们。

我们可以从GenerateCombinations返回的是chan string而不是[]string来看出你老师的提示:

func GenerateCombinations(alphabet string, length int) <-chan string

这也意味着该函数必须:1)创建一个通道来返回结果,2)启动一个goroutine来将迭代结果发送到通道中。这个函数的实现大致如下:

func GenerateCombinations(alphabet string, length int) <-chan string {
    c := make(chan string)

    // 启动一个独立的goroutine来生成所有的组合,并将它们发送到通道c中
    go func(c chan string) {
        defer close(c) // 迭代完成后关闭通道

        // 这里是迭代的地方
        // 你的老师的伪代码使用了递归
        // 这意味着你可能需要创建一个可以调用自身的独立函数
    }(c)

    return c // 将通道返回给调用函数
}

虽然我将实际的迭代过程留给你来完成,但每次循环都应该将一个组合字符串放入通道中。由于这是一个非缓冲通道,迭代函数会等待主函数读取值后再继续处理下一个迭代。

包含主函数的Playground版本:http://play.golang.org/p/CBkSjpmQ0t

使用递归的完整工作解决方案:http://play.golang.org/p/0bWDCibSUJ

英文:

Your teacher has already hinted that you should use a channel instead of returning a big array. By solving it like that, you will not have to store this big chunk of data containing all combination, but rather feed your function with different iterations and process them one at a time.

We can see your teachers hint in that the GenerateCombinations returns a chan string and not a []string:

func GenerateCombinations(alphabet string, length int) &lt;-chan string

This also means that the function must 1) create a channel to return, and 2) start a goroutine that feeds iterations to the channel. This function would look something like this:

func GenerateCombinations(alphabet string, length int) &lt;-chan string {
	c := make(chan string)

	// Starting a separate goroutine that will create all the combinations,
	// feeding them to the channel c
	go func(c chan string) {
		defer close(c) // Once the iteration function is finished, we close the channel

		// This is where the iteration will take place
		// Your teacher&#39;s pseudo code uses recursion
		// which mean you might want to create a separate function
		// that can call itself.
	}(c)
	
	return c // Return the channel to the calling function
}

While I will leave the actual iteration to you, every loop should result in you putting a combination string into the channel. Because it is not a buffered channel, the iterating function will wait til the main function has read the value before continue to process the next iteration.

A playground version including the main function: http://play.golang.org/p/CBkSjpmQ0t

A full working solution using recursion: http://play.golang.org/p/0bWDCibSUJ

答案2

得分: 4

这是一个棘手的问题,如果你完全不熟悉Go语言和/或如何生成排列组合。下面是解决方案的完整实现。我建议只有在真正遇到困难时才查看这个,因为这样做显然会减少学习的体验。

你可以在Go Playground上运行它以查看它的工作原理。

这种方法不是递归的,与你的教师的示例不同,但它可以很好地完成工作。

package main

import "fmt"
import "sync"

func main() {
	// 确保字母表已排序。
	const alphabet = "abcde"

	for str := range generate(alphabet) {
		fmt.Println(str)
	}
}

func generate(alphabet string) <-chan string {
	c := make(chan string, len(alphabet))

	go func() {
		defer close(c)

		if len(alphabet) == 0 {
			return
		}

		// 使用sync.WaitGroup来生成排列组合
		// 的goroutine,并允许我们等待它们全部
		// 完成。
		var wg sync.WaitGroup
		wg.Add(len(alphabet))

		for i := 1; i <= len(alphabet); i++ {
			go func(i int) {
				// 在字母表的子集上执行排列组合。
				Word(alphabet[:i]).Permute(c)

				// 通知WaitGroup我们已经完成。
				wg.Done()
			}(i)
		}

		// 等待所有goroutine完成。
		wg.Wait()
	}()

	return c
}

type Word []rune

// Permute生成当前单词的所有可能组合。
// 这假设符文已经排序。
func (w Word) Permute(out chan<- string) {
	if len(w) <= 1 {
		out <- string(w)
		return
	}

	// 手动写入第一个结果。
	out <- string(w)

	// 查找并打印所有剩余的排列组合。
	for w.next() {
		out <- string(w)
	}
}

// next通过重新排列字符执行单个排列组合。
// 如果没有更多新的排列组合,则返回false。
func (w Word) next() bool {
	var left, right int

	left = len(w) - 2
	for w[left] >= w[left+1] && left >= 1 {
		left--
	}

	if left == 0 && w[left] >= w[left+1] {
		return false
	}

	right = len(w) - 1
	for w[left] >= w[right] {
		right--
	}

	w[left], w[right] = w[right], w[left]

	left++
	right = len(w) - 1

	for left < right {
		w[left], w[right] = w[right], w[left]
		left++
		right--
	}

	return true
}

希望对你有帮助!

英文:

This is a tricky problem if you are entirely unfamiliar with Go and/or how to generate permutations. Below is a full implementation of the solution. I suggest you only look at this if you really get stuck, because doing so will obviously remove the learning experience.

You can run it on the go playground to see it work.

This approach is not recursive as your instructor's example suggests, but it gets the job done quite nicely.

package main
import &quot;fmt&quot;
import &quot;sync&quot;
func main() {
// Make sure the alphabet is sorted.
const alphabet = &quot;abcde&quot;
for str := range generate(alphabet) {
fmt.Println(str)
}
}
func generate(alphabet string) &lt;-chan string {
c := make(chan string, len(alphabet))
go func() {
defer close(c)
if len(alphabet) == 0 {
return
}
// Use a sync.WaitGroup to spawn permutation
// goroutines and allow us to wait for them all
// to finish.
var wg sync.WaitGroup
wg.Add(len(alphabet))
for i := 1; i &lt;= len(alphabet); i++ {
go func(i int) {
// Perform permutation on a subset of
// the alphabet.
Word(alphabet[:i]).Permute(c)
// Signal Waitgroup that we are done.
wg.Done()
}(i)
}
// Wait for all routines to finish.
wg.Wait()
}()
return c
}
type Word []rune
// Permute generates all possible combinations of
// the current word. This assumes the runes are sorted.
func (w Word) Permute(out chan&lt;- string) {
if len(w) &lt;= 1 {
out &lt;- string(w)
return
}
// Write first result manually.
out &lt;- string(w)
// Find and print all remaining permutations.
for w.next() {
out &lt;- string(w)
}
}
// next performs a single permutation by shuffling characters around.
// Returns false if there are no more new permutations.
func (w Word) next() bool {
var left, right int
left = len(w) - 2
for w[left] &gt;= w[left+1] &amp;&amp; left &gt;= 1 {
left--
}
if left == 0 &amp;&amp; w[left] &gt;= w[left+1] {
return false
}
right = len(w) - 1
for w[left] &gt;= w[right] {
right--
}
w[left], w[right] = w[right], w[left]
left++
right = len(w) - 1
for left &lt; right {
w[left], w[right] = w[right], w[left]
left++
right--
}
return true
}

答案3

得分: 0

对于这些任务,你可以尝试使用Iterium包:https://github.com/mowshon/iterium#user-content-combinatorics

product := iterium.Product([]string{"A", "B", "C", "D"}, 2)
toSlice, _ := product.Slice()

fmt.Println("Total:", product.Count())
fmt.Println(toSlice)
英文:

For these tasks you can try the Iterium package: https://github.com/mowshon/iterium#user-content-combinatorics

product := iterium.Product([]string{&quot;A&quot;, &quot;B&quot;, &quot;C&quot;, &quot;D&quot;}, 2)
toSlice, _ := product.Slice()

fmt.Println(&quot;Total:&quot;, product.Count())
fmt.Println(toSlice)

huangapple
  • 本文由 发表于 2013年10月8日 21:51:06
  • 转载请务必保留本文链接:https://go.coder-hub.com/19249588.html
匿名

发表评论

匿名网友

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

确定