p-メディアン問題のプログラミングについて質問です。
例えばa.txtから以下のような数字を読み込んだ場合、
<a.txt>
4//ノード数 2.0 0.5 1.0 3.0//重み /*距離行列*/ 0.0 4.0 5.0 7.0 4.0 0.0 3.0 3.0 5.0 3.0 0.0 4.0 7.0 3.0 4.0 0.0
行列を計算すると(実行すると)
0.000 8.000 10.000 14.000
2.000 0.000 1.500 1.500
5.000 3.000 0.000 4.000
21.000 9.000 12.000 0.000
合計
28.000 20.000 23.500 19.500
最初に重みと距離行列をかけて得られた数字に対して、1メディアンの場合、合計値の中の最小値が答えです。(今回は4列目)
2メディアンの場合は、その四列目は固定して,四列目と各列の値の値を比べ、小さい方の値を足していきます。そして、その中で最小値を取った列が選ばれます。
四列目と一列目の場合0+1.5+4+0=5.5
四列目と二列目の場合8+0+3+0=11
四列目と三列目の場合10+1.5+0+0=11.5
よって、2メディアンの値は1,4列目で5.5です。
このように計算するプログラムを作成したいのですが、どのように実装していいのか全くわかりませんでした。
とりあえず、1メディアン問題のプログラムは出来たので下に示します。
#include <stdio.h> #include <string.h> #include <stdlib.h> #include <math.h> const int FNLEN = 50; const int MAX_N = 100; int main() { FILE *fp; int i, j, nnode; double dem[MAX_N], dist[MAX_N][MAX_N], wdist[MAX_N][MAX_N], wsum[MAX_N]; char file_name[FNLEN]; printf("Data file name: "); scanf("%s", file_name); if ((fp = fopen(file_name,"r")) == NULL){ printf("%s: ファイルをオープンできません!\n", file_name); return -1; } printf("データファイル名: %s\n", file_name); fscanf(fp,"%d",&nnode); if (nnode < 1 || nnode > MAX_N) { printf("ノードの数は1以上%3d以下にしてください!\n", MAX_N); return -1; } for (i = 0; i < nnode; i++){ fscanf(fp, "%lf", &dem[i]); } for (i = 0; i < nnode; i++){ for (j = 0; j < nnode; j++){ fscanf(fp, "%lf", &dist[i][j]); } }fclose(fp); for (i = 0; i < nnode; i++) { // 行 for (j = 0; j < nnode; j++) { // 列 wdist[i][j] = dem[i] * dist[i][j]; printf("%.3f ", wdist[i][j]);//出力 }printf("\n"); }puts("------------------\n合計"); for (j = 0; j < nnode; j++) { wsum[j] = 0.0; for (i = 0; i < nnode; i++) { wsum[j] += wdist[i][j]; } printf("%.3f ", wsum[j]); } int min_index = 0; for (j = 1; j < nnode; j++) { if (wsum[min_index] > wsum[j]) { min_index = j; } } printf("\n\n最小値の列番号は %dで、その合計は %.3f\n", min_index + 1, wsum[min_index]); return 0; }
これをもとに実装したいのですが、どのようにすればいいのかがわからなかったので、もしわかる方がいらっしゃれば教えていただけるととても助かります。
気になる質問をクリップする
クリップした質問は、後からいつでもMYページで確認できます。
またクリップした質問に回答があった際、通知やメールを受け取ることができます。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
回答2件
0
ppaul氏の回答のコメント欄に書いた話
真の最適解は,「2店舗を同時に出店するとしたら,最適なのはどこか?」という話の解なんだけども,
それだと計算量の観点で解を探索するのが非常につらいので,妥協して
・まずは1店舗目を出店する.最適なのはどこか?
・「その後」,2店舗目を出店する.「2店舗目の」最適な場所はどこか
という形で考える,という話なのかな.
当然,この方法では「2店舗同時」の最適解は見つからず,局所解が得られる可能性がある,と.
について言えば…
まず,出店位置を探す際に,既に出店した場所を再度選んでしまわないように
- 各地点に店舗を出したかどうかを覚えておくフラグ F[]
的なのを用意して,
//出店する場所 k の決定 //F[場所]が「出店済」でない場所のなかから選ぶ. for( 場所 ) { if( F[場所]==出店済 )continue; //行列のこの場所に対する列の合計が最小だったらkを更新 if( /*略*/ ){ k = 場所; } } //(ここまでで k = 今回出店する場所 になっている) //フラグ更新 F[ k ] = 出店済;
みたいな感じで出店場所を決定.
で,店舗を出店する場所が決定されたとき,
//出店場所 k が決定された後の行列の更新 for( 行列のすべての列について ) { //既に出店した列の要素は更新しなくてよい if( F[列]==出店済 )continue; //列の行列要素を更新 for( この列のすべての行について ) {//今回選ばれた列の値の方が小さければその値に更新する 行列[行,列] = min( 行列[行,列], 行列[行,k] ); } }
みたくして,次回の出店場所決定処理時に計算される列の合計値が
2メディアンの場合は、その四列目は固定して,四列目と各列の値の値を比べ、小さい方の値を足していきます。
という話になるように行列を更新すればよいのではないでしょうか.
投稿2021/01/20 02:32
編集2021/01/20 02:35総合スコア11996
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
0
ベストアンサー
p-メディアン問題をちょっと調べて見ました。
ノードをA,B,C,D,Eとして
重みが
40.0 35.0 3.0 30.0 45.0
距離が
0.0 2.0 3.0 5.0 6.0
2.0 0.0 4.0 6.0 7.0
3.0 4.0 0.0 2.0 3.0
5.0 6.0 2.0 0.0 2.0
6.0 7.0 3.0 2.0 0.0
の場合を考えると、1メディアンであれば、最適解はCです。
それは積
499.0 587.0 455.0 506.0 554.0
をみれば分かります。
しかし、2メディアンであれば、最適解はAとEになりますね。
これはどうやって求めれば良いのかというアルゴリズムが分からないので、もう少し詳しく書いてもらえませんか。
YATさんが期待するのは以下のプログラムでよろしいですか。
関数分割をしたとき、本来であれば1次元配列に変えて受け渡しをするべきなのですが、そこまでやる気力がなく、外部変数とさせていただきました。変数名や関数名も間に合わせですみません。
また、k=1とk>2を同じアルゴリズムで書くために無限大を使っています。こういう使い方については賛否両論あるだろうとは思います。
C
1#include <stdio.h> 2#include <stdlib.h> 3#include <math.h> 4#define FNLEN (50) 5#define MAX_N (100) 6#define NOTSELECTED (0) 7#define SELECTED (1) 8 9double dem[MAX_N]; //重み 10double dist[MAX_N][MAX_N]; //距離 11double wdist[MAX_N][MAX_N]; //重み付き距離 12double wdistk[MAX_N][MAX_N]; //k番目の重み付き距離 13double wsum[MAX_N]; //重み付き距離合計 14double mindist[MAX_N]; //重みの最小値 15int selected[MAX_N]; //既に選ばれた列 16int nnode; // ノード数 17 18void read_data(void) { 19 FILE *fp; 20 int i, j; 21 char file_name[FNLEN]; 22 23 printf("Data file name: "); scanf("%s", file_name); 24 if ((fp = fopen(file_name,"r")) == NULL){ 25 printf("%s: ファイルをオープンできません!\n", file_name); 26 exit(-1); 27 } 28 29 printf("データファイル名: %s\n", file_name); 30 fscanf(fp,"%d",&nnode); 31 if (nnode < 1 || nnode > MAX_N) { 32 printf("ノードの数は1以上%3d以下にしてください!\n", MAX_N); 33 exit(-1); 34 } 35 36 for (i = 0; i < nnode; i++){ 37 fscanf(fp, "%lf", &dem[i]); 38 } 39 for (i = 0; i < nnode; i++){ 40 for (j = 0; j < nnode; j++){ 41 fscanf(fp, "%lf", &dist[i][j]); 42 } 43 } 44 fclose(fp); 45 return; 46} 47 48void initialize(void) { 49 int i; 50 for (i=0; i<nnode; i++) { 51 mindist[i] = INFINITY; 52 selected[i] = NOTSELECTED; 53 } 54} 55 56void cal_wdist(void) { 57 int i, j; 58 59 for (i = 0; i < nnode; i++) { 60 for (j = 0; j < nnode; j++) { 61 wdist[i][j] = dem[i] * dist[i][j]; 62 } 63 } 64 return; 65} 66 67void print_double(double d) { 68 if (d == INFINITY) { 69 printf(" - "); 70 } 71 else { 72 printf("%5.1f ", d); 73 } 74} 75 76void print_wdistk(int k, int select) { 77 int i, j; 78 79 printf(" "); 80 for (i=0; i < nnode; i++) { 81 printf(" N%03d ", i+1); 82 } 83 printf("\n"); 84 85 for (i = 0; i < nnode; i++) { 86 printf("N%03d ", i+1); 87 for (j = 0; j < nnode; j++) { 88 print_double(wdistk[i][j]); 89 } 90 printf("\n"); 91 } 92 printf("合計 "); 93 for (j = 0; j < nnode; j++) { 94 print_double(wsum[j]); 95 } 96 printf("\n\nk=%dの最小値の列番号は %dで、その合計は ", k, select + 1); 97 print_double(wsum[select]); 98 printf("\n\n\n"); 99 return; 100} 101 102int cal_wdistk(void) { 103 int i, j; 104 int min_index = 0; 105 106 for (j = 0; j < nnode; j++) { 107 for (i = 0; i < nnode; i++) { 108 if (selected[j] == SELECTED) { 109 wdistk[i][j] = INFINITY; 110 } 111 else if (wdist[i][j] < mindist[i]) { 112 wdistk[i][j] = wdist[i][j]; 113 } 114 else { 115 wdistk[i][j] = mindist[i]; 116 } 117 } 118 } 119 for (j = 0; j < nnode; j++) { 120 wsum[j] = 0.0; 121 for (i = 0; i < nnode; i++) { 122 wsum[j] += wdistk[i][j]; 123 } 124 } 125 for (j = 1; j < nnode; j++) { 126 if (wsum[min_index] > wsum[j]) { 127 min_index = j; 128 } 129 } 130 return min_index; 131} 132 133void update(int select) { 134 int j; 135 136 selected[select] = SELECTED; 137 for (j=0; j < nnode; j++) { 138 if (wdist[j][select] < mindist[j]) { 139 mindist[j] = wdist[j][select]; 140 } 141 } 142} 143 144int main() 145{ 146 int k, select; 147 148 read_data(); 149 initialize(); 150 cal_wdist(); 151 152 for (k=1; k < nnode; k++) { 153 select = cal_wdistk(); 154 print_wdistk(k, select); 155 update(select); 156 } 157 158 return 0; 159}
出力例は以下です。
shell
1データファイル名: b.txt 2 N001 N002 N003 N004 N005 3N001 0.0 80.0 120.0 200.0 240.0 4N002 70.0 0.0 140.0 210.0 245.0 5N003 9.0 12.0 0.0 6.0 9.0 6N004 150.0 180.0 60.0 0.0 60.0 7N005 270.0 315.0 135.0 90.0 0.0 8合計 499.0 587.0 455.0 506.0 554.0 9 10k=1の最小値の列番号は 3で、その合計は 455.0 11 12 13 N001 N002 N003 N004 N005 14N001 0.0 80.0 - 120.0 120.0 15N002 70.0 0.0 - 140.0 140.0 16N003 0.0 0.0 - 0.0 0.0 17N004 60.0 60.0 - 0.0 60.0 18N005 135.0 135.0 - 90.0 0.0 19合計 265.0 275.0 - 350.0 320.0 20 21k=2の最小値の列番号は 1で、その合計は 265.0 22 23 24 N001 N002 N003 N004 N005 25N001 - 0.0 - 0.0 0.0 26N002 - 0.0 - 70.0 70.0 27N003 - 0.0 - 0.0 0.0 28N004 - 60.0 - 0.0 60.0 29N005 - 135.0 - 90.0 0.0 30合計 - 195.0 - 160.0 130.0 31 32k=3の最小値の列番号は 5で、その合計は 130.0 33 34 35 N001 N002 N003 N004 N005 36N001 - 0.0 - 0.0 - 37N002 - 0.0 - 70.0 - 38N003 - 0.0 - 0.0 - 39N004 - 60.0 - 0.0 - 40N005 - 0.0 - 0.0 - 41合計 - 60.0 - 70.0 - 42 43k=4の最小値の列番号は 2で、その合計は 60.0
投稿2021/01/16 12:46
編集2021/01/20 09:52総合スコア24670
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2021/01/16 17:08
2021/01/17 08:58
2021/01/17 12:20 編集
2021/01/18 09:40
2021/01/18 11:15
2021/01/20 01:11
2021/01/20 03:54
2021/01/20 14:49 編集
2021/01/23 12:00
2021/01/24 03:18
あなたの回答
tips
太字
斜体
打ち消し線
見出し
引用テキストの挿入
コードの挿入
リンクの挿入
リストの挿入
番号リストの挿入
表の挿入
水平線の挿入
プレビュー
質問の解決につながる回答をしましょう。 サンプルコードなど、より具体的な説明があると質問者の理解の助けになります。 また、読む側のことを考えた、分かりやすい文章を心がけましょう。