• ベストアンサー
※ ChatGPTを利用し、要約された質問です(原文:数値を複数の群に分ける最適な組み合わせを求める方法)

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

このQ&Aのポイント
  • 数値を複数の群に分ける方法について説明します。与えられた数値列で隣り合う数値しか同じ群に含めることはできず、各群の合計値が最も均等になる組み合わせを求めることが目標です。
  • 例えば、数値列{5,2,7,12,6,15,4}を3つの群に分ける場合、{5,2,7},{12},{6,15,4}という組み合わせが最も均等です。
  • 実際の問題では、組み合わせの数が増えると計算時間に影響するため、全ての組み合わせを試さずに最適な組み合わせを求める方法を提示してください。

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

  • ベストアンサー
回答No.27

>回答No.23 amanojaku1 「2018/06/15 03:48:47」の時点で修正しました。 それ以前にコピーしている場合は注意して下さい。 数値を複数の群に分ける最適な組み合わせを求める方法 http://ashtarte.pa.land.to/utf8/smt.cgi?r+sara/&bid+000000DC&tsn+000000DC&bts+2018/06/15%2002%3A04%3A46&

katorea21
質問者

お礼

コメント追加して頂きありがとうございます。今頑張って理解しているところですが、何となく分かってきました。 それにしても2進数の数学的特徴をうまくこの問題に当てはめるという発想が凄いですね。問題の本質は順列のパターンを網羅することですが、私は再帰を使うイメージしかなかったです。

全文を見る
すると、全ての回答が全文表示されます。

その他の回答 (91)

回答No.52

>回答No.51 amanojaku1 分岐予測が外れたら(CPUの)パイプラインのStall(ストール:停止状態)が発生します。 「min、max」ライブラリ関数が内部的に どうなってるか分かりませんが、もし内部的に分岐が使われてないとすれば(インライン化すれば)パフォーマンスが良い可能性があります(あくまで可能性ですが)。 なので どうなるか試してみて下さい。

katorea21
質問者

お礼

min_element,max_elementはSTLのライブラリ関数なのでinline化は出来ません。他の環境なら可能かも知れませんが、VisualC++では出来ないと思います。自分がやったのは、関数呼び出しではなく最大値最小値を求めるロジックを直接whileループ中に書きました。つまり普通にinlineになっています。

全文を見る
すると、全ての回答が全文表示されます。
回答No.51

>ライブラリ関数は使わず (インライン化したライブラリ関数がパフォーマンスが良い可能性もあるかもしれないので)一応「min、max」ライブラリ関数をインライン化して どうなるか試してみて下さい。

全文を見る
すると、全ての回答が全文表示されます。
回答No.50

>gmmin = *min_element(gmt, gmt+gp); ←内部でループしてると思われる >gmmax = *max_element(gmt, gmt+gp); ←内部でループしてると思われる 下記はJavaScriptです。 インライン化は良く分からないので、「#pragma forceinline」で良いのか良く分かりません。 上記はループが2つ、下記はループが1つ、なのでパフォーマスが改善する"可能性"があります(あくまで"可能性"ですが…)。 gmmin = gmt[0]; gmmax = gmt[0]; for(let m = 1; m<gmt.length; m++){ // 1からスタートしていることに注意して下さい。 gmmin = Math.min(gmmin,gmt[m]); // ←「min」関数を「#pragma forceinline」でインライン化を定義しておく gmmax = Math.max(gmmax,gmt[m]); // ←「max」関数を「#pragma forceinline」でインライン化を定義しておく }

katorea21
質問者

お礼

やってみたら1.8秒→1.5秒に改善しました。ライブラリ関数は使わず自前で全部書いてみました。 なかなか奥が深いですね。たったこれだけで2割も早くなります。 単純な処理と思っていたものが、ループの中だとかなりのインパクトになるのですね。こうした純粋なアルゴリズムでは、ブラックボックスなライブラリやクラスは一切使用しない方が良さそうです。

全文を見る
すると、全ての回答が全文表示されます。
回答No.49

まあ、細かい事を言っていても、恐らく大してパフォーマスには影響ないでしょう。 >gmmin = *min_element(gmt, gmt+gp); >gmmax = *max_element(gmt, gmt+gp); ↑ここがパフォーマスの肝になると分かったのですから、手動で最適化してみて試してみるのも良いかもしれません。

全文を見る
すると、全ての回答が全文表示されます。
回答No.48

>回答No.46 amanojaku1 >if ((qc < gp) && (i == (gmq_st + gmq[qc]))) { >gmq_st += gmq[qc]; >qc++;gmc++; >} 実際のパフォーマス的にどうでしょうか? もし、パフォーマス的に変わらないならスマートな記述をオススメします。

