LoginSignup
2
4

More than 5 years have passed since last update.

C#:n個の異なった要素の中から k個を選ぶ順列

Last updated at Posted at 2017-05-21

全ての要素を使った順列を求める」で書いたコードをすこし手直しするだけで、n個の異なった要素の中から k個を選ぶ順列は求まります。

全ての要素を使った順列を求めるコード

全ての要素を使った順列を求める」で示したコードを再掲載します。

public IEnumerable<T[]> Enumerate<T>(IEnumerable<T> items) {
    if (items.Count() == 1) {
        yield return new T[] { items.First() };
        yield break;
    }
    foreach (var item in items) {
        var leftside = new T[] { item };
        var unused = items.Except(leftside);
        foreach (var rightside in Enumerate(unused)) {
            yield return leftside.Concat(rightside).ToArray();
        }
    }
}

n個の異なった要素の中からk個を選ぶ順列を求めるコード

上記コードを元に、引数kを追加し、k個を選ぶようにしています。

public IEnumerable<T[]> Enumerate<T>(IEnumerable<T> items, int k) {
    if (k == 1) {
        foreach (var item in items) {
            yield return new T[] { item };
        }
        yield break;
    }
    foreach (var item in items) {
        var leftside = new T[] { item };
        var unused = items.Except(leftside);
        foreach (var rightside in Enumerate(unused, k - 1)) {
            yield return leftside.Concat(rightside).ToArray();
        }
    }
}

再帰処理をすることで、kの数が1ずつ減ってゆくわけですが、k が 1になった時は、「itemsから1つを取り出す処理」となりますから、ここだけは特別扱いをしています。それが、

  if (k == 1) {
      foreach (var item in items) {
          yield return new T[] { item };
      }
      yield break;
  }

の箇所です。

重複を許す順列も求めてみる

重複を許す順列は、同じ要素を何回も用いて良い順列です。
引数withRepetitionがtrueの時は重複を許します。これも、既存のコードを少し手直しするだけで求めることができます。
unusedを求めるところで、重複を許す場合は、元のitemsをそのまま利用するようにするだけです。

public IEnumerable<T[]> Enumerate<T>(IEnumerable<T> items, int k, bool withRepetition) {
    if (k == 1) {
        foreach (var item in items) {
            yield return new T[] { item };
        }
        yield break;
    }
    foreach (var item in items) {
        var leftside = new T[] { item };
        var unused = withRepetition ? items : items.Except(leftside);
        foreach (var rightside in Enumerate(unused, k - 1, withRepetition)) {
            yield return leftside.Concat(rightside).ToArray();
        }
    }
}

最初の易しい問題の時に、しっかりと問題の本質を理解することが前提だと思いますが、易しい問題から徐々に難しい問題を解いていくようにすれば、意外とすんなりと解けるものですね。

GitHubで最終版のソースコードを公開しています。

重複なしの実行例

  Permutation perm = new Permutation();
  foreach (var n in perm.Enumerate("ABCD", 3)) {
      foreach (var x in n)
          Console.Write("{0} ", x);
      Console.WriteLine();
  }

以下、上記コードを実行した結果です。


A B C
A B D
A C B
A C D
A D B
A D C
B A C
B A D
B C A
B C D
B D A
B D C
C A B
C A D
C B A
C B D
C D A
C D B
D A B
D A C
D B A
D B C
D C A
D C B

重複ありの場合の実行例

    Permutation perm = new Permutation();
    foreach (var n in perm.Enumerate("ABCD", 3, withRepetition: true)) {
        foreach (var x in n)
            Console.Write("{0} ", x);
        Console.WriteLine();
    }

以下は、上記コードを実行した結果です。長すぎるので、途中省略しています。


A A A
A A B
A A C
A A D
A B A
A B B
A B C
A B D
A C A
A C B
A C C
A C D
A D A
A D B
A D C
A D D
B A A
B A B
B A C
B A D
... 省略
C D B
C D C
C D D
D A A
D A B
D A C
D A D
D B A
D B B
D B C
D B D
D C A
D C B
D C C
D C D
D D A
D D B
D D C
D D D

この記事は、Gushwell's C# Programming Pageで公開したものを加筆・修正したものです。

2
4
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
2
4