LoginSignup
3
6

More than 5 years have passed since last update.

Go 言語で要素数を指定した組み合わせを列挙する

Posted at

はじめに

  • 前回 は全要素での組み合わせの列挙をしてみたが、今回は要素数を指定した組み合わせの列挙を行う。

全要素の組み合わせから指定の要素数の組み合わせを抽出する

  • 前回作成した全要素の組み合わせの列挙を行い、結果から指定の要素数の組み合わせを抽出する。
  • 明らかに無駄が多いがとりあえず一番ナイーブな実装から。
github.com/nirasan/algo/blob/master/combination.go#L3
func Combination1(nums []int, n int) [][]int {
    ps := Powerset2(nums)
    result := make([][]int, CombinationCount(len(nums), n))
    index := 0
    for _, s := range ps {
        if len(s) == n {
            result[index] = s
            index++
        }
    }
    return result
}

// 中略

func CombinationCount(n, m int) int {
    return Fact(n) / (Fact(n-m) * Fact(m))
}

func Fact(n int) int {
    if n == 0 {
        return 1
    } else {
        return n * Fact(n-1)
    }
}

ビット操作で組み合わせのパターンを列挙する

  • http://blog.livedoor.jp/dankogai/archives/51857323.html にある解法の移植
  • 2 進数表現で選ぶ選ばないを表現し列挙するのは前回も使った方法
  • Combination2NextIndex によって 2 進数で特定の数だけ 1 が立っている数を順次生成することができるのでこれを利用して列挙する
github.com/nirasan/algo/blob/master/combination.go#L16
func Combination2(nums []int, n int) [][]int {
    size := len(nums)
    result := make([][]int, CombinationCount(len(nums), n))
    bi := (1 << uint(n)) - 1
    max := 1 << uint(size)
    index := 0
    for bi < max {
        bi = Combination2NextIndex(bi)
        flags := bi
        s := []int{}
        for _, n := range nums {
            if flags%2 != 0 {
                s = append(s, n)
            }
            flags /= 2
        }
        result[index] = s
        index++
    }
    return result
}

// 中略

func Combination2NextIndex(n int) int {
    // (1) 2 進数で 1 になっている一番下の桁を取得
    smallest := n & -n
    // (2) 1 になっている一番下の桁に 1 を足す
    ripple := n + smallest
    // (3) 新たに 1 になっている一番下の桁を取得
    newSmallest := ripple & -ripple
    // (3) / (1) で (2) の操作により増えた 0 の数(失われた 1 の数)が算出され
    // - 1 することで補填するべき 1 のビット列を取得する
    ones := ((newSmallest / smallest) >> 1) - 1
    // ripple に ones を補填して新しいインデックスとする
    return ripple | ones
}

比較

  • やはり dankogai 氏のマジカルな実装が実行速度とメモリ効率ともに素晴らしい。
BenchmarkCombination_5to3/Combination1-4                  500000              3334 ns/op            1736 B/op         33 allocs/op
BenchmarkCombination_5to3/Combination2-4                  500000              2964 ns/op             768 B/op         30 allocs/op
BenchmarkCombination_10to3/Combination1-4                  20000            104392 ns/op           72624 B/op       1025 allocs/op
BenchmarkCombination_10to3/Combination2-4                  30000             45778 ns/op            9760 B/op        360 allocs/op
PASS
ok      github.com/nirasan/algo 8.124s

参考 URL

ソースコード

3
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
6