• ベストアンサー
※ 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)

  • bunjii
  • ベストアンサー率43% (3589/8248)
回答No.21

>参考にさせて頂きます。 言葉のキャッチボールが上手くできていないようです。 元データの数列(要素数が不定の整数値配列)を何処から持ってくるのかを補足して頂くこととグループの数(与えられた個数の群)をどのように定義するかを補足頂けないと一般化の可否が考え難いと言ったつもりです。 回答No.11のお礼にある下記の関数のコードが今回の質問の趣旨のように推測しました。 void GetOptimalCombination(int* pSrc, int nSize, int nBlock, int** ppResult); 従って、元データの数列とグループ数の組み合わせを数例提示して頂けるよう申し上げました。 別解(C言語以外での処理)の方法で対処することになったのであれば質問を締め切ってください。

全文を見る
すると、全ての回答が全文表示されます。
  • bunjii
  • ベストアンサー率43% (3589/8248)
回答No.20

>任意のグループ数に対応出来ますか? 要素数とグループの数によってはループの多重化に限界があると思います。 グループ数が4以上になるとパターン数の計算にも工夫が増えますので専門のプログラマーに委託された方が良いでしょう。 パターン毎のグループ合計を配列変数に保存してすべての計算が済んでから組み合わせの判定をすれば良いでしょう。 >再帰を使うようなイメージだったのですが。 私はプログラマーではありませんので詳しいことは分かりませんが再帰を多重化したときにループから抜け出せなくなることもあるようですから方法論を良く検討してください。 >I/Fのイメージとしてはこんな感じです。 前述のようにプログラマーではないので提示の関数(サブルーチン)については理解できません。 何れにしてもmain()から関数へ条件を渡して戻り値で要素の配列とグループ数を得ると解釈します。 要素の配列とグループ数の模擬データを数組提示して頂ければ暇を見ながら考えたいと思います。(解決は難しいかも知れません)

katorea21
質問者

お礼

ご回答ありがとうございました。参考にさせて頂きます。

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

>回答No.18 amanojaku1 ただし、デメリットもあります。 x64で「int_fast32_t」と定義した場合(8 バイト:64 ビット)と、386(32bit)で「int_fast32_t」と定義した場合(4 バイト:32 ビット)では、ザイズが変わってしまいます。 4 バイト (32 ビット)で符号付き整数値の範囲は「-2,147,483,648~2,147,483,647」 8 バイト (64 ビット)で符号付き整数値の範囲は「-9,223,372,036,854,775,808~9,223,372,036,854,775,807」 例えば、ビット演算でNOTを使ったりすると明確に違う値になります(当然、それ以外でも明確に違う値になる場合はあるでしょう)。 (「x64、386(32bit)」の両対応にしたい場合で)これが問題になる場合は基本的に「int_fast~_t」は使わないほうが良いでしょうが、どうしても必要な場合はプログラマーが それに対応できるプログラムを作る必要があります。

katorea21
質問者

お礼

ご回答ありがとうございました。参考にさせて頂きます。

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

>回答No.17 amanojaku1 x64で「int_fast32_t」と定義しても、32bit演算にはならないと言う事に注意して下さい。 32bit以上のbit数が保障され、そのCPUの最速のbit数になります(x64なら64bit)。

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

>回答No.16 amanojaku1 組み込みにおけるCプログラムの速度最適化 https://qiita.com/YankeeDeltaBravo225/items/274b92735fbafc060a75 >32bit CPUを使う前提で書きます。 >基本的に32bit CPUは32bit演算しかできません。 >(64bit演算もできるものはあったりしますが) >では8bit,16bit変数の演算はどうなるかと言うと、 >32bitで演算した結果に対して0xFF (8bit) / 0xFFFF (16bit)の >ビットマスクを掛けるので、その分遅くなります。 ↑これは32bit CPUの話ですが、恐らく64bit CPUでも「32bit、16bit、8bit」演算ではマスク処理が入り遅くなると予想されます。

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

>回答No.15 amanojaku1 x64だけで良いなら「int_fast64_t」で定義すれば良いですし、i386(32bit)も視野に入れたいなら「int_fast32_t」で定義しておけばi386(32bit)でコンパイルした時に極端に遅くなったりはしないと思われます(ただしi386(32bit)はレジスターが少ないのでレジスターによる最適化の恩恵は あんまり受けられないと思われますが…)

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

