引数として文字の配列と選択する文字の数を取る関数を書きたいと思います。
8文字の配列を入力し、その中から3文字を選択したいとします。それからあなたは得るべきです:
8! / ((8 - 3)! * 3!) = 56
それぞれが3文字からなる配列(または単語)。
Art of Computer Programmingの第4巻:Fascicle 3 は、私が説明するよりもあなたの特定の状況に合うかもしれないこれらのトンを持っています。
あなたが遭遇することになる問題は、もちろんメモリであり、かなり迅速に、あなたはあなたのセットの中に20の要素までに問題があるでしょう - 20C3 =1140。そして集合を反復したい場合は、修正されたグレイコードアルゴリズムを使用するのが最善であるので、それらすべてをメモリに保持しているわけではありません。これらは前のものから次の組み合わせを生成し、繰り返しを避けます。さまざまな用途にこれらの多くがあります。連続した組み合わせの違いを最大限にしたいですか?最小化しますか?等。
グレーコードを説明しているオリジナルの論文のいくつか:
このトピックをカバーしている他の論文は次のとおりです。
フィリップJチェイス、 ` アルゴリズム382:N個のオブジェクトのうちのM個の組み合わせ '(1970)
Cのアルゴリズム ...
組み合わせをインデックスで参照することもできます(辞書順)。インデックスに基づいてインデックスが右から左へいくらかの量の変化であるべきであることを認識すると、組み合わせを回復するものを構築することができます。
それで、{1,2,3,4,5,6}というセットがあります...そして3つの要素が欲しいのです。 {1,2,3}としましょう。要素間の差は1であり、順番は最小であると言えます。 {1,2,4}には1つの変更があり、辞書的に2となります。したがって、最後の場所の「変更」の数は、辞書順での1つの変更を表します。 1つの変更を伴う2番目の場所{1,3,4}には1つの変更がありますが、2番目の場所にあるため、より多くの変更があります(元のセットの要素数に比例します)。
私が説明した方法は、セットからインデックスへの解体ですが、逆にする必要があります - これははるかに面倒です。これが Buckles が問題を解決する方法です。それらを計算するためにいくつかの Cを書いた 小さな変更を加えて - 私は集合を表すために数の範囲ではなく集合のインデックスを使ったので、いつも0 ... nから作業している。注意:
別の方法 :があります。その概念は把握しやすくプログラムするのが簡単ですが、Bucklesを最適化していません。幸いなことに、それはまた重複した組み合わせを生成しません。
例えば27 = C(6,4) + C(5,3) + C(2,2) + C(1,1)
です。ですから、27の辞書的な組み合わせからなる4つのことは、{1、2、5、6}です。以下の例(OCaml)では、読者に任されたchoose
関数が必要です。
(* this will find the [x] combination of a [set] list when taking [k] elements *)
let combination_maccaffery set k x =
(* maximize function -- maximize a that is aCb *)
(* return largest c where c < i and choose(c,i) <= z *)
let rec maximize a b x =
if (choose a b ) <= x then a else maximize (a-1) b x
in
let rec iterate n x i = match i with
| 0 -> []
| i ->
let max = maximize n i x in
max :: iterate n (x - (choose max i)) (i-1)
in
if x < 0 then failwith "errors" else
let idxs = iterate (List.length set) x k in
List.map (List.nth set) (List.sort (-) idxs)
次の2つのアルゴリズムは、教育目的のために提供されています。それらはイテレータと(より一般的な)フォルダ全体の組み合わせを実装しています。それらは可能な限り高速で、複雑度O(nCk)メモリ消費はk
によって制限されます。
反復子から始めます。反復子は各組み合わせに対してユーザー提供の関数を呼び出します。
let iter_combs n k f =
let rec iter v s j =
if j = k then f v
else for i = s to n - 1 do iter (i::v) (i+1) (j+1) done in
iter [] 0 0
より一般的なバージョンでは、初期状態から始めて、状態変数と共にユーザー提供の関数を呼び出します。異なる状態間で状態を渡す必要があるため、forループを使用しませんが、代わりに再帰を使用します。
let fold_combs n k f x =
let rec loop i s c x =
if i < n then
loop (i+1) s c @@
let c = i::c and s = s + 1 and i = i + 1 in
if s < k then loop i s c x else f c x
else x in
loop 0 0 [] x
C#の場合:
public static IEnumerable<IEnumerable<T>> Combinations<T>(this IEnumerable<T> elements, int k)
{
return k == 0 ? new[] { new T[0] } :
elements.SelectMany((e, i) =>
elements.Skip(i + 1).Combinations(k - 1).Select(c => (new[] {e}).Concat(c)));
}
使用法:
var result = Combinations(new[] { 1, 2, 3, 4, 5 }, 3);
結果:
123
124
125
134
135
145
234
235
245
345
短いJavaソリューション
import Java.util.Arrays;
public class Combination {
public static void main(String[] args){
String[] arr = {"A","B","C","D","E","F"};
combinations2(arr, 3, 0, new String[3]);
}
static void combinations2(String[] arr, int len, int startPosition, String[] result){
if (len == 0){
System.out.println(Arrays.toString(result));
return;
}
for (int i = startPosition; i <= arr.length-len; i++){
result[result.length - len] = arr[i];
combinations2(arr, len-1, i+1, result);
}
}
}
結果は
[A, B, C]
[A, B, D]
[A, B, E]
[A, B, F]
[A, C, D]
[A, C, E]
[A, C, F]
[A, D, E]
[A, D, F]
[A, E, F]
[B, C, D]
[B, C, E]
[B, C, F]
[B, D, E]
[B, D, F]
[B, E, F]
[C, D, E]
[C, D, F]
[C, E, F]
[D, E, F]
この問題に対する私の再帰的Pythonソリューションを紹介してもいいですか。
def choose_iter(elements, length):
for i in xrange(len(elements)):
if length == 1:
yield (elements[i],)
else:
for next in choose_iter(elements[i+1:len(elements)], length-1):
yield (elements[i],) + next
def choose(l, k):
return list(choose_iter(l, k))
使用例
>>> len(list(choose_iter("abcdefgh",3)))
56
私はその単純さのためにそれが好きです。
「ABCDEFGH」のようにあなたの文字の配列はこんな感じになるでしょう。現在の単語にどの文字を使用するのかを示す3つのインデックス(i、j、k)があります。
A ^^ ^ i j k
最初にkを変えます、それで次のステップはそのようになります:
A ^^ ^ i j k
終わりに達したら続けてjを変えてからkに変えます。
[...] [...] [] [] [] [] [] [] [] [] [] [] [] [] [] [] [] [] [] [] [] [] [] [] [] [] [] [] [] []
あなたがjに到達すると、あなたはまたiを変化させ始めます。
ABCDEFGH ^ ^ ^ ijk ABCDEFGH ^ ^ ^ ijk ...
コードで書かれて、これはそのように見える
void print_combinations(const char *string)
{
int i, j, k;
int len = strlen(string);
for (i = 0; i < len - 2; i++)
{
for (j = i + 1; j < len - 1; j++)
{
for (k = j + 1; k < len; k++)
printf("%c%c%c\n", string[i], string[j], string[k]);
}
}
}
次の再帰アルゴリズムは、順序集合からすべてのk要素の組み合わせを選びます。
i
を選択してくださいi
よりも大きい要素のセットから再帰的に選択されたk-1
要素の各組み合わせとi
を組み合わせます。セット内の各i
について上記を繰り返します。
繰り返しを避けるために、残りの要素をi
よりも大きいものとして選択することが重要です。このようにして、[3,5]は、[3]を[5]と組み合わせて2回ではなく1回だけ選択されます(条件は[5] + [3]を排除します)。この条件がなければ、組み合わせではなくバリエーションが得られます。
私はこのスレッドが役に立つと思い、Firebugに取り込めるJavascriptソリューションを追加することを考えました。 JSエンジンによっては、開始文字列が大きい場合は少し時間がかかります。
function string_recurse(active, rest) {
if (rest.length == 0) {
console.log(active);
} else {
string_recurse(active + rest.charAt(0), rest.substring(1, rest.length));
string_recurse(active, rest.substring(1, rest.length));
}
}
string_recurse("", "abc");
出力は次のようになります。
abc
ab
ac
a
bc
b
c
C++では、次のルーチンは、範囲[first、last)の間の長さ距離(first、k)のすべての組み合わせを生成します。
#include <algorithm>
template <typename Iterator>
bool next_combination(const Iterator first, Iterator k, const Iterator last)
{
/* Credits: Mark Nelson http://marknelson.us */
if ((first == last) || (first == k) || (last == k))
return false;
Iterator i1 = first;
Iterator i2 = last;
++i1;
if (last == i1)
return false;
i1 = last;
--i1;
i1 = k;
--i2;
while (first != i1)
{
if (*--i1 < *i2)
{
Iterator j = k;
while (!(*i1 < *j)) ++j;
std::iter_swap(i1,j);
++i1;
++j;
i2 = k;
std::rotate(i1,j,last);
while (last != j)
{
++j;
++i2;
}
std::rotate(k,i2,last);
return true;
}
}
std::rotate(first,k,last);
return false;
}
それはこのように使用することができます:
#include <string>
#include <iostream>
int main()
{
std::string s = "12345";
std::size_t comb_size = 3;
do
{
std::cout << std::string(s.begin(), s.begin() + comb_size) << std::endl;
} while (next_combination(s.begin(), s.begin() + comb_size, s.end()));
return 0;
}
これにより、以下が印刷されます。
123
124
125
134
135
145
234
235
245
345
static IEnumerable<string> Combinations(List<string> characters, int length)
{
for (int i = 0; i < characters.Count; i++)
{
// only want 1 character, just return this one
if (length == 1)
yield return characters[i];
// want more than one character, return this one plus all combinations one shorter
// only use characters after the current one for the rest of the combinations
else
foreach (string next in Combinations(characters.GetRange(i + 1, characters.Count - (i + 1)), length - 1))
yield return characters[i] + next;
}
}
Pythonの簡単な例:
def comb(sofar, rest, n):
if n == 0:
print sofar
else:
for i in range(len(rest)):
comb(sofar + rest[i], rest[i+1:], n-1)
>>> comb("", "abcde", 3)
abc
abd
abe
acd
ace
ade
bcd
bce
bde
cde
説明のために、再帰的方法を以下の例で説明する。
例:A B C D E
3のすべての組み合わせは次のようになります。
Haskellにおける単純な再帰的アルゴリズム
import Data.List
combinations 0 lst = [[]]
combinations n lst = do
(x:xs) <- tails lst
rest <- combinations (n-1) xs
return $ x : rest
最初に特別な場合、すなわちゼロ要素の選択を定義します。それは単一の結果を生成し、それは空のリスト(すなわち空のリストを含むリスト)である。
N> 0の場合、x
はリストのすべての要素を調べ、xs
はx
の後のすべての要素です。
rest
は、xs
への再帰呼び出しを使用してcombinations
からn - 1
要素を選択します。関数の最終結果は、x
とrest
の異なる値ごとに、各要素がx : rest
であるリスト(つまり、x
をhead、rest
をtailとするリスト)です。
> combinations 3 "abcde"
["abc","abd","abe","acd","ace","ade","bcd","bce","bde","cde"]
そしてもちろん、Haskellは怠惰なので、リストは必要に応じて徐々に生成されるので、指数関数的に大きな組み合わせを部分的に評価することができます。
> let c = combinations 8 "abcdefghijklmnopqrstuvwxyz"
> take 10 c
["abcdefgh","abcdefgi","abcdefgj","abcdefgk","abcdefgl","abcdefgm","abcdefgn",
"abcdefgo","abcdefgp","abcdefgq"]
そしてここに祖父COBOLがあります。
それぞれ8バイトの34要素の配列(純粋に任意の選択)を想定しましょう。考えられるすべての4要素の組み合わせを列挙し、それらを配列にロードすることです。
4つのインデックスを使います。4つのグループの各ポジションに1つずつあります。
配列は次のように処理されます。
idx1 = 1
idx2 = 2
idx3 = 3
idx4 = 4
Idx4を4から最後まで変えます。 idx4ごとに、4つのグループからなる一意の組み合わせが得られます。 idx4が配列の最後に来ると、idx3を1だけインクリメントし、idx4をidx3 + 1に設定します。それからidx4を最後まで実行します。 idx1の位置が配列の末尾から4未満になるまで、それぞれidx3、idx2、およびidx1を増やしながら、この方法で処理を進めます。これでアルゴリズムは終了です。
1 --- pos.1
2 --- pos 2
3 --- pos 3
4 --- pos 4
5
6
7
etc.
最初の繰り返し:
1234
1235
1236
1237
1245
1246
1247
1256
1257
1267
etc.
COBOLの例
01 DATA_ARAY.
05 FILLER PIC X(8) VALUE "VALUE_01".
05 FILLER PIC X(8) VALUE "VALUE_02".
etc.
01 ARAY_DATA OCCURS 34.
05 ARAY_ITEM PIC X(8).
01 OUTPUT_ARAY OCCURS 50000 PIC X(32).
01 MAX_NUM PIC 99 COMP VALUE 34.
01 INDEXXES COMP.
05 IDX1 PIC 99.
05 IDX2 PIC 99.
05 IDX3 PIC 99.
05 IDX4 PIC 99.
05 OUT_IDX PIC 9(9).
01 WHERE_TO_STOP_SEARCH PIC 99 COMP.
* Stop the search when IDX1 is on the third last array element:
COMPUTE WHERE_TO_STOP_SEARCH = MAX_VALUE - 3
MOVE 1 TO IDX1
PERFORM UNTIL IDX1 > WHERE_TO_STOP_SEARCH
COMPUTE IDX2 = IDX1 + 1
PERFORM UNTIL IDX2 > MAX_NUM
COMPUTE IDX3 = IDX2 + 1
PERFORM UNTIL IDX3 > MAX_NUM
COMPUTE IDX4 = IDX3 + 1
PERFORM UNTIL IDX4 > MAX_NUM
ADD 1 TO OUT_IDX
STRING ARAY_ITEM(IDX1)
ARAY_ITEM(IDX2)
ARAY_ITEM(IDX3)
ARAY_ITEM(IDX4)
INTO OUTPUT_ARAY(OUT_IDX)
ADD 1 TO IDX4
END-PERFORM
ADD 1 TO IDX3
END-PERFORM
ADD 1 TO IDX2
END_PERFORM
ADD 1 TO IDX1
END-PERFORM.
99 Scalaの問題 で説明されているように、これはScalaでのエレガントで一般的な実装です。
object P26 {
def flatMapSublists[A,B](ls: List[A])(f: (List[A]) => List[B]): List[B] =
ls match {
case Nil => Nil
case sublist@(_ :: tail) => f(sublist) ::: flatMapSublists(tail)(f)
}
def combinations[A](n: Int, ls: List[A]): List[List[A]] =
if (n == 0) List(Nil)
else flatMapSublists(ls) { sl =>
combinations(n - 1, sl.tail) map {sl.head :: _}
}
}
SQL構文を使用できる場合 - たとえば、LINQを使用して構造体または配列のフィールドにアクセスする場合、または1つのcharフィールド「Letter」を持つ「Alphabet」という名前のテーブルを持つデータベースに直接アクセスする場合コード:
SELECT A.Letter, B.Letter, C.Letter
FROM Alphabet AS A, Alphabet AS B, Alphabet AS C
WHERE A.Letter<>B.Letter AND A.Letter<>C.Letter AND B.Letter<>C.Letter
AND A.Letter<B.Letter AND B.Letter<C.Letter
これは3文字のすべての組み合わせを返します。テーブル "Alphabet"に何文字あるかにかかわらず(3、8、10、27など)。
あなたが欲しいのが組み合わせではなくすべての順列であるならば(すなわち、あなたは一度だけ現れるのではなく、「ACB」と「ABC」を違うものとして数えたい)最後の行(AND)を削除するだけです。
編集後:質問をもう一度読んだ後、必要なのは general アルゴリズムであり、3つの項目を選択する場合の特定のアルゴリズムではないことがわかりました。 Adam Hughesの答えは完全なものです、残念ながら私は(まだ)それを投票することはできません。この答えは簡単ですが、正確に3つの項目が必要な場合にのみ機能します。
私はPythonで、プロジェクトのオイラーに使用した置換アルゴリズムを持っていました:
def missing(miss,src):
"Returns the list of items in src not present in miss"
return [i for i in src if i not in miss]
def permutation_gen(n,l):
"Generates all the permutations of n items of the l list"
for i in l:
if n<=1: yield [i]
r = [i]
for j in permutation_gen(n-1,missing([i],l)): yield r+j
もし
n<len(l)
あなたは繰り返しなしであなたが必要とするすべての組み合わせを持つべきです、あなたはそれを必要としますか?
これはジェネレータなので、次のように使用します。
for comb in permutation_gen(3,list("ABCDEFGH")):
print comb
ここに、C#でコーディングされたそのアルゴリズムの遅延評価版があります。
static bool nextCombination(int[] num, int n, int k)
{
bool finished, changed;
changed = finished = false;
if (k > 0)
{
for (int i = k - 1; !finished && !changed; i--)
{
if (num[i] < (n - 1) - (k - 1) + i)
{
num[i]++;
if (i < k - 1)
{
for (int j = i + 1; j < k; j++)
{
num[j] = num[j - 1] + 1;
}
}
changed = true;
}
finished = (i == 0);
}
}
return changed;
}
static IEnumerable Combinations<T>(IEnumerable<T> elements, int k)
{
T[] elem = elements.ToArray();
int size = elem.Length;
if (k <= size)
{
int[] numbers = new int[k];
for (int i = 0; i < k; i++)
{
numbers[i] = i;
}
do
{
yield return numbers.Select(n => elem[n]);
}
while (nextCombination(numbers, size, k));
}
}
そしてテスト部分:
static void Main(string[] args)
{
int k = 3;
var t = new[] { "dog", "cat", "mouse", "zebra"};
foreach (IEnumerable<string> i in Combinations(t, k))
{
Console.WriteLine(string.Join(",", i));
}
}
これがお役に立てば幸いです。
組み合わせインデックスの遅延生成を伴う別のC#バージョン。このバージョンは、すべての値のリストと現在の組み合わせの値との間のマッピングを定義するためのインデックスの単一の配列を維持します。つまり、常に全体の間に O(k) 追加スペースを使用しますランタイム。コードは、最初の組み合わせを含む個々の組み合わせを O(k) timeに生成します。
public static IEnumerable<T[]> Combinations<T>(this T[] values, int k)
{
if (k < 0 || values.Length < k)
yield break; // invalid parameters, no combinations possible
// generate the initial combination indices
var combIndices = new int[k];
for (var i = 0; i < k; i++)
{
combIndices[i] = i;
}
while (true)
{
// return next combination
var combination = new T[k];
for (var i = 0; i < k; i++)
{
combination[i] = values[combIndices[i]];
}
yield return combination;
// find first index to update
var indexToUpdate = k - 1;
while (indexToUpdate >= 0 && combIndices[indexToUpdate] >= values.Length - k + indexToUpdate)
{
indexToUpdate--;
}
if (indexToUpdate < 0)
yield break; // done
// update combination indices
for (var combIndex = combIndices[indexToUpdate] + 1; indexToUpdate < k; indexToUpdate++, combIndex++)
{
combIndices[indexToUpdate] = combIndex;
}
}
}
テストコード:
foreach (var combination in new[] {'a', 'b', 'c', 'd', 'e'}.Combinations(3))
{
System.Console.WriteLine(String.Join(" ", combination));
}
出力:
a b c
a b d
a b e
a c d
a c e
a d e
b c d
b c e
b d e
c d e
Array.prototype.combs = function(num) {
var str = this,
length = str.length,
of = Math.pow(2, length) - 1,
out, combinations = [];
while(of) {
out = [];
for(var i = 0, y; i < length; i++) {
y = (1 << i);
if(y & of && (y !== of))
out.Push(str[i]);
}
if (out.length >= num) {
combinations.Push(out);
}
of--;
}
return combinations;
}
クロージュバージョン:
(defn comb [k l]
(if (= 1 k) (map vector l)
(apply concat
(map-indexed
#(map (fn [x] (conj x %2))
(comb (dec k) (drop (inc %1) l)))
l))))
https://Gist.github.com/3118596
JavaScriptの実装があります。それはk-組み合わせと任意のオブジェクトの配列のすべての組み合わせを取得する機能を持っています。例:
k_combinations([1,2,3], 2)
-> [[1,2], [1,3], [2,3]]
combinations([1,2,3])
-> [[1],[2],[3],[1,2],[1,3],[2,3],[1,2,3]]
「ABCDEFGH」のようにあなたの文字の配列はこんな感じになるでしょう。現在の単語にどの文字を使用するのかを示す3つのインデックス(i、j、k)があります。
A ^^ ^ i j k
最初にkを変えます、それで次のステップはそのようになります:
A ^^ ^ i j k
終わりに達したら続けてjを変えてからkに変えます。
[...] [...] [] [] [] [] [] [] [] [] [] [] [] [] [] [] [] [] [] [] [] [] [] [] [] [] [] [] [] []
あなたがjに到達すると、あなたはまたiを変化させ始めます。
ABCDEFGH ^ ^ ^ ijk ABCDEFGH ^ ^ ^ ijk ...
function initializePointers($cnt) {
$pointers = [];
for($i=0; $i<$cnt; $i++) {
$pointers[] = $i;
}
return $pointers;
}
function incrementPointers(&$pointers, &$arrLength) {
for($i=0; $i<count($pointers); $i++) {
$currentPointerIndex = count($pointers) - $i - 1;
$currentPointer = $pointers[$currentPointerIndex];
if($currentPointer < $arrLength - $i - 1) {
++$pointers[$currentPointerIndex];
for($j=1; ($currentPointerIndex+$j)<count($pointers); $j++) {
$pointers[$currentPointerIndex+$j] = $pointers[$currentPointerIndex]+$j;
}
return true;
}
}
return false;
}
function getDataByPointers(&$arr, &$pointers) {
$data = [];
for($i=0; $i<count($pointers); $i++) {
$data[] = $arr[$pointers[$i]];
}
return $data;
}
function getCombinations($arr, $cnt)
{
$len = count($arr);
$result = [];
$pointers = initializePointers($cnt);
do {
$result[] = getDataByPointers($arr, $pointers);
} while(incrementPointers($pointers, count($arr)));
return $result;
}
$result = getCombinations([0, 1, 2, 3, 4, 5], 3);
print_r($result);
https://stackoverflow.com/a/127898/2628125 に基づいていますが、あらゆるサイズのポインタについてはもっと抽象的です。
これはランダムな長さの文字列から指定されたサイズのすべての組み合わせを与える方法です。 quinmarsの解と似ていますが、さまざまな入力とkに対して機能します。
ラップアラウンドするようにコードを変更することができます。すなわち、入力 'abcd'からの 'dab' w k = 3です。
public void run(String data, int howMany){
choose(data, howMany, new StringBuffer(), 0);
}
//n choose k
private void choose(String data, int k, StringBuffer result, int startIndex){
if (result.length()==k){
System.out.println(result.toString());
return;
}
for (int i=startIndex; i<data.length(); i++){
result.append(data.charAt(i));
choose(data,k,result, i+1);
result.setLength(result.length()-1);
}
}
"abcde"の出力:
abc abd abe acdエースアデーbcd bce bde cde
簡潔なJavascriptソリューション:
Array.prototype.combine=function combine(k){
var toCombine=this;
var last;
function combi(n,comb){
var combs=[];
for ( var x=0,y=comb.length;x<y;x++){
for ( var l=0,m=toCombine.length;l<m;l++){
combs.Push(comb[x]+toCombine[l]);
}
}
if (n<k-1){
n++;
combi(n,combs);
} else{last=combs;}
}
combi(1,toCombine);
return last;
}
// Example:
// var toCombine=['a','b','c'];
// var results=toCombine.combine(4);
これは私が最近Javaで書いたコードです。これは "outOf"要素から "num"要素のすべての組み合わせを計算して返します。
// author: Sourabh Bhat ([email protected])
public class Testing
{
public static void main(String[] args)
{
// Test case num = 5, outOf = 8.
int num = 5;
int outOf = 8;
int[][] combinations = getCombinations(num, outOf);
for (int i = 0; i < combinations.length; i++)
{
for (int j = 0; j < combinations[i].length; j++)
{
System.out.print(combinations[i][j] + " ");
}
System.out.println();
}
}
private static int[][] getCombinations(int num, int outOf)
{
int possibilities = get_nCr(outOf, num);
int[][] combinations = new int[possibilities][num];
int arrayPointer = 0;
int[] counter = new int[num];
for (int i = 0; i < num; i++)
{
counter[i] = i;
}
breakLoop: while (true)
{
// Initializing part
for (int i = 1; i < num; i++)
{
if (counter[i] >= outOf - (num - 1 - i))
counter[i] = counter[i - 1] + 1;
}
// Testing part
for (int i = 0; i < num; i++)
{
if (counter[i] < outOf)
{
continue;
} else
{
break breakLoop;
}
}
// Innermost part
combinations[arrayPointer] = counter.clone();
arrayPointer++;
// Incrementing part
counter[num - 1]++;
for (int i = num - 1; i >= 1; i--)
{
if (counter[i] >= outOf - (num - 1 - i))
counter[i - 1]++;
}
}
return combinations;
}
private static int get_nCr(int n, int r)
{
if(r > n)
{
throw new ArithmeticException("r is greater then n");
}
long numerator = 1;
long denominator = 1;
for (int i = n; i >= r + 1; i--)
{
numerator *= i;
}
for (int i = 2; i <= n - r; i++)
{
denominator *= i;
}
return (int) (numerator / denominator);
}
}
私はこのためにSQL Server 2005でソリューションを作成し、私のウェブサイトに投稿しました: http://www.jessemclain.com/downloads/code/sql/fn_GetMChooseNCombos.sql.htm
使い方を示す例を次に示します。
SELECT * FROM dbo.fn_GetMChooseNCombos('ABCD', 2, '')
結果:
Word
----
AB
AC
AD
BC
BD
CD
(6 row(s) affected)
これについては、O'camlコードがすべて言っています。アルゴリズムはコードから明らかです。
let combi n lst =
let rec comb l c =
if( List.length c = n) then [c] else
match l with
[] -> []
| (h::t) -> (combi t (h::c))@(combi t c)
in
combi lst []
;;
これがC++での私の命題です
私はできる限りこのイテレータ型に制限を加えないようにしました。そのため、このソリューションでは順方向イテレータだけを想定していますが、const_iteratorでもかまいません。これはどんな標準コンテナでも動作するはずです。引数が意味をなさない場合には、std :: invalid_argumnentを投げます。
#include <vector>
#include <stdexcept>
template <typename Fci> // Fci - forward const iterator
std::vector<std::vector<Fci> >
enumerate_combinations(Fci begin, Fci end, unsigned int combination_size)
{
if(begin == end && combination_size > 0u)
throw std::invalid_argument("empty set and positive combination size!");
std::vector<std::vector<Fci> > result; // empty set of combinations
if(combination_size == 0u) return result; // there is exactly one combination of
// size 0 - emty set
std::vector<Fci> current_combination;
current_combination.reserve(combination_size + 1u); // I reserve one aditional slot
// in my vector to store
// the end sentinel there.
// The code is cleaner thanks to that
for(unsigned int i = 0u; i < combination_size && begin != end; ++i, ++begin)
{
current_combination.Push_back(begin); // Construction of the first combination
}
// Since I assume the itarators support only incrementing, I have to iterate over
// the set to get its size, which is expensive. Here I had to itrate anyway to
// produce the first cobination, so I use the loop to also check the size.
if(current_combination.size() < combination_size)
throw std::invalid_argument("combination size > set size!");
result.Push_back(current_combination); // Store the first combination in the results set
current_combination.Push_back(end); // Here I add mentioned earlier sentinel to
// simplyfy rest of the code. If I did it
// earlier, previous statement would get ugly.
while(true)
{
unsigned int i = combination_size;
Fci tmp; // Thanks to the sentinel I can find first
do // iterator to change, simply by scaning
{ // from right to left and looking for the
tmp = current_combination[--i]; // first "bubble". The fact, that it's
++tmp; // a forward iterator makes it ugly but I
} // can't help it.
while(i > 0u && tmp == current_combination[i + 1u]);
// Here is probably my most obfuscated expression.
// Loop above looks for a "bubble". If there is no "bubble", that means, that
// current_combination is the last combination, Expression in the if statement
// below evaluates to true and the function exits returning result.
// If the "bubble" is found however, the ststement below has a sideeffect of
// incrementing the first iterator to the left of the "bubble".
if(++current_combination[i] == current_combination[i + 1u])
return result;
// Rest of the code sets posiotons of the rest of the iterstors
// (if there are any), that are to the right of the incremented one,
// to form next combination
while(++i < combination_size)
{
current_combination[i] = current_combination[i - 1u];
++current_combination[i];
}
// Below is the ugly side of using the sentinel. Well it had to haave some
// disadvantage. Try without it.
result.Push_back(std::vector<Fci>(current_combination.begin(),
current_combination.end() - 1));
}
}
アルゴリズム:
C#の場合:
void Main()
{
var set = new [] {"A", "B", "C", "D" }; //, "E", "F", "G", "H", "I", "J" };
var kElement = 2;
for(var i = 1; i < Math.Pow(2, set.Length); i++) {
var result = Convert.ToString(i, 2).PadLeft(set.Length, '0');
var cnt = Regex.Matches(Regex.Escape(result), "1").Count;
if (cnt == kElement) {
for(int j = 0; j < set.Length; j++)
if ( Char.GetNumericValue(result[j]) == 1)
Console.Write(set[j]);
Console.WriteLine();
}
}
}
なぜそれが機能するのですか?
N要素セットのサブセットとnビットシーケンスの間に全単射があります。
つまり、シーケンスを数えることで、サブセットがいくつあるかを把握できます。
例えば、以下の4つの要素セットは、{0,1}×{0,1}×{0,1}×{0,1}(または2 ^ 4)の異なるシーケンスによって表すことができる。
だから - すべての組み合わせを見つけるために1から2 ^ nまで数えるだけです。 (空集合は無視します。)次に、数字をそれらの2進表現に変換します。それから、あなたのセットの要素を 'on'ビットに置き換えます。
K要素の結果だけが欲しい場合は、kビットが 'on'のときだけ出力します。
(kの長さのサブセットの代わりにすべてのサブセットが必要な場合は、cnt/kElementの部分を削除してください。)
(証明については、MITコンピュータ科学のための無料コースウェア数学、Lehmanら、セクション11.2.2を参照。 https://ocw.mit.edu/courses/electrical-engineering-and-computer-科学/ 6-042j数学のための数学 - 秋-2010/readings/ )
二項係数を扱うための一般的な関数を扱うクラスを書きました。これはあなたの問題が属する問題のタイプです。以下のタスクを実行します。
任意のNがKを選択すると、すべてのKインデックスをNice形式でファイルに出力します。 Kインデックスは、より説明的な文字列または文字に置き換えることができます。この方法では、この種の問題を解決することは非常に簡単です。
Kインデックスをソートされた2項係数テーブル内のエントリの適切なインデックスに変換します。この手法は、反復に依存している古い公開手法よりもはるかに高速です。それはパスカルの三角形に固有の数学的性質を使ってこれを行います。私の論文はこれについて話しています。私はこの手法を最初に発見して公開したと思いますが、私は間違っているかもしれません。
ソートされた二項係数テーブル内のインデックスを対応するKインデックスに変換します。
Mark Dominus メソッドを使用して二項係数を計算します。二項係数はオーバーフローする可能性がはるかに低く、より大きな数で機能します。
このクラスは.NET C#で書かれており、汎用リストを使用して問題に関連するオブジェクトがある場合はそれを管理する方法を提供します。このクラスのコンストラクタはInitTableというブール値を取り、trueの場合は管理対象のオブジェクトを保持するための汎用リストを作成します。この値がfalseの場合、テーブルは作成されません。上記の4つの方法を実行するためにテーブルを作成する必要はありません。テーブルにアクセスするためのアクセサメソッドが提供されています。
クラスとそのメソッドの使用方法を示す関連テストクラスがあります。これは2つのケースで広範囲にテストされており、既知のバグはありません。
このクラスについて読んでコードをダウンロードするには、 二項係数を表にする を参照してください。
このクラスをC++に変換するのは難しくありません。
私はPHPのための同様の解決策を探していて、以下に遭遇しました
class Combinations implements Iterator
{
protected $c = null;
protected $s = null;
protected $n = 0;
protected $k = 0;
protected $pos = 0;
function __construct($s, $k) {
if(is_array($s)) {
$this->s = array_values($s);
$this->n = count($this->s);
} else {
$this->s = (string) $s;
$this->n = strlen($this->s);
}
$this->k = $k;
$this->rewind();
}
function key() {
return $this->pos;
}
function current() {
$r = array();
for($i = 0; $i < $this->k; $i++)
$r[] = $this->s[$this->c[$i]];
return is_array($this->s) ? $r : implode('', $r);
}
function next() {
if($this->_next())
$this->pos++;
else
$this->pos = -1;
}
function rewind() {
$this->c = range(0, $this->k);
$this->pos = 0;
}
function valid() {
return $this->pos >= 0;
}
protected function _next() {
$i = $this->k - 1;
while ($i >= 0 && $this->c[$i] == $this->n - $this->k + $i)
$i--;
if($i < 0)
return false;
$this->c[$i]++;
while($i++ < $this->k - 1)
$this->c[$i] = $this->c[$i - 1] + 1;
return true;
}
}
foreach(new Combinations("1234567", 5) as $substring)
echo $substring, ' ';
クラスがどれほど効率的であるかはわかりませんが、シーダーにしか使用していませんでした。
LISPマクロはすべての値r(一度に取得)のコードを生成します。
(defmacro txaat (some-list taken-at-a-time)
(let* ((vars (reverse (truncate-list '(a b c d e f g h i j) taken-at-a-time))))
`(
,@(loop for i below taken-at-a-time
for j in vars
with nested = nil
finally (return nested)
do
(setf
nested
`(loop for ,j from
,(if (< i (1- (length vars)))
`(1+ ,(nth (1+ i) vars))
0)
below (- (length ,some-list) ,i)
,@(if (equal i 0)
`(collect
(list
,@(loop for k from (1- taken-at-a-time) downto 0
append `((nth ,(nth k vars) ,some-list)))))
`(append ,nested))))))))
そう、
CL-USER> (macroexpand-1 '(txaat '(a b c d) 1))
(LOOP FOR A FROM 0 TO (- (LENGTH '(A B C D)) 1)
COLLECT (LIST (NTH A '(A B C D))))
T
CL-USER> (macroexpand-1 '(txaat '(a b c d) 2))
(LOOP FOR A FROM 0 TO (- (LENGTH '(A B C D)) 2)
APPEND (LOOP FOR B FROM (1+ A) TO (- (LENGTH '(A B C D)) 1)
COLLECT (LIST (NTH A '(A B C D)) (NTH B '(A B C D)))))
T
CL-USER> (macroexpand-1 '(txaat '(a b c d) 3))
(LOOP FOR A FROM 0 TO (- (LENGTH '(A B C D)) 3)
APPEND (LOOP FOR B FROM (1+ A) TO (- (LENGTH '(A B C D)) 2)
APPEND (LOOP FOR C FROM (1+ B) TO (- (LENGTH '(A B C D)) 1)
COLLECT (LIST (NTH A '(A B C D))
(NTH B '(A B C D))
(NTH C '(A B C D))))))
T
CL-USER>
そして、
CL-USER> (txaat '(a b c d) 1)
((A) (B) (C) (D))
CL-USER> (txaat '(a b c d) 2)
((A B) (A C) (A D) (B C) (B D) (C D))
CL-USER> (txaat '(a b c d) 3)
((A B C) (A B D) (A C D) (B C D))
CL-USER> (txaat '(a b c d) 4)
((A B C D))
CL-USER> (txaat '(a b c d) 5)
NIL
CL-USER> (txaat '(a b c d) 0)
NIL
CL-USER>
Javaソリューションに基づいてn(二項係数)からk個の要素のすべての組み合わせを返す短いphpアルゴリズム:
$array = array(1,2,3,4,5);
$array_result = NULL;
$array_general = NULL;
function combinations($array, $len, $start_position, $result_array, $result_len, &$general_array)
{
if($len == 0)
{
$general_array[] = $result_array;
return;
}
for ($i = $start_position; $i <= count($array) - $len; $i++)
{
$result_array[$result_len - $len] = $array[$i];
combinations($array, $len-1, $i+1, $result_array, $result_len, $general_array);
}
}
combinations($array, 3, 0, $array_result, 3, $array_general);
echo "<pre>";
print_r($array_general);
echo "</pre>";
同じ解決策だがJavaScriptで:
var newArray = [1, 2, 3, 4, 5];
var arrayResult = [];
var arrayGeneral = [];
function combinations(newArray, len, startPosition, resultArray, resultLen, arrayGeneral) {
if(len === 0) {
var tempArray = [];
resultArray.forEach(value => tempArray.Push(value));
arrayGeneral.Push(tempArray);
return;
}
for (var i = startPosition; i <= newArray.length - len; i++) {
resultArray[resultLen - len] = newArray[i];
combinations(newArray, len-1, i+1, resultArray, resultLen, arrayGeneral);
}
}
combinations(newArray, 3, 0, arrayResult, 3, arrayGeneral);
console.log(arrayGeneral);
これが 私のScalaソリューション :です。
def combinations[A](s: List[A], k: Int): List[List[A]] =
if (k > s.length) Nil
else if (k == 1) s.map(List(_))
else combinations(s.tail, k - 1).map(s.head :: _) ::: combinations(s.tail, k)
これはコレクション内のnCk.Elements
の組み合わせを生成する再帰的プログラムで、1
からn
までと想定されています。
#include<stdio.h>
#include<stdlib.h>
int nCk(int n,int loopno,int ini,int *a,int k)
{
static int count=0;
int i;
loopno--;
if(loopno<0)
{
a[k-1]=ini;
for(i=0;i<k;i++)
{
printf("%d,",a[i]);
}
printf("\n");
count++;
return 0;
}
for(i=ini;i<=n-loopno-1;i++)
{
a[k-1-loopno]=i+1;
nCk(n,loopno,i+1,a,k);
}
if(ini==0)
return count;
else
return 0;
}
void main()
{
int n,k,*a,count;
printf("Enter the value of n and k\n");
scanf("%d %d",&n,&k);
a=(int*)malloc(k*sizeof(int));
count=nCk(n,k,0,a,k);
printf("No of combinations=%d\n",count);
}
JavaScript、ジェネレータベース、再帰的アプローチ:
function *nCk(n,k){
for(var i=n-1;i>=k-1;--i)
if(k===1)
yield [i];
else
for(var temp of nCk(i,k-1)){
temp.unshift(i);
yield temp;
}
}
function test(){
try{
var n=parseInt(ninp.value);
var k=parseInt(kinp.value);
log.innerText="";
var stop=Date.now()+1000;
if(k>=1)
for(var res of nCk(n,k))
if(Date.now()<stop)
log.innerText+=JSON.stringify(res)+" ";
else{
log.innerText+="1 second passed, stopping here.";
break;
}
}catch(ex){}
}
n:<input id="ninp" oninput="test()">
>= k:<input id="kinp" oninput="test()"> >= 1
<div id="log"></div>
このように(i
とunshift()
を減らす)それは組み合わせと組み合わせの中の要素を降順で生成し、やや見栄えがします。
テストは1秒後に停止するので、奇妙な数字を入力するのは比較的安全です。
VB.Netでは、このアルゴリズムは一組の数からn個の数のすべての組み合わせを集めます(PoolArray)。例えば「8、10、20、33、41、44、47」から5つのピックのすべての組み合わせ。
Sub CreateAllCombinationsOfPicksFromPool(ByVal PicksArray() As UInteger, ByVal PicksIndex As UInteger, ByVal PoolArray() As UInteger, ByVal PoolIndex As UInteger)
If PicksIndex < PicksArray.Length Then
For i As Integer = PoolIndex To PoolArray.Length - PicksArray.Length + PicksIndex
PicksArray(PicksIndex) = PoolArray(i)
CreateAllCombinationsOfPicksFromPool(PicksArray, PicksIndex + 1, PoolArray, i + 1)
Next
Else
' completed combination. build your collections using PicksArray.
End If
End Sub
Dim PoolArray() As UInteger = Array.ConvertAll("8,10,20,33,41,44,47".Split(","), Function(u) UInteger.Parse(u))
Dim nPicks as UInteger = 5
Dim Picks(nPicks - 1) As UInteger
CreateAllCombinationsOfPicksFromPool(Picks, 0, PoolArray, 0)
そしてこれが Clojure のバージョンです。私は私の OCaml で説明したのと同じアルゴリズムを使います。
(defn select
([items]
(select items 0 (inc (count items))))
([items n1 n2]
(reduce concat
(map #(select % items)
(range n1 (inc n2)))))
([n items]
(let [
lmul (fn [a list-of-lists-of-bs]
(map #(cons a %) list-of-lists-of-bs))
]
(if (= n (count items))
(list items)
(if (empty? items)
items
(concat
(select n (rest items))
(lmul (first items) (select (dec n) (rest items)))))))))
これを呼び出す方法は3つあります。
(a) 厳密に n 質問に応じて選択された項目:
user=> (count (select 3 "abcdefgh"))
56
(b) n 1 と n 2 の間の選択項目:
user=> (select '(1 2 3 4) 2 3)
((3 4) (2 4) (2 3) (1 4) (1 3) (1 2) (2 3 4) (1 3 4) (1 2 4) (1 2 3))
(c) /の間 0 および選択されたコレクションの項目のサイズ:
user=> (select '(1 2 3))
(() (3) (2) (1) (2 3) (1 3) (1 2) (1 2 3))
コンピュータプログラミングの芸術、第4A巻:コンビナトリアルアルゴリズム、パート1 - のセクション7.2.1.3のアルゴリズムL(辞書編集の組み合わせ)のCコード :
#include <stdio.h>
#include <stdlib.h>
void visit(int* c, int t)
{
// for (int j = 1; j <= t; j++)
for (int j = t; j > 0; j--)
printf("%d ", c[j]);
printf("\n");
}
int* initialize(int n, int t)
{
// c[0] not used
int *c = (int*) malloc((t + 3) * sizeof(int));
for (int j = 1; j <= t; j++)
c[j] = j - 1;
c[t+1] = n;
c[t+2] = 0;
return c;
}
void comb(int n, int t)
{
int *c = initialize(n, t);
int j;
for (;;) {
visit(c, t);
j = 1;
while (c[j]+1 == c[j+1]) {
c[j] = j - 1;
++j;
}
if (j > t)
return;
++c[j];
}
free(c);
}
int main(int argc, char *argv[])
{
comb(5, 3);
return 0;
}
#include <stdio.h>
unsigned int next_combination(unsigned int *ar, size_t n, unsigned int k)
{
unsigned int finished = 0;
unsigned int changed = 0;
unsigned int i;
if (k > 0) {
for (i = k - 1; !finished && !changed; i--) {
if (ar[i] < (n - 1) - (k - 1) + i) {
/* Increment this element */
ar[i]++;
if (i < k - 1) {
/* Turn the elements after it into a linear sequence */
unsigned int j;
for (j = i + 1; j < k; j++) {
ar[j] = ar[j - 1] + 1;
}
}
changed = 1;
}
finished = i == 0;
}
if (!changed) {
/* Reset to first combination */
for (i = 0; i < k; i++) {
ar[i] = i;
}
}
}
return changed;
}
typedef void(*printfn)(const void *, FILE *);
void print_set(const unsigned int *ar, size_t len, const void **elements,
const char *brackets, printfn print, FILE *fptr)
{
unsigned int i;
fputc(brackets[0], fptr);
for (i = 0; i < len; i++) {
print(elements[ar[i]], fptr);
if (i < len - 1) {
fputs(", ", fptr);
}
}
fputc(brackets[1], fptr);
}
int main(void)
{
unsigned int numbers[] = { 0, 1, 2 };
char *elements[] = { "a", "b", "c", "d", "e" };
const unsigned int k = sizeof(numbers) / sizeof(unsigned int);
const unsigned int n = sizeof(elements) / sizeof(const char*);
do {
print_set(numbers, k, (void*)elements, "[]", (printfn)fputs, stdout);
putchar('\n');
} while (next_combination(numbers, n, k));
getchar();
return 0;
}
C#でもう一つの解決策:
static List<List<T>> GetCombinations<T>(List<T> originalItems, int combinationLength)
{
if (combinationLength < 1)
{
return null;
}
return CreateCombinations<T>(new List<T>(), 0, combinationLength, originalItems);
}
static List<List<T>> CreateCombinations<T>(List<T> initialCombination, int startIndex, int length, List<T> originalItems)
{
List<List<T>> combinations = new List<List<T>>();
for (int i = startIndex; i < originalItems.Count - length + 1; i++)
{
List<T> newCombination = new List<T>(initialCombination);
newCombination.Add(originalItems[i]);
if (length > 1)
{
List<List<T>> newCombinations = CreateCombinations(newCombination, i + 1, length - 1, originalItems);
combinations.AddRange(newCombinations);
}
else
{
combinations.Add(newCombination);
}
}
return combinations;
}
使用例
List<char> initialArray = new List<char>() { 'a','b','c','d'};
int combinationLength = 3;
List<List<char>> combinations = GetCombinations(initialArray, combinationLength);
void combine(char a[], int N, int M, int m, int start, char result[]) {
if (0 == m) {
for (int i = M - 1; i >= 0; i--)
std::cout << result[i];
std::cout << std::endl;
return;
}
for (int i = start; i < (N - m + 1); i++) {
result[m - 1] = a[i];
combine(a, N, M, m-1, i+1, result);
}
}
void combine(char a[], int N, int M) {
char *result = new char[M];
combine(a, N, M, M, 0, result);
delete[] result;
}
最初の関数で、mはさらにいくつを選択する必要があるかを示し、startは配列のどの位置から選択を開始する必要があるかを示します。
これはC(n、m)のすべての組み合わせを表示する簡単なコードです。これは、次の有効な組み合わせを指す一連の配列インデックスを初期化して移動することによって機能します。インデックスは、最小のm個のインデックス(辞書順には最小の組み合わせ)を指すように初期化されます。次に、m番目のインデックスから始めて、インデックスを前方に移動しようとします。インデックスがその限界に達したら、前のインデックスを試します(インデックス1まで)。インデックスを前方に移動できる場合は、それより大きいインデックスをすべてリセットします。
m=(Rand()%n)+1; // m will vary from 1 to n
for (i=0;i<n;i++) a[i]=i+1;
// we want to print all possible C(n,m) combinations of selecting m objects out of n
printf("Printing C(%d,%d) possible combinations ...\n", n,m);
// This is an adhoc algo that keeps m pointers to the next valid combination
for (i=0;i<m;i++) p[i]=i; // the p[.] contain indices to the a vector whose elements constitute next combination
done=false;
while (!done)
{
// print combination
for (i=0;i<m;i++) printf("%2d ", a[p[i]]);
printf("\n");
// update combination
// method: start with p[m-1]. try to increment it. if it is already at the end, then try moving p[m-2] ahead.
// if this is possible, then reset p[m-1] to 1 more than (the new) p[m-2].
// if p[m-2] can not also be moved, then try p[m-3]. move that ahead. then reset p[m-2] and p[m-1].
// repeat all the way down to p[0]. if p[0] can not also be moved, then we have generated all combinations.
j=m-1;
i=1;
move_found=false;
while ((j>=0) && !move_found)
{
if (p[j]<(n-i))
{
move_found=true;
p[j]++; // point p[j] to next index
for (k=j+1;k<m;k++)
{
p[k]=p[j]+(k-j);
}
}
else
{
j--;
i++;
}
}
if (!move_found) done=true;
}
プログラミング言語は言及されていないので、私はリストも大丈夫だと思います。それで、これは短いリストに適したOCamlバージョンです(非末尾再帰)。 any型の要素のリストlと整数nを指定すると、_を含むすべての可能なリストのリストが返されます。nlの要素結果リストの要素の順序が無視されると仮定した場合、つまりlist ['a'; 'b']が['b'; 'a']と同じで、一度報告されます。結果のリストのサイズは((List.length l)nになります)になります。
再帰の直感は次のとおりです。リストの先頭に移動してから、2つの再帰呼び出しを行います。
再帰的な結果を組み合わせるには、リストの先頭にRC1の結果をlist-multiply(奇数の名前を付けてください)してからRC2の結果を追加(@)します。リスト乗算は以下の操作lmul
です。
a lmul [ l1 ; l2 ; l3] = [a::l1 ; a::l2 ; a::l3]
lmul
は以下のコードで次のように実装されています。
List.map (fun x -> h::x)
リストのサイズが選択したい要素の数に等しくなると再帰は終了します。その場合はリスト自体を返すだけです。
それで、上記のアルゴリズムを実装するOCamlの4つのライナーです:
let rec choose l n = match l, (List.length l) with
| _, lsize when n==lsize -> [l]
| h::t, _ -> (List.map (fun x-> h::x) (choose t (n-1))) @ (choose t n)
| [], _ -> []
流行に乗って、別の解決策を投稿する。これは一般的なJava実装です。入力:(int k)
は選択する要素の数、(List<T> list)
は選択可能なリストです。組み合わせ(List<List<T>>)
のリストを返します。
public static <T> List<List<T>> getCombinations(int k, List<T> list) {
List<List<T>> combinations = new ArrayList<List<T>>();
if (k == 0) {
combinations.add(new ArrayList<T>());
return combinations;
}
for (int i = 0; i < list.size(); i++) {
T element = list.get(i);
List<T> rest = getSublist(list, i+1);
for (List<T> previous : getCombinations(k-1, rest)) {
previous.add(element);
combinations.add(previous);
}
}
return combinations;
}
public static <T> List<T> getSublist(List<T> list, int i) {
List<T> sublist = new ArrayList<T>();
for (int j = i; j < list.size(); j++) {
sublist.add(list.get(j));
}
return sublist;
}
短いPythonコード、インデックス位置
def yield_combos(n,k):
# n is set size, k is combo size
i = 0
a = [0 for i in range(k)]
while i > -1:
for j in range(i+1, k):
a[j] = a[j-1]+1
i=j
yield a
while a[i] == i + n - k:
i -= 1
a[i] += 1
これが簡単なJSソリューションです。
function getAllCombinations(n, k, f1) {
indexes = Array(k);
for (let i =0; i< k; i++) {
indexes[i] = i;
}
var total = 1;
f1(indexes);
while (indexes[0] !== n-k) {
total++;
getNext(n, indexes);
f1(indexes);
}
return {total};
}
function getNext(n, vec) {
const k = vec.length;
vec[k-1]++;
for (var i=0; i<k; i++) {
var currentIndex = k-i-1;
if (vec[currentIndex] === n - i) {
var nextIndex = k-i-2;
vec[nextIndex]++;
vec[currentIndex] = vec[nextIndex] + 1;
}
}
for (var i=1; i<k; i++) {
if (vec[i] === n - (k-i - 1)) {
vec[i] = vec[i-1] + 1;
}
}
return vec;
}
let start = new Date();
let result = getAllCombinations(10, 3, indexes => console.log(indexes));
let runTime = new Date() - start;
console.log({
result, runTime
});
これがマクロを使ったLISPアプローチです。これはCommon LISPで機能し、他のLISP方言でも機能するはずです。
以下のコードは 'n'個のネストしたループを作成し、リストbody
からの 'n'個の要素の組み合わせごとに(lst
変数に格納された)任意のコードの塊を実行します。変数var
は、ループに使用される変数を含むリストを指します。
(defmacro do-combinations ((var lst num) &body body)
(loop with syms = (loop repeat num collect (gensym))
for i on syms
for k = `(loop for ,(car i) on (cdr ,(cadr i))
do (let ((,var (list ,@(reverse syms)))) (progn ,@body)))
then `(loop for ,(car i) on ,(if (cadr i) `(cdr ,(cadr i)) lst) do ,k)
finally (return k)))
どれどれ...
(macroexpand-1 '(do-combinations (p '(1 2 3 4 5 6 7) 4) (pprint (mapcar #'car p))))
(LOOP FOR #:G3217 ON '(1 2 3 4 5 6 7) DO
(LOOP FOR #:G3216 ON (CDR #:G3217) DO
(LOOP FOR #:G3215 ON (CDR #:G3216) DO
(LOOP FOR #:G3214 ON (CDR #:G3215) DO
(LET ((P (LIST #:G3217 #:G3216 #:G3215 #:G3214)))
(PROGN (PPRINT (MAPCAR #'CAR P))))))))
(do-combinations (p '(1 2 3 4 5 6 7) 4) (pprint (mapcar #'car p)))
(1 2 3 4)
(1 2 3 5)
(1 2 3 6)
...
組み合わせはデフォルトでは保存されないため、保存容量は最小限に抑えられます。すべての結果を格納する代わりにbody
コードを選択する可能性も、より高い柔軟性をもたらします。
これが私が再帰とビットシフトを使って思いついたC++の解決策です。 Cでも動作するかもしれません。
void r_nCr(unsigned int startNum, unsigned int bitVal, unsigned int testNum) // Should be called with arguments (2^r)-1, 2^(r-1), 2^(n-1)
{
unsigned int n = (startNum - bitVal) << 1;
n += bitVal ? 1 : 0;
for (unsigned int i = log2(testNum) + 1; i > 0; i--) // Prints combination as a series of 1s and 0s
cout << (n >> (i - 1) & 1);
cout << endl;
if (!(n & testNum) && n != startNum)
r_nCr(n, bitVal, testNum);
if (bitVal && bitVal < testNum)
r_nCr(startNum, bitVal >> 1, testNum);
}
これがどのように機能するかの説明を見つけることができます ここ 。
これは私がこの問題を解決するために思い付いたアルゴリズムです。これはc ++で書かれていますが、ビット単位操作をサポートするほとんどすべての言語に適応できます。
void r_nCr(const unsigned int &startNum, const unsigned int &bitVal, const unsigned int &testNum) // Should be called with arguments (2^r)-1, 2^(r-1), 2^(n-1)
{
unsigned int n = (startNum - bitVal) << 1;
n += bitVal ? 1 : 0;
for (unsigned int i = log2(testNum) + 1; i > 0; i--) // Prints combination as a series of 1s and 0s
cout << (n >> (i - 1) & 1);
cout << endl;
if (!(n & testNum) && n != startNum)
r_nCr(n, bitVal, testNum);
if (bitVal && bitVal < testNum)
r_nCr(startNum, bitVal >> 1, testNum);
}
それがどのように機能するかの説明を見ることができます ここ 。
C#の簡単なアルゴリズム。 (私があなたがアップロードしたものを使用しようとしたので投稿していますが、何らかの理由でそれをコンパイルできませんでした - クラスを拡張しますか?やった)。ちなみに、私はc#については基本的なプログラミング以上のものではありませんが、これはうまくいきます。
public static List<List<int>> GetSubsetsOfSizeK(List<int> lInputSet, int k)
{
List<List<int>> lSubsets = new List<List<int>>();
GetSubsetsOfSizeK_rec(lInputSet, k, 0, new List<int>(), lSubsets);
return lSubsets;
}
public static void GetSubsetsOfSizeK_rec(List<int> lInputSet, int k, int i, List<int> lCurrSet, List<List<int>> lSubsets)
{
if (lCurrSet.Count == k)
{
lSubsets.Add(lCurrSet);
return;
}
if (i >= lInputSet.Count)
return;
List<int> lWith = new List<int>(lCurrSet);
List<int> lWithout = new List<int>(lCurrSet);
lWith.Add(lInputSet[i++]);
GetSubsetsOfSizeK_rec(lInputSet, k, i, lWith, lSubsets);
GetSubsetsOfSizeK_rec(lInputSet, k, i, lWithout, lSubsets);
}
使い方:GetSubsetsOfSizeK(set of type List<int>, integer k)
あなたが使っているものは何でも繰り返すようにそれを修正することができます。
がんばろう!
Pythonでは、再帰とすべてが参照によって行われるという事実を利用します。これは非常に大きな集合には大量のメモリを消費しますが、初期集合が複雑なオブジェクトになることがあるという利点があります。それは唯一のユニークな組み合わせを見つけるでしょう。
import copy
def find_combinations( length, set, combinations = None, candidate = None ):
# recursive function to calculate all unique combinations of unique values
# from [set], given combinations of [length]. The result is populated
# into the 'combinations' list.
#
if combinations == None:
combinations = []
if candidate == None:
candidate = []
for item in set:
if item in candidate:
# this item already appears in the current combination somewhere.
# skip it
continue
attempt = copy.deepcopy(candidate)
attempt.append(item)
# sorting the subset is what gives us completely unique combinations,
# so that [1, 2, 3] and [1, 3, 2] will be treated as equals
attempt.sort()
if len(attempt) < length:
# the current attempt at finding a new combination is still too
# short, so add another item to the end of the set
# yay recursion!
find_combinations( length, set, combinations, attempt )
else:
# the current combination attempt is the right length. If it
# already appears in the list of found combinations then we'll
# skip it.
if attempt in combinations:
continue
else:
# otherwise, we append it to the list of found combinations
# and move on.
combinations.append(attempt)
continue
return len(combinations)
あなたはそれをこのように使っています。 'result'を渡すことはオプションであるため、可能な組み合わせの数を取得するためにそれを使用することができます。
size = 3
set = [1, 2, 3, 4, 5]
result = []
num = find_combinations( size, set, result )
print "size %d results in %d sets" % (size, num)
print "result: %s" % (result,)
そのテストデータから次のような出力が得られます。
size 3 results in 10 sets
result: [[1, 2, 3], [1, 2, 4], [1, 2, 5], [1, 3, 4], [1, 3, 5], [1, 4, 5], [2, 3, 4], [2, 3, 5], [2, 4, 5], [3, 4, 5]]
あなたのセットがこのようになっているなら、それは同様にうまくいきます:
set = [
[ 'Vanilla', 'cupcake' ],
[ 'chocolate', 'pudding' ],
[ 'Vanilla', 'pudding' ],
[ 'chocolate', 'cookie' ],
[ 'mint', 'cookie' ]
]
イテレータオブジェクトとして実装されたMetaTrader MQL4のための非常に速い組み合わせ。
コードはとても理解しやすいです。
私はたくさんのアルゴリズムをベンチマークしました、これは本当にとても速いです - ほとんどのnext_combination()関数より約3倍速いです。
class CombinationsIterator
{
private:
int input_array[]; // 1 2 3 4 5
int index_array[]; // i j k
int m_elements; // N
int m_indices; // K
public:
CombinationsIterator(int &src_data[], int k)
{
m_indices = k;
m_elements = ArraySize(src_data);
ArrayCopy(input_array, src_data);
ArrayResize(index_array, m_indices);
// create initial combination (0..k-1)
for (int i = 0; i < m_indices; i++)
{
index_array[i] = i;
}
}
// https://stackoverflow.com/questions/5076695
// bool next_combination(int &item[], int k, int N)
bool advance()
{
int N = m_elements;
for (int i = m_indices - 1; i >= 0; --i)
{
if (index_array[i] < --N)
{
++index_array[i];
for (int j = i + 1; j < m_indices; ++j)
{
index_array[j] = index_array[j - 1] + 1;
}
return true;
}
}
return false;
}
void getItems(int &items[])
{
// fill items[] from input array
for (int i = 0; i < m_indices; i++)
{
items[i] = input_array[index_array[i]];
}
}
};
上記のイテレータクラスをテストするためのドライバプログラム:
//+------------------------------------------------------------------+
//| |
//+------------------------------------------------------------------+
// driver program to test above class
#define N 5
#define K 3
void OnStart()
{
int myset[N] = {1, 2, 3, 4, 5};
int items[K];
CombinationsIterator comboIt(myset, K);
do
{
comboIt.getItems(items);
printf("%s", ArrayToString(items));
} while (comboIt.advance());
}
Output:
1 2 3
1 2 4
1 2 5
1 3 4
1 3 5
1 4 5
2 3 4
2 3 5
2 4 5
3 4 5
短い高速C#実装
public static IEnumerable<IEnumerable<T>> Combinations<T>(IEnumerable<T> elements, int k)
{
return Combinations(elements.Count(), k).Select(p => p.Select(q => elements.ElementAt(q)));
}
public static List<int[]> Combinations(int setLenght, int subSetLenght) //5, 3
{
var result = new List<int[]>();
var lastIndex = subSetLenght - 1;
var dif = setLenght - subSetLenght;
var prevSubSet = new int[subSetLenght];
var lastSubSet = new int[subSetLenght];
for (int i = 0; i < subSetLenght; i++)
{
prevSubSet[i] = i;
lastSubSet[i] = i + dif;
}
while(true)
{
//add subSet ad result set
var n = new int[subSetLenght];
for (int i = 0; i < subSetLenght; i++)
n[i] = prevSubSet[i];
result.Add(n);
if (prevSubSet[0] >= lastSubSet[0])
break;
//start at index 1 because index 0 is checked and breaking in the current loop
int j = 1;
for (; j < subSetLenght; j++)
{
if (prevSubSet[j] >= lastSubSet[j])
{
prevSubSet[j - 1]++;
for (int p = j; p < subSetLenght; p++)
prevSubSet[p] = prevSubSet[p - 1] + 1;
break;
}
}
if (j > lastIndex)
prevSubSet[lastIndex]++;
}
return result;
}
もう一つの再帰的な解決策(あなたはこれを移植して数字の代わりに文字を使うことができるはずです)。
stack = []
def choose(n,x):
r(0,0,n+1,x)
def r(p, c, n,x):
if x-c == 0:
print stack
return
for i in range(p, n-(x-1)+c):
stack.append(i)
r(i+1,c+1,n,x)
stack.pop()
4 3を選ぶか、0から4で始まる3つの数字の組み合わせをすべて欲しい
choose(4,3)
[0, 1, 2]
[0, 1, 3]
[0, 1, 4]
[0, 2, 3]
[0, 2, 4]
[0, 3, 4]
[1, 2, 3]
[1, 2, 4]
[1, 3, 4]
[2, 3, 4]
私の解決策を提示したいのですが。 next
には再帰呼び出しもネストループもありません。コードの中心はnext()
メソッドです。
public class Combinations {
final int pos[];
final List<Object> set;
public Combinations(List<?> l, int k) {
pos = new int[k];
set=new ArrayList<Object>(l);
reset();
}
public void reset() {
for (int i=0; i < pos.length; ++i) pos[i]=i;
}
public boolean next() {
int i = pos.length-1;
for (int maxpos = set.size()-1; pos[i] >= maxpos; --maxpos) {
if (i==0) return false;
--i;
}
++pos[i];
while (++i < pos.length)
pos[i]=pos[i-1]+1;
return true;
}
public void getSelection(List<?> l) {
@SuppressWarnings("unchecked")
List<Object> ll = (List<Object>)l;
if (ll.size()!=pos.length) {
ll.clear();
for (int i=0; i < pos.length; ++i)
ll.add(set.get(pos[i]));
}
else {
for (int i=0; i < pos.length; ++i)
ll.set(i, set.get(pos[i]));
}
}
}
そして使用例:
static void main(String[] args) {
List<Character> l = new ArrayList<Character>();
for (int i=0; i < 32; ++i) l.add((char)('a'+i));
Combinations comb = new Combinations(l,5);
int n=0;
do {
++n;
comb.getSelection(l);
//Log.debug("%d: %s", n, l.toString());
} while (comb.next());
Log.debug("num = %d", n);
}
これがコーヒースクリプトの実装です。
combinations: (list, n) ->
permuations = Math.pow(2, list.length) - 1
out = []
combinations = []
while permuations
out = []
for i in [0..list.length]
y = ( 1 << i )
if( y & permuations and (y isnt permuations))
out.Push(list[i])
if out.length <= n and out.length > 0
combinations.Push(out)
permuations--
return combinations
おそらく私はその点を見逃しています(あなたはアルゴリズムを必要とし、既製の解決策は必要ないということです)、しかしscalaは箱から出してそれをしているようです(今):
def combis(str:String, k:Int):Array[String] = {
str.combinations(k).toArray
}
このような方法を使う:
println(combis("abcd",2).toList)
生成します:
List(ab, ac, ad, bc, bd, cd)
これは私のJavaScriptでの投稿です(再帰なし)
set = ["q0", "q1", "q2", "q3"]
collector = []
function comb(num) {
results = []
one_comb = []
for (i = set.length - 1; i >= 0; --i) {
tmp = Math.pow(2, i)
quotient = parseInt(num / tmp)
results.Push(quotient)
num = num % tmp
}
k = 0
for (i = 0; i < results.length; ++i)
if (results[i]) {
++k
one_comb.Push(set[i])
}
if (collector[k] == undefined)
collector[k] = []
collector[k].Push(one_comb)
}
sum = 0
for (i = 0; i < set.length; ++i)
sum += Math.pow(2, i)
for (ii = sum; ii > 0; --ii)
comb(ii)
cnt = 0
for (i = 1; i < collector.length; ++i) {
n = 0
for (j = 0; j < collector[i].length; ++j)
document.write(++cnt, " - " + (++n) + " - ", collector[i][j], "<br>")
document.write("<hr>")
}
再帰的に、Free Pascalの非常に単純な答えcombo
。
procedure combinata (n, k :integer; producer :oneintproc);
procedure combo (ndx, nbr, len, lnd :integer);
begin
for nbr := nbr to len do begin
productarray[ndx] := nbr;
if len < lnd then
combo(ndx+1,nbr+1,len+1,lnd)
else
producer(k);
end;
end;
begin
combo (0, 0, n-k, n-1);
end;
「プロデューサー」は、各組み合わせに対して作成されたproductarrayを処理します。
コレクション操作は必要ありません。この問題は、K個の入れ子になったループを巡回するのとほとんど同じですが、インデックスと境界に注意する必要があります(JavaとOOPを無視して)。
public class CombinationsGen {
private final int n;
private final int k;
private int[] buf;
public CombinationsGen(int n, int k) {
this.n = n;
this.k = k;
}
public void combine(Consumer<int[]> consumer) {
buf = new int[k];
rec(0, 0, consumer);
}
private void rec(int index, int next, Consumer<int[]> consumer) {
int max = n - index;
if (index == k - 1) {
for (int i = 0; i < max && next < n; i++) {
buf[index] = next;
next++;
consumer.accept(buf);
}
} else {
for (int i = 0; i < max && next + index < n; i++) {
buf[index] = next;
next++;
rec(index + 1, next, consumer);
}
}
}
}
こんな感じで使う:
CombinationsGen gen = new CombinationsGen(5, 2);
AtomicInteger total = new AtomicInteger();
gen.combine(arr -> {
System.out.println(Arrays.toString(arr));
total.incrementAndGet();
});
System.out.println(total);
期待される結果を得る:
[0, 1]
[0, 2]
[0, 3]
[0, 4]
[1, 2]
[1, 3]
[1, 4]
[2, 3]
[2, 4]
[3, 4]
10
最後に、インデックスを自分が持っている可能性のあるデータセットにマップします。
短い高速Cの実装
#include <stdio.h>
void main(int argc, char *argv[]) {
const int n = 6; /* The size of the set; for {1, 2, 3, 4} it's 4 */
const int p = 4; /* The size of the subsets; for {1, 2}, {1, 3}, ... it's 2 */
int comb[40] = {0}; /* comb[i] is the index of the i-th element in the combination */
int i = 0;
for (int j = 0; j <= n; j++) comb[j] = 0;
while (i >= 0) {
if (comb[i] < n + i - p + 1) {
comb[i]++;
if (i == p - 1) { for (int j = 0; j < p; j++) printf("%d ", comb[j]); printf("\n"); }
else { comb[++i] = comb[i - 1]; }
} else i--; }
}
どれくらい速いのかを見るためには、このコードを使ってテストしてください。
#include <time.h>
#include <stdio.h>
void main(int argc, char *argv[]) {
const int n = 32; /* The size of the set; for {1, 2, 3, 4} it's 4 */
const int p = 16; /* The size of the subsets; for {1, 2}, {1, 3}, ... it's 2 */
int comb[40] = {0}; /* comb[i] is the index of the i-th element in the combination */
int c = 0; int i = 0;
for (int j = 0; j <= n; j++) comb[j] = 0;
while (i >= 0) {
if (comb[i] < n + i - p + 1) {
comb[i]++;
/* if (i == p - 1) { for (int j = 0; j < p; j++) printf("%d ", comb[j]); printf("\n"); } */
if (i == p - 1) c++;
else { comb[++i] = comb[i - 1]; }
} else i--; }
printf("%d!%d == %d combination(s) in %15.3f second(s)\n ", p, n, c, clock()/1000.0);
}
cmd.exe(windows)でテストします。
Microsoft Windows XP [Version 5.1.2600]
(C) Copyright 1985-2001 Microsoft Corp.
c:\Program Files\lcc\projects>combination
16!32 == 601080390 combination(s) in 5.781 second(s)
c:\Program Files\lcc\projects>
良い一日を。
これは、ほとんどすべての変数を排除するreduce/mapの使用によりもう少し機能的な私のJavaScriptソリューションです。
function combinations(arr, size) {
var len = arr.length;
if (size > len) return [];
if (!size) return [[]];
if (size == len) return [arr];
return arr.reduce(function (acc, val, i) {
var res = combinations(arr.slice(i + 1), size - 1)
.map(function (comb) { return [val].concat(comb); });
return acc.concat(res);
}, []);
}
var combs = combinations([1,2,3,4,5,6,7,8],3);
combs.map(function (comb) {
document.body.innerHTML += comb.toString() + '<br />';
});
document.body.innerHTML += '<br /> Total combinations = ' + combs.length;
この答えはどうですか...これは長さ3のすべての組み合わせを出力します...そしてそれは任意の長さに一般化することができます...実用的なコード...
#include<iostream>
#include<string>
using namespace std;
void combination(string a,string dest){
int l = dest.length();
if(a.empty() && l == 3 ){
cout<<dest<<endl;}
else{
if(!a.empty() && dest.length() < 3 ){
combination(a.substr(1,a.length()),dest+a[0]);}
if(!a.empty() && dest.length() <= 3 ){
combination(a.substr(1,a.length()),dest);}
}
}
int main(){
string demo("abcd");
combination(demo,"");
return 0;
}
これを行うためにビットの概念を使うことができます。 "abc"という文字列があり、長さ2の要素のすべての組み合わせ(つまり、 "ab"、 "ac"、 "bc")が欲しいとします。
セットされたビットは1から2 ^ n(排他的)までの範囲の数で見つけることができます。ここでは1から7、そしてbits = 2に設定したところはどこでも、stringから対応する値を出力することができます。
例えば:
print ab (str[0] , str[1])
print ac (str[0] , str[2])
print ab (str[1] , str[2])
コードサンプル:
public class StringCombinationK {
static void combk(String s , int k){
int n = s.length();
int num = 1<<n;
int j=0;
int count=0;
for(int i=0;i<num;i++){
if (countSet(i)==k){
setBits(i,j,s);
count++;
System.out.println();
}
}
System.out.println(count);
}
static void setBits(int i,int j,String s){ // print the corresponding string value,j represent the index of set bit
if(i==0){
return;
}
if(i%2==1){
System.out.print(s.charAt(j));
}
setBits(i/2,j+1,s);
}
static int countSet(int i){ //count number of set bits
if( i==0){
return 0;
}
return (i%2==0? 0:1) + countSet(i/2);
}
public static void main(String[] arhs){
String s = "abcdefgh";
int k=3;
combk(s,k);
}
}
C++で組み合わせ用の一般クラスを作りました。こんな感じで使われています。
char ar[] = "0ABCDEFGH";
nCr ncr(8, 3);
while(ncr.next()) {
for(int i=0; i<ncr.size(); i++) cout << ar[ncr[i]];
cout << ' ';
}
私のライブラリncr [i]は、0からではなく1から戻ります。そのため、配列に0があります。順序を考慮したい場合は、nCrクラスをnPrに変更してください。使い方は同じです。
結果
ABC ABD ABE ABF ABG ABH ACD ACE ACF ACG ADE ADF ADG ADH AEG AEG AEH AFH AGH BCD BCE BCF BCG BCH BDF BDG BEF BEG BEH BFG BFH BGH CDE CDF CDG CDH CEF CFG CFH CGH DEG DFH DGH EFG EFH EGH FGH
これがヘッダファイルです。
#pragma once
#include <exception>
class NRexception : public std::exception
{
public:
virtual const char* what() const throw() {
return "Combination : N, R should be positive integer!!";
}
};
class Combination
{
public:
Combination(int n, int r);
virtual ~Combination() { delete [] ar;}
int& operator[](unsigned i) {return ar[i];}
bool next();
int size() {return r;}
static int factorial(int n);
protected:
int* ar;
int n, r;
};
class nCr : public Combination
{
public:
nCr(int n, int r);
bool next();
int count() const;
};
class nTr : public Combination
{
public:
nTr(int n, int r);
bool next();
int count() const;
};
class nHr : public nTr
{
public:
nHr(int n, int r) : nTr(n,r) {}
bool next();
int count() const;
};
class nPr : public Combination
{
public:
nPr(int n, int r);
virtual ~nPr() {delete [] on;}
bool next();
void rewind();
int count() const;
private:
bool* on;
void inc_ar(int i);
};
そして実装。
#include "combi.h"
#include <set>
#include<cmath>
Combination::Combination(int n, int r)
{
//if(n < 1 || r < 1) throw NRexception();
ar = new int[r];
this->n = n;
this->r = r;
}
int Combination::factorial(int n)
{
return n == 1 ? n : n * factorial(n-1);
}
int nPr::count() const
{
return factorial(n)/factorial(n-r);
}
int nCr::count() const
{
return factorial(n)/factorial(n-r)/factorial(r);
}
int nTr::count() const
{
return pow(n, r);
}
int nHr::count() const
{
return factorial(n+r-1)/factorial(n-1)/factorial(r);
}
nCr::nCr(int n, int r) : Combination(n, r)
{
if(r == 0) return;
for(int i=0; i<r-1; i++) ar[i] = i + 1;
ar[r-1] = r-1;
}
nTr::nTr(int n, int r) : Combination(n, r)
{
for(int i=0; i<r-1; i++) ar[i] = 1;
ar[r-1] = 0;
}
bool nCr::next()
{
if(r == 0) return false;
ar[r-1]++;
int i = r-1;
while(ar[i] == n-r+2+i) {
if(--i == -1) return false;
ar[i]++;
}
while(i < r-1) ar[i+1] = ar[i++] + 1;
return true;
}
bool nTr::next()
{
ar[r-1]++;
int i = r-1;
while(ar[i] == n+1) {
ar[i] = 1;
if(--i == -1) return false;
ar[i]++;
}
return true;
}
bool nHr::next()
{
ar[r-1]++;
int i = r-1;
while(ar[i] == n+1) {
if(--i == -1) return false;
ar[i]++;
}
while(i < r-1) ar[i+1] = ar[i++];
return true;
}
nPr::nPr(int n, int r) : Combination(n, r)
{
on = new bool[n+2];
for(int i=0; i<n+2; i++) on[i] = false;
for(int i=0; i<r; i++) {
ar[i] = i + 1;
on[i] = true;
}
ar[r-1] = 0;
}
void nPr::rewind()
{
for(int i=0; i<r; i++) {
ar[i] = i + 1;
on[i] = true;
}
ar[r-1] = 0;
}
bool nPr::next()
{
inc_ar(r-1);
int i = r-1;
while(ar[i] == n+1) {
if(--i == -1) return false;
inc_ar(i);
}
while(i < r-1) {
ar[++i] = 0;
inc_ar(i);
}
return true;
}
void nPr::inc_ar(int i)
{
on[ar[i]] = false;
while(on[++ar[i]]);
if(ar[i] != n+1) on[ar[i]] = true;
}
単純だが遅いC++バックトラッキングアルゴリズム.
#include <iostream>
void backtrack(int* numbers, int n, int k, int i, int s)
{
if (i == k)
{
for (int j = 0; j < k; ++j)
{
std::cout << numbers[j];
}
std::cout << std::endl;
return;
}
if (s > n)
{
return;
}
numbers[i] = s;
backtrack(numbers, n, k, i + 1, s + 1);
backtrack(numbers, n, k, i, s + 1);
}
int main(int argc, char* argv[])
{
int n = 5;
int k = 3;
int* numbers = new int[k];
backtrack(numbers, n, k, 0, 1);
delete[] numbers;
return 0;
}
Andrea AmbuのようなPythonでは、3を選択するためにハードコードされていません。
def combinations(list, k):
"""Choose combinations of list, choosing k elements(no repeats)"""
if len(list) < k:
return []
else:
seq = [i for i in range(k)]
while seq:
print [list[index] for index in seq]
seq = get_next_combination(len(list), k, seq)
def get_next_combination(num_elements, k, seq):
index_to_move = find_index_to_move(num_elements, seq)
if index_to_move == None:
return None
else:
seq[index_to_move] += 1
#for every element past this sequence, move it down
for i, elem in enumerate(seq[(index_to_move+1):]):
seq[i + 1 + index_to_move] = seq[index_to_move] + i + 1
return seq
def find_index_to_move(num_elements, seq):
"""Tells which index should be moved"""
for rev_index, elem in enumerate(reversed(seq)):
if elem < (num_elements - rev_index - 1):
return len(seq) - rev_index - 1
return None
以下のHaskellコードは 組み合わせ番号と組み合わせを同時に計算します そしてHaskellの怠惰のおかげで、あなたは他を計算せずにそれらの一部を得ることができます。
import Data.Semigroup
import Data.Monoid
data Comb = MkComb {count :: Int, combinations :: [[Int]]} deriving (Show, Eq, Ord)
instance Semigroup Comb where
(MkComb c1 cs1) <> (MkComb c2 cs2) = MkComb (c1 + c2) (cs1 ++ cs2)
instance Monoid Comb where
mempty = MkComb 0 []
addElem :: Comb -> Int -> Comb
addElem (MkComb c cs) x = MkComb c (map (x :) cs)
comb :: Int -> Int -> Comb
comb n k | n < 0 || k < 0 = error "error in `comb n k`, n and k should be natural number"
comb n k | k == 0 || k == n = MkComb 1 [(take k [k-1,k-2..0])]
comb n k | n < k = mempty
comb n k = comb (n-1) k <> (comb (n-1) (k-1) `addElem` (n-1))
それはのように動作します:
*Main> comb 0 1
MkComb {count = 0, combinations = []}
*Main> comb 0 0
MkComb {count = 1, combinations = [[]]}
*Main> comb 1 1
MkComb {count = 1, combinations = [[0]]}
*Main> comb 4 2
MkComb {count = 6, combinations = [[1,0],[2,0],[2,1],[3,0],[3,1],[3,2]]}
*Main> count (comb 10 5)
252