Python の itertools.product を C# で作る(3 引数まで対応)
Python の itertools モジュールには product という関数が定義されています。
たとえば 1, 3, 4 の数字を使って 4 桁の数を作るには、Python では itertools.product([1, 3, 4], repeat=4)
と書くだけで実現できます。
配列を用意してバックトラックで桁を埋めていく、という処理が不要になるのは素晴らしいですね。
C# で product 関数を作ってみました(ただし、引数の要素数は 3 までの制限があります)。
using System; using System.Collections.Generic; using System.Linq; class Program { static IEnumerable<T[]> Product_1<T>(int p, T[] buf, List<List<T>> pools) { if (p == buf.Length) { yield return buf; } else { foreach (var e in pools[p]) { buf[p] = e; foreach (var ret in Product_1(p+1, buf, pools)) { yield return ret; } } } } static IEnumerable<T[]> Product<T>(IEnumerable<T> xs, int repeat = 1) { var pools = new List<List<T>>(Enumerable.Repeat(xs.ToList(), repeat)); return Product_1(0, new T[pools.Count], pools); } static IEnumerable<T[]> Product<T>(IEnumerable<T> xs, IEnumerable<T> ys, int repeat = 1) { var ls = new List<List<T>> { xs.ToList(), ys.ToList() }; var pools = new List<List<T>>(Enumerable.Repeat(ls, repeat).SelectMany(e => e)); return Product_1(0, new T[pools.Count], pools); } static IEnumerable<T[]> Product<T>(IEnumerable<T> xs, IEnumerable<T> ys, IEnumerable<T> zs, int repeat = 1) { var ls = new List<List<T>> { xs.ToList(), ys.ToList(), zs.ToList() }; var pools = new List<List<T>>(Enumerable.Repeat(ls, repeat).SelectMany(e => e)); return Product_1(0, new T[pools.Count], pools); } static void Display<T>(IEnumerable<T[]> xs) { var ys = xs.Select(e => string.Join("", e)); Console.WriteLine(string.Join(" ", ys)); Console.WriteLine(); } static void Main() { // 1, 2, 3 の数字を使って、2 桁の数をつくる Display(Product(new[] { 1, 2, 3 }, 2)); // { 1, 2, 3 } と { 4, 5 } を組み合わせて数字をつくる Display(Product(new[] { 1, 2, 3 }, new[] { 4, 5 })); // 1, 3, 4 の数字を使って 3 桁の数をつくる Display(Product(new[] { 1, 3, 4 }, 3)); } }
実行結果です。
11 12 13 21 22 23 31 32 33 14 15 24 25 34 35 111 113 114 131 133 134 141 143 144 311 313 314 331 333 334 341 343 344 411 413 414 431 433 434 441 443 444