• ベストアンサー

組み合わせ問題のアルゴリズム

あらかじめ用意された整数を足して、その合計がある指定された整数と等しくなる組み合わせの数を調べるプログラムを書こうとしているのですが、苦労しています。 具体例がないと伝わりにくいかもしれないので例をあげると、 例えばあらかじめ用意された整数というのが 1・1・2・2・5・8 の4つで、 指定された整数が10である場合は、 8と2 8と1と1 5と2と2と1 という3通りの組み合わせがあるので、3を出力したいというわけです。 今まではもっと単純なアルゴリズムしか考えてこなかったので、こういった組み合わせのような問題が難しく感じられます。 こういう場合、アルゴリズムはどのようなものが考えられるでしょうか。 よろしくお願いします。

質問者が選んだベストアンサー

  • ベストアンサー
  • rabbit_cat
  • ベストアンサー率40% (829/2062)
回答No.2

何か上手い手を考え付かないんだったら、バックトラック法でしょうね。完全な総当りよりは、かなり計算量を減らせるはずです。 バックトラックは再帰で実装すると簡単です。(メモリ食うけど)

cheesue
質問者

お礼

バックトラックというのは初めて聞く言葉です。 少し調べてみたら、おっしゃる通り再起関数をうまく使う必要があるようですね。 参考になります。ありがとうございました。

その他の回答 (2)

回答No.3

//C#で書く。総当り。 using System; using System.Collections.Generic; //ソースがやけに見づらいのは //http://d.hatena.ne.jp/akkt/20080424/1209051266 //の1を努力しようとしたから。挫折したけど。 //回答にはしないけど気力が沸いたらもっとクラスに分割して,50行に収めるかも。 //特にNumberListは独自のクラスで分割できそうだし。 namespace Q4304402B { class Q4304402B { private List<int> NumberList; private List<List<int>> AnswerList; public Q4304402B(){ NumberList = new List<int>(); AnswerList = new List<List<int>>(); NumberList.AddRange(new int[]{1,1,2,2,5,8}); //例) //i =13(2進数で001101)の時, //1 * 0 + 1 * 0 + 2 * 1 + 2 * 1 + 5 * 0 + 8 * 1の総和を求める。 //それが10ならばリストに追加 //iを0からMath.Pow(2,6) - 1 = 63まで変化させる for(int i = 0;i < Math.Pow((double)2,(double)NumberList.Count) ; i++){ Test(i); } } private List<int> GetNumbers(List<int> list1){ List<int> list = new List<int>(); if (NumberList.Count != list1.Count){ throw new ArgumentException(); } for (int i = 0;i < list1.Count;i++){ if (list1[i] == 1){ list.Add(NumberList[i]); } } return list; } //引数の10進数を2進数をcount ビットで表現し //各ビットのint値をリストに追加し,そのリストを返す。 private List<int> GetFlags(int count,int i){ List<int> list; list = new List<int>(); for (int j = 0;j < NumberList.Count;j++){ list.Add(( i >> j ) % 2); } return list; } //listに入っているintの合計を求める。 private int GetSum(List<int> l1){ int x = 0; foreach(int i in l1){ x += i; } return x; } private void Test(int i){ List<int> list = GetNumbers(GetFlags(NumberList.Count,i)); if ((GetSum(list) == 10) && IsAddable(list)){ AnswerList.Add(list); } } //既にリスト中に同じ構成要素からなるものが存在しないため, //答えとして有効。 //たとえば,既にAnswerListに{1,2,2,5}となるようなものが存在するとき, //仮引数listが{1,2,2,5}ならfalse,{1,1,8}とかならtrue private bool IsAddable(List<int> list){ bool retval = true; foreach(List<int> l in AnswerList){ l.Sort(); retval = retval && !CompareList(list,l); } return retval; } //個数が一致しないときはfalse; //内容を各リストの同一インデックスの内容を比較し, //全て一致したらtrue,そうでないときはfalseを返す。 private bool CompareList(List<int> l1,List<int> l2){ bool retval = true; l1.Sort(); l2.Sort(); if (l1.Count != l2.Count){ return false; } for (int i = 0;i < l1.Count;i++){ retval = retval && (l1[i] == l2[i]); } return retval; } //答え表示用のルーチン public void Display(){ Console.WriteLine(AnswerList.Count); // foreach(List<int> list in AnswerList){ // DisplayItem(list); // System.Console.WriteLine("================="); // } } //Displayメソッドで使用しようかと思ったけど, //求められているのは個数なので封印。 private void DisplayItem(List<int> list){ foreach(int i in list){ System.Console.WriteLine(i); } } public static void Main(){ Q4304402B o1 = new Q4304402B(); o1.Display(); System.Console.ReadKey(true); //キー押すまで待機 } } }