>回答No.14 amanojaku1 最も速い数値型 http://cpp.aquariuscode.com/int_fast_t >今やどこを見ても64bitCPUで溢れています。 >にも関わらず、多くの環境でintは4byteのままです。 >今の時代で引数に盲目的にintを指定することは、環境の変化に対応しきれていない可能性があります。 >幸いにも、どの環境でもなるべく高効率になる数値型を標準が用意してくれています。 >fastの後ろの数値が最低限保証するbit精度です。 >例えばuint_fast8_tは「最低でも8bitの精度が保証された中で最速のデータ幅」を持つ符号無し整数を表します。

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

>>ただJavaScriptだと群の数が増えると急激に重くなりますね。Cに移植すれば高速になるでしょうか。 >コンパイラーなら問題ないと思いますよ。 x64の方がレジスターが多いので、x64で(最適化を有効にし)コンパイルすると良いらしいです。 コンパイラの最適化についてすべてのプログラマが知っておくべきこと (第 2 部) https://msdn.microsoft.com/ja-jp/magazine/dn973015.aspx >レジスタ割り当て >レジスタ割り当てとは、変数用にメモリを確保する必要をなくし、使用可能なレジスタに一連の変数を割り当てるプロセスです。このプロセスは通常、1 つの関数全体のレベルで実行されます。ただし、リンク時のコード生成 (/LTCG) が有効になっている場合は特に、複数の関数にまたがってこのプロセスを実行して、さらに効率の高い割り当てが可能になる場合があります (ここでは、特に記載がない限り、変数はすべて自動変数、つまり構文上有効期間が決まる変数です)。 > >レジスタ割り当ては、特に重要な最適化です。これを理解するために、さまざまなレベルのメモリへのアクセスにかかる時間を確認します。レジスタへのアクセスにかかる時間は 1 プロセッサ サイクル未満です。キャッシュへのアクセスは少し時間がかかり、数サイクル~数十サイクルです。(リモート) DRAM メモリへのアクセスはそれよりもずっと時間がかかります。さらに、ハード ドライブへのアクセスは非常に遅く、数百万サイクルかかります。また、メモリ アクセスによって共有キャッシュやメイン メモリとのトラフィックが増加します。レジスタ割り当てを行い、使用可能なレジスタをできる限り活用することで、メモリへのアクセス回数が減少します。 > >コンパイラは各変数のレジスタへの割り当てを試みます。その変数が関係するすべての命令が実行されるまで、変数がレジスタに割り当たったままの状態が理想です。後ほど簡単に説明する理由からよく起こることですが、割り当て状態を維持できないと、1 つ以上の変数がメモリに吐き出され、読み込みと書き込みが頻繁に行われることになります。レジスタ負荷とは、レジスタの使用を維持できないために変数がメモリに吐き出されたレジスタの数を表します。レジスタ負荷が大きいほどメモリのアクセス回数が多くなり、メモリのアクセス回数が増えるとプログラム自体が遅くなるだけでなく、システム全体の処理速度が低下する可能性があります。 > >最新の x86 プロセッサには、コンパイラが割り当てることができるレジスタとして、8 個の 32 ビット汎用レジスタ、8 個の 80 ビット浮動小数点レジスタ、および 8 個の 128 ビット ベクター レジスタがあります。すべての x64 プロセッサに、16 個の 64 ビット汎用レジスタ、8 個の 80 ビット浮動小数点レジスタ、および最低 16 個の (128 ビット幅以上の) ベクター レジスタがあります。最新の 32 ビット ARM プロセッサには、15 個の 32 ビット汎用レジスタと 32 個の 64 ビット浮動小数点レジスタがあります。すべての 64 ビット ARM プロセッサには、31 個の 64 ビット汎用レジスタ、32 個の 128 ビット浮動小数点レジスタ、および 16 個の 128 ビット ベクター レジスタ (NEON) があります。これらのレジスタはすべて、レジスタ割り当てに使用できます (また、グラフィック カードに用意されているレジスタもこの一覧に加えることができます)。使用可能なレジスタのどれにもローカル変数を割り当てられない場合、その変数はスタック上に割り当てられます。

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

>ただJavaScriptだと群の数が増えると急激に重くなりますね。Cに移植すれば高速になるでしょうか。 コンパイラーなら問題ないと思いますよ。

katorea21
質問者

お礼