全文を見る
すると、全ての回答が全文表示されます。
回答No.47

>回答No.46 amanojaku1 変数を減らすならpcを減らすほうがスマートです(パフォーマス的に どうかは分かりませんが)。 >if(pc<gmp.length){ >if(gmp[pc]==i){ >pc++; >gmc++; >} >} if((gmc+1)<gmp.length){ if(gmp[(gmc+1)]==i){ gmc++; } }

全文を見る
すると、全ての回答が全文表示されます。
回答No.46

>配列のソートに時間がかかっていたようです。 そこが問題でしたか。 >最大値、最小値の算出にソートは必要ないですね。 そう言われると、そうですね(^_^;)。 >この結果、2000万パターンで約1.8秒まで改善しました。ちなみに要素数28、グループ数14で約2000万パターンとなります。 これで充分に実用的ですね。 >gmpは使用しない。 >if ((qc < gp) && (i == (gmq_st + gmq[qc]))) { >gmq_st += gmq[qc]; >qc++;gmc++; >} かえって処理が複雑になってますが、変数を減らした方が良いと言う判断ですか。 gmq_stが増えているので、どちらが良いとも一概には言えないかもしれません。 まあ、全体の処理からすれば些細な事でしょうが。 if(gmp[pc]==i){ pc++; gmc++; }

全文を見る
すると、全ての回答が全文表示されます。
回答No.45

>C言語はサッパリというのは? >そもそもここはC/C++/C#のスレッドですが。 C言語(C++も)とか、サッパリ分からないのでJavaScriptで組んだ訳です、JavaScript→C++への変換も それほど難しくないだろうと思ったので…。 >それだけの知識がありながら プリプロセッサ指令に関してはパスカル系で同様なコンパイラ・スイッチ?みたいなモノが有ったので、当然 C++にも同様なモノが有るだろうとググっただけです。 386(32bit)ではコンパイラーによる最適化の恩恵はあまり受けらないと言う批判を受けて、Intelはx64ではレジスタを倍増したと言うのは割りと有名な話です。 今回、たまたまググっていてC言語?(C++?)でx64でもintは32bitだと言う記事を どこかで読みました(マジか?と本当にビックリしました)。 今回、たまたまググっていてC言語で386(32bit)で「16bit、8bit」演算ではマスク処理が入り遅くなると言う記事を見つけたので、x64でも「32bit、16bit、8bit」演算ではマスク処理が入り遅くなるだろうと推測される訳です。

katorea21
質問者

お礼

アルゴリズムの改善を少し頑張ってみました。具体的にはwhileループの中の次のような見直しをしました。 ・std::vectorをやめて普通の配列にする。 ・gmpは使用しない。gmqだけで記述可能なので。 ・gmqの最大値、最小値の算出時にソートをしない。 また全ての数値を64bit型に変更しました。 whileループは最終的に約50行となりました。1番最初のパターンのみ特殊扱いになってしまうのが気になりますが。 この結果、2000万パターンで約1.8秒まで改善しました。ちなみに要素数28、グループ数14で約2000万パターンとなります。(27_C_13=20058300) 既に現実の応用範囲を超えており、プログラムの勉強みたいな感じになっていますが。 配列のソートに時間がかかっていたようです。最大値、最小値の算出にソートは必要ないですね。 #include <algorithm> using namespace std; typedef unsigned long long ULLONG; void CalcOptimalGrouping(const ULLONG* gpa, const ULLONG gpa_size, const ULLONG gp) { ULLONG* gmq = new ULLONG[gp]; fill(gmq, gmq + gp, 1); ULLONG* gmp = new ULLONG[gp]; fill(gmp, gmp + gp, 0); ULLONG* gmt = new ULLONG[gp]; ULLONG* gtmp = new ULLONG[gp]; ULLONG gmmin = -1, gmmax = -1, gmmdif = -1; ULLONG gtmin = -1, gtmax = -1, gtmdif = -1; ULLONG gppc = 0; while (0 < gmq[gp - 1]) { gppc++; fill(gmt, gmt+gp, 0); if (gppc == 1) { gmq[gp - 1] = gpa_size; for (size_t i = 0; i < (gp - 1); i++) { gmq[gp - 1] -= gmq[i]; } } ULLONG gmc = 0; size_t qc = 0; ULLONG gmq_st = 0; for (size_t i = 0; i < gpa_size; i++) { if ((qc < gp) && (i == (gmq_st + gmq[qc]))) { gmq_st += gmq[qc]; qc++; gmc++; } gmt[gmc] += gpa[i]; } gmmin = *min_element(gmt, gmt+gp); gmmax = *max_element(gmt, gmt+gp); gmmdif = gmmax - gmmin; if ((gtmdif < 0) || (gtmdif > gmmdif)) { gtmdif = gmmdif; gtmin = gmmin; gtmax = gmmax; copy(gmq, gmq+(ULLONG)gp, gtmp); } for (size_t k = 0; k < (gp - 1); k++) { gmq[k]++; for (size_t i = 0; i < k; i++) { gmq[i] = 1; } gmq[gp - 1] = (ULLONG)gpa_size; for (size_t i = 0; i < (gp - 1); i++) { gmq[gp - 1] -= gmq[i]; } if (0 < gmq[gp - 1]) break; } } //<<Answer>> delete[] gmq; delete[] gmp; delete[] gmt; delete[] gtmp; }