cheesue
質問者

お礼

わざわざコードまで書いていただいてありがとうございます。 Cは使ったことがないので理解しているとは言い難いのですが、コメントも参考にさせていただきながら解釈していこうと思います。

  • neKo_deux
  • ベストアンサー率44% (5541/12319)
回答No.1

> 組み合わせの数を調べる 近似解を許さないのなら、総当りするしかないのでは? 質問者さんが思っているような、単純なアルゴリズムで良いと思います。

cheesue
質問者

お礼

回答ありがとうございます。 計算量が少なくなるというバックトラック法を先に試してみたいと思います。

関連するQ&A

  • 遺伝的アルゴリズムの評価式に関する質問です。

    膨大な数の組み合わせから正解の組み合わせを求めるという大規模組み合わせ問題があったとします。 このような問題を遺伝的アルゴリズムを用いて解こうとしているのですが、今用いている評価式より良いアイデアが自分では考えつかなかったため質問します。 以下、問題や用いている遺伝的アルゴリズムに関する詳しい説明です。 例えば、仮に、23, 21, 65, 78, 43, 78, 83, 56, 78 ,89 の10個の数の組み合わせがあるとき、 合計して109になる組み合わせ(23,21,65)を見つけたいという問題です。(正解の組み合わせは複数個あっても、一個見つかれば良い。また正解の組み合わせは必ずあるものとする。) この問題を遺伝的アルゴリズム(GA)を使って解くとします。 以下、簡単なGAの説明です。 表現型に2進数ビット列を用いる。 個体数は200とし、初期個体はランダムで生成する。 評価式はf(x) = b/(b+|b-t|)(bは正解の組み合わせの合計値で、tは2進数ビット列で1を立てた場所の数の合計値である。)。終了条件はこの評価値がf(x)=1になることである。 交叉は一様交叉で突然変異も行う。 表現型について詳しく説明すると、 コード化に 0と 1の並びである2進数ビット列を用いて、 その場所に対応する数を加算する場合は1を, 逆に加算しない場合は0を遺伝子の表現型に立てビット列を生成しました。 例えば今回の正解の組み合わせ(109)を2進数ビットで表すと下のようになる。 23, 21, 65, 78, 43, 78, 83, 56, 78 ,89 1 1 1 0 0 0 0 0 0 0 ←2進数ビットを用いた表現型。 (1が立っている場所の数が加算されて合計で109となり、これが正解の組み合わせであることがわかる。) そして、この遺伝的アルゴリズムの評価式を f(x) = b/(b+|b-t|) とします。(bは正解の組み合わせの合計値で、tはビット列で1を立てた場所に対応する数の合計値である。) 評価式f(x)=1になる、つまり正解の組み合わせが見つかれば、遺伝的アルゴリズムは終了する。 この評価式で遺伝的アルゴリズムを回しているのですが、この簡単な評価式では近似解に陥ったとき、解を求めるのがどうしても遅くなってしまいます。 全体的に長く、わかりにくい説明で申し訳ないのですが、この評価式の改善案、またはこの遺伝的アルゴリズムの改善案などがあれば教えていただきたいです。 以上、よろしくお願いします。

  • アルゴリズムについての問題

    (1)AとBは正の整数値とする。 (2)A=L,B=Sとして代入する。 (3)L:Sで比較したときにL>Sの場合はL-S→L(矢印は代入を表す)としてL<Sの場合はS-L→Sとする。 (4)どちらかの計算をした後にLとSを比較し、=(イコール)の場合はLとして出力する。 (5)=でない場合は(3)に戻る。LはA,Bに関してどのような関係であるか簡潔に答えよ。 という問題がありました。私はLはAとBの最大公約数と答えました。 あっていますでしょうか?またこのアルゴリズムの名前のようなものがあれば教えてください。

  • N個の値の大小関係の組み合わせ数

    N個の値の大小関係の組み合わせ数を求めるプログラムのアルゴリズムを考えていますがうまくいきません。(3個の値の場合は13通り) スマートに求める式をご存知の方、教えていただけないでしょうか

  • 最適円順列を求めるアルゴリズムについて教えてください

    ExcelのVBAでプログラムを作っているのですが、ある問題で   つまづいてしまいました。     問題は、30個のデータ(#1~#30とします)を円順列で   並べます。 (#1=10とか#18=24などの数値が入ります)   その場合の組み合わせは29!通りありますよね   そして、この組み合わせそれぞれである計算をさせます。   (ベクトル計算のようなものです)   その答えのもっとも最適な結果(今回の場合最小)が出る   組み合わせを見つけ出したいのです。   最適結果を導き出すアルゴリズムをご指導下さいお願いします。             

  • 組み合わせのプログラム

    組み合わせのプログラムを考えています。 例として tray[0]=1000, tray[1]=500, tray[2]=300 があるとします。 各配列の値を使って、その合計値が例えば「1000 以下」と言う条件に当てはまる組み合わせは 1000, 800(500+300), 500, 300, (0) です(各配列の値は1回だけ使用可能とします)。 1つの tray に対して、それを「足すか」「足さないか」の2通りが考えれるので、全体で2^n個(trayの数をnとする)の組み合わせを調べれば良いと思っています(これは間違っているのかな?!) プログラムのイメージは以下のような感じです。 int sum,x; (ここは x を使って if か for を使った足すか足さないかの条件ではないか!?){ sum=0; for(int i=0;i<n;i++){ sum+=tray[i]*x;//ここでさきほどの x を使うのではないか、x に 0 or 1 が入ってくるイメージです if(指定した数字(条件)>=sum) System.out.print("ここで組み合わせの出力"); } } 初歩的な質問でお恥ずかしいです。 意味的に、かなりはしょった部分があるので、言いたい意味が分からないなど、ご質問がある方はご遠慮なくして下さい。 色々頑張ってみたのですが無理でした、もしご解答いただける方がいればすごく助かります。 宜しくお願いします。

    • ベストアンサー
    • Java
  • C++でのアルゴリズム

    次の条件を満たすアルゴリズムをC++のコードで教えてください! 大きさ2×1の長方形n個を 縦2 横n の長方形になるように並べるときの並べ方の総数を求めるアルゴリズム 入力nは、1以上の整数が入力される前提でよい。 例として、 n=1 1通り n=2 2通り n=3 3通り n=5 8通り n=7 21通り となります お願いします。

  • 組み合わせの数を計算する数式を教えて下さい。

    12個数字(1~12)の組み合わせの数を計算する数式を知りたいです。 ルール: 1.最低1個、最大12個数字を選べます 2.重複不可 3.順番関係なし 一応、プログラムで全組み合わせを出力しましたが、正しいかどうかは不明です。 一応組み合わせの数は4095らしいです。 出力例: "1" "1, 2" "1, 2, 3" "1, 2, 3, 4" "1, 2, 3, 4, 5" "1, 2, 3, 4, 5, 6" "1, 2, 3, 4, 5, 6, 7" "1, 2, 3, 4, 5, 6, 7, 8" "1, 2, 3, 4, 5, 6, 7, 8, 9" "1, 2, 3, 4, 5, 6, 7, 8, 9, 10" "1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11" "1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12" "1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 12" "1, 2, 3, 4, 5, 6, 7, 8, 9, 11" "1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 12" "1, 2, 3, 4, 5, 6, 7, 8, 9, 12" "1, 2, 3, 4, 5, 6, 7, 8, 10" "1, 2, 3, 4, 5, 6, 7, 8, 10, 11" "1, 2, 3, 4, 5, 6, 7, 8, 10, 11, 12" "1, 2, 3, 4, 5, 6, 7, 8, 10, 12" "1, 2, 3, 4, 5, 6, 7, 8, 11" "1, 2, 3, 4, 5, 6, 7, 8, 11, 12" "1, 2, 3, 4, 5, 6, 7, 8, 12" "1, 2, 3, 4, 5, 6, 7, 9" "1, 2, 3, 4, 5, 6, 7, 9, 10" "1, 2, 3, 4, 5, 6, 7, 9, 10, 11" … … … "8, 12" "9" "9, 10" "9, 10, 11" "9, 10, 11, 12" "9, 10, 12" "9, 11" "9, 11, 12" "9, 12" "10" "10, 11" "10, 11, 12" "10, 12" "11" "11, 12" "12"

  • アルゴリズムと問題解決に関する問題

    二つの正の整数A,Bの最大公約数を求めるユークリッドの互除法のアルゴリズム 互除法(A,B) 入力正の整数A,B 1 もしA<Bならば 2 BをAで割ったあまりをCとせよ 3 もしC=0でないならAを出力して終了 4 そうでないならば 5 GCD=互除法(A,C)とし、GCDを出力し終了 6 そうでないならば 7 AをBで割ったあまりをCとせよ 8 もしC=0ならばBを出力して終了 9 そうでないならば 10 GCD=互除法(B,C)とし、GCDを出力し終了 互除法(48,84)を行ったときのうえのアルゴリズムの動作(再帰呼び出しの途中に現れるCやGCDの値)を求めよ これを一応といてみましたが表現方法がいまいちわからないので良い回答の仕方教えてください。 自分で解いた答え 1 A<BなのでB/Aのあまり36をCとする 2 C=36 3 Cは0でない 4、5 GCD=(A,C)=(48,36) 1に戻る A=48,B=36とする 1 A<Bでない 6 A/BのあまりをCとする 7 48/36のあまり C=12 8 Cは0でない 9,10 GCD=(36,12) 1に戻る A=36,B=12 1 A<Bでない 6,7 36/12 C=0 8 C=0 B=12

  • ヒントのない問題(組合せ)

    「題意からは何を(どっちを)問いたいのか判断ができないな」と悩む問題がでてきました。 問 1から10までの10個の整数から、5個の整数を選んで5個の組を作る。このとき、最大値が8の組は何通りあるか。 ☆僕が思いついた解き方 最大値が8ということは、あとの4つは1~7から選ぶわけだから、純粋に8C5で解けるはず。もしくは、8P5で解けるはずだ。 ↓ 問の題意は、「何通りあるか」なわけだから、123でも132でも 別々のものと捉えて、総じて「全部で何通りか」と問いていると解釈するのが自然である。 ↓ よって、正しい解き方は8P5である、と考えました。 ☆テキストの解説 まず8を選んで、あとの4つは1~7から選べばよいので、7C4 =35通りが正解。 ☆テキストの解説を読んで感じた疑問点 7C4では、「123」「132」を同じものとして捉えた場合に使う式であり、この問題では「何通りあるか」と問いてはいても、決して「組合せはいくつあるか」とは問いていない…よって、7C4は不適切であると感じています。 また、仮にこのCが正しかったとしても、8C5ではダメで7C4なら正しいという理由がないと感じています。なぜなら、8C5は、1~8のうち5個を選び抜き出す、という式にちゃんとなっています。 7C4だと、5つ並ぶ数字のうち、8の位置が無視されているため、不適切であると感じています。 「組合せはいくつあるか」ではなく、わざわざ「何通りあるか」という表現をしている問本文が最大のヒントだと思いましたが、どうやらそれは違っているようです。しかし、選び方ではなく組合せをここでは問いている、というヒント(証拠)がないため、これ以上は解き手には判断のしようがありません。 正しい解き方はなぜ正しいのですか。また、何をヒントにどれが正しいと見極めればよいのですか。いくら勉強をしても、その問題問題によってパターンが全く違うため、勉強になっていないということに困っています。よろしくお願いいたします。

  • 数値を複数の群に分ける最適な組み合わせを求める方法

    次のような問題をCで記述するにはどのようにすればいいでしょうか? 要素数が不定の整数値配列がある。 この配列の各要素を、与えられた個数の群に分ける。 条件として、与えられた数値列で隣り合う数値しか同じ群に含めることは出来ない。 例: 数値列は{5,2,7,12,6,15,4} 群の個数を3とする。 (1) {5,2},{7,12,6},{15,4} (2) {5},{2,7},{12,6,15,4} (3) {5,2,7},{12},{6,15,4} ・・・ と複数の組み合わせがある。 これらの組み合わせのうち、各群の合計値が最も均等になるような組み合わせを求める。最大値と最小値の差が最小となる組み合わせを最も均等と考える。 上の例であれば、各群の合計値と、合計値の最大値と最小値の差は、 (1) {5,2},{7,12,6},{15,4} ==> 7,25,19 (25-7=18) (2) {5},{2,7},{12,6,15,4} ==> 5,9,37 (37-5=32) (3) {5,2,7},{12},{6,15,4} ==> 14,12,25 (25-12=13) ☆ のようになり、この3つの中で最も均等なのは (3) {5,2,7}{12}{6,15,4} となる。 実際は、これ以外の組み合わせでより均等となるものがあるかと思います。 この問題そのものが必要なわけではなく、この結果を利用して別のある問題を解決しようとしています。 数値の数が増えると組み合わせの数も大幅に増えて計算時間に影響すると思います。 全ての組み合わせを試すことなく答えにたどり着く方法があれば、考え方だけでも提示頂ければと思います。

専門家に質問してみよう