C++に移植したところ、性能はかなり改善しました。実用的には十分です。 ところで、提示頂いたプログラムですが、簡単なコメントを入れて頂くことは出来ますでしょうか?各変数の意味、ブロック単位の処理内容、while文を抜ける条件などが書いてあれば処理の流れを理解出来ると思います。

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

>回答No.10 amanojaku1 ほんのチョッピリ(処理的に)スマートになりました。 デバッグ用の表示も分かりやすく変更してます。 >群の数をNに一般化出来るでしょうか? 「gp」に群の数を代入して下さい。 下記はJavaScriptです(「gp = 4」にしています) <head> <meta http-equiv="Content-Type" content="text/html; charset=Shift-JIS"> <!-- charset=Shift-JIS、UTF-8 --> <TITLE>test</TITLE> </head> <body> <script type="text/javascript"> <!-- gpa = [5,2,7,12,6,15,4]; gp = 4; gmq = Array(gp).fill(1); gmp = Array(gp).fill(0); gmmin = -1; gmmax = -1; gmmdif = -1; gtmin = -1; gtmax = -1; gtmdif = -1; gppc = 0; // gtmp, gtmq, gppc while(0<gmq[gp-1]){ gppc++; // gmq[2] = gpa.length-(gmq[0]+gmq[1]); gmq[gp-1] = gpa.length p = 0; for(let i = 0; i<(gmq.length-1); i++){ gmq[gp-1] -= gmq[i]; p += gmq[i]; gmp[i+1] = p; } gmt = Array(gp).fill(0); gmc = 0; pc = 1; for(let i = 0; i<gpa.length; i++){ if(pc<gmp.length){ if(gmp[pc]==i){ pc++; document.write(' | '); gmc++; } } gmt[gmc] = gmt[gmc]+gpa[i]; document.write(gpa[i]+','); } document.write('<br>'); document.write('gmt='+gmt+'<br>'); gmm = gmt.slice().sort((a, b) => a-b); gmmin = gmm[0]; gmmax = gmm[gmm.length-1]; gmmdif = gmmax-gmmin; document.write( 'gmmin='+gmmin+'; '+ 'gmmax='+gmmax+'; '+ 'gmmdif='+gmmdif+'; '+ '<br>'); if((gtmdif<0)||(gtmdif>gmmdif)){ gtmdif = gmmdif; gtmin = gmmin; gtmax = gmmax; gtmq = gmq.slice(); gtmp = gmp.slice(); } document.write( 'gtmin='+gtmin+'; '+ 'gtmax='+gtmax+'; '+ 'gtmdif='+gtmdif+'; '+ '<br>'); document.write('<br>'); for(let k = 0; k<(gmq.length-1); k++){ gmq[k]++; for(let i = 0; i<k; i++){ gmq[i] = 1; } gmq[gp-1] = gpa.length document.write( 'k='+k+'; '+ 'gmq[k]='+gmq[k]+'; '+ '<br>'); p = 0; for(let i = 0; i<(gmq.length-1); i++){ gmq[gp-1] -= gmq[i]; p += gmq[i]; gmp[i+1] = p; } document.write('gmq='+gmq+'<br>'); if(0<gmq[gp-1]){ break; } } document.write('<br>'); } document.write('<br>'); document.write("<< Answer >>"+'<br>'); gppc document.write('PatternCounter='+gppc+'<br>'); gmp = gtmp; gmt = Array(gp).fill(0); gmc = 0; for(let i = 0; i<gpa.length; i++){ for(let j = 1; j<gmp.length; j++){ if(gmp[j]==i){ document.write(' | '); gmc++; } } gmt[gmc] = gmt[gmc]+gpa[i]; document.write(gpa[i]+','); } document.write('<br>'); document.write('gmt='+gmt+'<br>'); document.write( 'gtmin='+gtmin+'; '+ 'gtmax='+gtmax+'; '+ 'gtmdif='+gtmdif+'; '+ '<br>'); // --> </script> </body> </html>

katorea21
質問者

お礼

素晴らしいです。意外とシンプルに書けるものなんですね。 ただJavaScriptだと群の数が増えると急激に重くなりますね。Cに移植すれば高速になるでしょうか。

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

関連する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)を最大化するという風に目的関数を定義しようかと思っています。 当然、不一致数と要素数は一致しないわけですが、 不一致数が最大のときに、必ず要素数が最大となるのであれば、 目的関数として問題ないと考えています。 なんとなく正しい気もしますが、本当に正しいのかよくわかりません。 よろしくお願いします。