全文を見る
すると、全ての回答が全文表示されます。
回答No.44

>説明不足でした。計測時はデバッグ出力は全て無効化しています。最適化は最大の/O2としています。この状態で2000万パターンが5~6秒程度でした。コードもかなり最適化したつもりですが、まだ計算量を減らせる部分があるでしょうか。 C言語(C++も)とか、サッパリ分かりませんが、「回答No.17 amanojaku1」で言及したようにx64で下記のように「int」では遅くなるのではないかと思われます(ちなみに386(32bit)では最適化の恩恵はあまり受けられません)。 「int_fast64_t」の定義がない場合、「64bit」幅の変数を定義してみて下さい。 >vector<int> gmq(gp, 1); >const size_t gmq_size = gmq.size(); >vector<int> gmp(gp, 0); >const size_t gmp_size = gmp.size(); >vector<int> gmt(gp); >const size_t gmt_size = gmt.size(); > >vector<int> gtmp; >int gmmin = -1; >int gmmax = -1; >int gmmdif = -1; >int gtmin = -1; >int gtmax = -1; >int gtmdif = -1;

katorea21
質問者

お礼

amanojaku1さんは何者ですか? それだけの知識がありながらC言語はサッパリというのは? そもそもここはC/C++/C#のスレッドですが。

全文を見る
すると、全ての回答が全文表示されます。
回答No.43

訂正です >define hoge 0 #define hoge 0 下記では「#define」になってますね。 もう一度基礎からC言語 第14回 ヘッダファイルとプリプロセッサ指令 > 主なプリプロセッサ指令 https://www.grapecity.com/tools/support/powernews/column/clang/014/page02.htm #if、#elif、#else、および #endif ディレクティブ (C/C++) https://msdn.microsoft.com/ja-jp/library/ew2hz0yd.aspx

全文を見る
すると、全ての回答が全文表示されます。

