2

The project is more complex but the blocking issue is: How to generate a sequence of words of specific length from a list?

I've found how to generate all the possible combinations(see below) but the issue is that I need only the combinations of specific length.

Wolfram working example (it uses permutations though, I need only combinations(order doesn't matter)) :

Permutations[{a, b, c, d}, {3}]

Example(pseudo go):

  list := []string{"alice", "moon", "walks", "mars", "sings", "guitar", "bravo"}
  var premutationOf3
  premutationOf3 = premuate(list, 3)
  // this should return a list of all premutations such 
  // [][]string{[]string{"alice", "walks", "moon"}, []string{"alice", "signs", "guitar"} ....}

Current code to premutate all the possible sequences (no length limit)

    for _, perm := range permutations(list) {
        fmt.Printf("%q\n", perm)
    }

func permutations(arr []string) [][]string {
    var helper func([]string, int)
    res := [][]string{}

    helper = func(arr []string, n int) {
        if n == 1 {
            tmp := make([]string, len(arr))
            copy(tmp, arr)
            res = append(res, tmp)
        } else {
            for i := 0; i < n; i++ {
                helper(arr, n-1)
                if n%2 == 1 {
                    tmp := arr[i]
                    arr[i] = arr[n-1]
                    arr[n-1] = tmp
                } else {
                    tmp := arr[0]
                    arr[0] = arr[n-1]
                    arr[n-1] = tmp
                }
            }
        }
    }
    helper(arr, len(arr))
    return res
}
mike
  • 533
  • 1
  • 7
  • 24
  • I would think changing `if n == 1` to `if len(arr) == 2` would do the trick. – cdhowie Dec 12 '17 at 17:42
  • That doesn't really make sense. – mike Dec 12 '17 at 17:48
  • Have you tried it? – cdhowie Dec 12 '17 at 17:51
  • It doesn't make sense because I need a specific length and changing ``if n == 1`` to ``if len(arr) == 2`` doesn't seem to help. I edited the question with an expected output to make it more clear. – mike Dec 12 '17 at 17:57
  • That's exactly what this change does. It changes the terminating condition for recursion from "we have fully enumerated the input array" to "we have three elements in the current output array." You would also likely need to change `tmp := make([]string, len(arr))` to `tmp := make([]string, 3)` and adjust the `copy()` invocation as well, unless Go is smart enough to use the smaller of the array dimensions. – cdhowie Dec 12 '17 at 18:03
  • ``arr`` has more than 2 elements so changing if len(arr) == 2 results in no result returned if a slice like `list` provided in the example is used . Not sure what you mean. – mike Dec 12 '17 at 18:06
  • I see, I misunderstood how the algorithm works. It's unlikely that this algorithm is going to work in this case. A simpler O(n^2) recursive algorithm would do the trick, though I don't know if it would perform well enough for your use case. – cdhowie Dec 12 '17 at 18:09
  • The word is "permute," not "premute." Please edit your question accordingly. – Jim Mischel Dec 12 '17 at 18:29
  • 2
    If order of items is not important, then you want to generate `combinations` - look for this term, there is a lot of effective methods. – MBo Dec 12 '17 at 18:35
  • for generating combinations you can check [this](https://stackoverflow.com/questions/127704/algorithm-to-return-all-combinations-of-k-elements-from-n) answer. – Parham Alvani Dec 12 '17 at 19:14
  • @MBo the order is not important but I could not find an anaswer. – mike Dec 12 '17 at 19:26
  • You can change your wolfram link to use `combinations` instead: [Combinations\[{a, b, c, d}, {3}\]](https://www.wolframalpha.com/input/?i=Combinations%5B%7Ba,+b,+c,+d%7D,+%7B3%7D%5D) – assefamaru Dec 14 '17 at 16:45

1 Answers1

1

I implement twiddle algorithm for generating combination in Go. Here is my implementation:

package twiddle

// Twiddle type contains all information twiddle algorithm
// need between each iteration.
type Twiddle struct {
    p   []int
    b   []bool
    end bool
}

// New creates new twiddle algorithm instance
func New(m int, n int) *Twiddle {
    p := make([]int, n+2)
    b := make([]bool, n)

    // initiate p
    p[0] = n + 1

    var i int

    for i = 1; i != n-m+1; i++ {
        p[i] = 0
    }
    for i != n+1 {
        p[i] = i + m - n
        i++
    }

    p[n+1] = -2

    if m == 0 {
        p[1] = 1
    }

    // initiate b
    for i = 0; i != n-m; i++ {
        b[i] = false
    }
    for i != n {
        b[i] = true
        i++
    }

    return &Twiddle{
        p: p,
        b: b,
    }
}

// Next creates next combination and return it.
// it returns nil on end of combinations
func (t *Twiddle) Next() []bool {
    if t.end {
        return nil
    }

    r := make([]bool, len(t.b))
    for i := 0; i < len(t.b); i++ {
        r[i] = t.b[i]
    }

    x, y, end := t.twiddle()
    t.b[x] = true
    t.b[y] = false
    t.end = end

    return r
}

func (t *Twiddle) twiddle() (int, int, bool) {
    var i, j, k int
    var x, y int

    j = 1
    for t.p[j] <= 0 {
        j++
    }

    if t.p[j-1] == 0 {
        for i = j - 1; i != 1; i-- {
            t.p[i] = -1
        }
        t.p[j] = 0
        x = 0
        t.p[1] = 1
        y = j - 1
    } else {
        if j > 1 {
            t.p[j-1] = 0
        }
        j++
        for t.p[j] > 0 {
            j++
        }
        k = j - 1
        i = j
        for t.p[i] == 0 {
            t.p[i] = -1
            i++
        }
        if t.p[i] == -1 {
            t.p[i] = t.p[k]
            x = i - 1
            y = k - 1
            t.p[k] = -1
        } else {
            if i == t.p[0] {
                return x, y, true
            }

            t.p[j] = t.p[i]
            t.p[i] = 0
            x = j - 1
            y = i - 1
        }
    }
    return x, y, false

}

you can use my tweedle package as follow:

tw := tweedle.New(1, 2)

for b := tw.Next(); b != nil; b = tw.Next() {
    fmt.Println(b)
}
Parham Alvani
  • 2,305
  • 2
  • 14
  • 25
  • it seems to work with integers...I guess I just need to modify it to work with strings. Can you document `m, n` params? – mike Dec 12 '17 at 20:24
  • @mike you can use its boolean output and create your string list. for example, if `b[0]` is true you include `"alice"`. m is 7 and n are 3 in your given example. – Parham Alvani Dec 12 '17 at 20:30