関連するQ&A

  • 2つの群の2値の平均値が最小となる組み合わせを

    いつもお世話になっております。 少しややこしい内容なので説明が分かりにくくなってしまうと思うので忖度に期待して質問させていただきます。 図のように1群、2群にA、BのデータがNo1から順に最大30個(2群は4~15個)のデータの 1)A(1)列の2値の平均値と2群のA(2)列の2値の平均値の差が最小になる2つを見つける。 2)同様にB(1)列の2値の平均値とB(2)列の2値の平均値の差が最小になる組み合わせを見つける。 3)1)と2)の合計が最小になる組み合わせのデータNoを知りたいのです。 例えばイメージ(入力数値は無視)として1群からデータNo4とNo8を選んだ時に、2群のNo20とNo27を組み合わせるとA、Bの2値の平均値の誤差が最小値になる組み合わせを知りたいのです。 現在は差の絶対値を行列数式でA(1)のデータNo1とA(2)のNo16からNo30までの総当たりで、同様にB1についても総当たりで求めて、両方の差の和の一番小さくなるケースを探しているのですが精度(やり方)が悪いのでお知恵拝借したく、わかりにくくお手数ですが宜しくお願いします。

  • 列の数値群の数値の抽出方法(VBA)

    エクセルVBAでお願いします。 今、B列にRange(Cells(3,2),Cells(I.2))の範囲にランダムな実数値が 格納されているとします。(Iは変数) この数値群から、正の実数で最小値、または負の実数で最大値を抽出したいのですが、どのようなコードを組めばよいでしょうか。 数値群は大小順ではなくランダムに並んでいるとします。お願いいたします。数値群の数は約20000個あります。

  • 群数列です

    1から順に自然数を並べて次のように群にわける。 1/2,3/4,5,6,7/8,9,…/…… ただし第n群が含む数の個数は2のn-1乗個である。 第n群に含まれる数の総和が10000を超えない最大のnを求めよ。 という問題なんですが、わかる方解答お願いします。

  • n次対称群の要素を互換で表すときの最大個数

    n次対称群(置換群)の要素を、隣接互換で表すときの最大個数は、転倒数が最大のものあり、 C(n-1,2)=Σ[k=1,n-1]k=n(n-1)/2 ただし、Cは二項係数。 では、n次対称群(置換群)の要素を、(隣接とは限らない)互換で表すときの最大個数は何なのでしょうか。

  • 複数の数値を組み合わせ5000(少しオーバーでもよい)に纏める方法

    複数の数値を組み合わせ5000(少しオーバーでもよい)に纏める方法 ■使用機器 win7 Office2007 エクセル ■教えて戴きたい内容 ◎A列に通し番号(1~65)B列に10~1000台の数値がランダムに記載されています。例えば98  1245  3866・・・・と言うように。 ◎これ等65の内の何れかの複数の数値を組み合わせ「幾つかの5000」の数値に纏めたい。 5000ぴったりであれば理想的であるが、500を少しオーバーしても良い。(計算できる最小オーバー数値でよい)  ◎このような事ができる方法をご存知の方のご教示を御願いたします。 ■コメント ◎私の現状の力量はごく簡単な関数(SUM関数程度)です。あまり高等な関数は理解しかねます。比較的簡単な操作方法があれば幸甚です。

  • C♯の配列について

    C♯でプログラムを作っているのですが、配列の要素数の最大値と最小値の求め方がわかりません。配列の値の最大値の求め方は調べれば出てくるのですが、要素数の最大値等は調べてもわかりませんでした。 例えば下記のような配列があった場合 int[,,] a =new int[100,100,100] a[2,3,6]=1 a[4,5,9]=1 a[13,46,79]=1 a[8,15,45]=1 a[1,33,68]=1 それぞれの要素数の最小値1、3、6、最大値13、46、79は どのようにプログラムで求めればいいのでしょうか? よろしくお願いします。

  • 数値データの規格化

    数値データの規格化の方法を教えてください 最大値がXmax,最小値がXminであるN個のデータ群 Xn(n=1,2,3・・・)があります. このデータを最大値がA,最小値がBとなるように規格化したいです. 元のデータ群Xnで最大値を1,最小値を0となるような規格化は分かるのですが, そこから,最大値をA,最小値をBとなるように変換する方法が分かりません. よろしくお願い致します.

  • 算数パズルを教えてください

    算数パズルを教えてください 子供(小4)が持って帰ってきた問題です。 さっぱりわからないので解き方も含めて教えてください。 以下問題です・・・ 3×3=9マスの枠内に以下の27個の数字のうち、重複しない9つを入れ その9マスの各列(タテ、ヨコ、ナナメ)の合計を出し それぞれの合計のうち、最大値と最小値の差がもっとも小さくなる 組み合わせはどれか <数字> 13、16、17、21、25、 28、32、34、39、44、 45、47、53、55、59、 62、64、66、71、75、 79、82、84、87、93、 96、98

  • 複数列の組み合わせがユニークな行数をカウントしたい

    excelで、A列とB列の組み合わせがユニークな行数を数えたいと思っています。 COUNTA(UNIQUE(A2:B200))/2-1 複数列指定した時に返ってくるのが配列で、COUNTIFそのままだと A列とB列の数が合算されて返るようなので2で割り、頭になぜか 0、0がついているので-1として、とりあえずそれらしき値は出せたの ですが、もっとうまいやり方があれば教えていただけないでしょうか。

  • 要素数を最大化する組合せ最適化問題について

    要素数を最大化するような組合せ最適化問題(線形計画問題)を解こうとしていますが、 要素数を一度に計算したり、ソートしたりすることはできません。 (そのような関数を線形式で定義するのは難しいと思いますので。) 一方、2つの要素が同一かどうかのみの計算は簡単にできるので、 全ての2要素間で一致/不一致を判断し、不一致の数が最大となるように目的関数を設定して、この問題を解こうかと思っています。 この方法は正しいでしょうか。 例えば、 ある変数の組合せにおいて、 A1=X, A2=Y, A3=X といった結果になるとすると、 bin1=|A1-A2|(=1、不一致) bin2=|A1-A3|(=0、一致) bin3=|A2-A3|(=1、不一致) という風に要素の一致/不一致を計算し、 不一致数(=bin1+bin2+bin3)を最大化するという風に目的関数を定義しようかと思っています。 当然、不一致数と要素数は一致しないわけですが、 不一致数が最大のときに、必ず要素数が最大となるのであれば、 目的関数として問題ないと考えています。 なんとなく正しい気もしますが、本当に正しいのかよくわかりません。 よろしくお願いします。