2次元配列から線形計画法の制約式を作る方法
Posted: 2008年12月22日(月) 15:26
いつもお世話になっています。
今、グループスタイナーという問題のアルゴリズムを実装していまして、グラフの隣接行列に基づいた線形計画法の制約式を作成するC言語のプログラムを考えているのですが、行き詰ってしまいました。
まず、頂点数Cのグラフの隣接行列を与えます。
#define C 6
int c[C+1][C+1]={{0,0,0,0,0,0,0},
{0,0,1,M,1,M,1},
{0,1,0,1,M,1,M},
{0,M,1,0,1,M,1},
{0,1,M,1,0,1,0},
{0,M,1,M,1,0,1},
{0,1,M,1,M,1,0}};
例えば、c[1][3]が1というのは、頂点①と頂点③が繋がっている、という意味で、0とMは繋がっていない、
という意味です。
加えて、頂点の分割の情報
V0={1,2}
V1={3,4}
V2={5,6}
を与えます。これは頂点を①②のグループと③④のグループと⑤⑥のグループに分けるという意味です。
またこのグループに依存した変数
H(V0)=4
H(V1)=3
H(V2)=2
を与えます。
このとき、H(Vk)が一番大きいグループに属する頂点r(この場合では①と②)に関して、それぞれ制約式を作ります。
制約式は、一般化すると、
Σ{(Vkに属する頂点以外+頂点r)の頂点集合に"出入り"する辺}>=H(Vk)
(この場合、k=1or2※0は除く)と書けます。"(Vkに属する頂点以外+頂点r)の頂点集合"は様々なパターンがあるので、場合分けをしなければなりません。
具体的にはrが①の場合は、
(k=1)(①) x1_2+x1_4+x1_6>=3;
(①②) x1_6+x1_4+x2_5+x2_3>=3;
(①⑤) x1_2+x1_4+x1_6+x2_5+x4_5+x5_6>=3;
(①⑥) x1_2+x1_4+x3_6+x5_6>=3;
(①②⑤) x1_4+x1_6+x2_3+x4_5+x5_6>=3;
(①②⑥) x1_4+x2_3+x2_5+x3_6+x5_6>=3;
(①⑤⑥) x1_2+x1_4+x3_6+x2_5+x4_5>=3;
(①②⑤⑥)x1_4+x2_3+x3_6+x4_5>=3;
(k=2)(②) x1_2+x1_4+x1_6>=2;
(②①) x1_6+x1_4+x2_5+x2_3>=2;
(②③) x1_2+x1_4+x1_6+x2_3+x3_6+x3_4>=2;
(②④) x1_2+x1_6+x3_4+x4_5>=2;
(②①③) x1_6+x1_4+x2_5+x3_4+x3_6>=2;
(②①④) x1_6+x2_3+x2_5+x3_4+x4_5>=2;
(②③④) x1_2+x1_6+x2_3+x3_6+x4_5>=2;
(①②③④)x1_6+x2_5+x3_6+x4_5>=2;
となります。要は、この右側の部分が書き出せればいいのですが、どのように情報をインプットさせて、どのようにループさせていけばいいのか全くわかりません。自分でやってみたのは、
k=1;
for(i=1;i<=C;i++){
if(i==r){
for(j=1;j<=C;j++){
if(c[j]>0&&c[j]!=M){
if(i<j)
printf("+x%d_%d",i,j);
if(i>j)
printf("+x%d_%d",j,i);
}
}
printf(">=%d;\n",HVk[k]);
}
}
for(i=1;i<=C;i++){
if(i!=r&&i!=3&&i!=4){
for(j=1;j<=C;j++){
if(c[[/url][j]>0&&c[[/url][j]!=M&&i!=j){
if(r<j)
printf("+x%d_%d",r,j);
if(r>j)
printf("+x%d_%d",j,r);
}
if(c[j]>0&&c[j]!=M&&r!=j){
if(i<j)
printf("+x%d_%d",i,j);
if(i>j)
printf("+x%d_%d",j,i);
}
}
printf(">=%d;\n",HVk[k]);
}
}
のようなほぼ全列挙のような形なのですが、これだと頂点数が増えたときに場合分けが多すぎてどうしようもなくなってしまうと思います。
何かうまい方法はないでしょうか?ちなみに環境はVista/VisualC++2008です。
つたない説明で意図が伝わるかどうか怪しいですが・・・何卒よろしくお願いします。
今、グループスタイナーという問題のアルゴリズムを実装していまして、グラフの隣接行列に基づいた線形計画法の制約式を作成するC言語のプログラムを考えているのですが、行き詰ってしまいました。
まず、頂点数Cのグラフの隣接行列を与えます。
#define C 6
int c[C+1][C+1]={{0,0,0,0,0,0,0},
{0,0,1,M,1,M,1},
{0,1,0,1,M,1,M},
{0,M,1,0,1,M,1},
{0,1,M,1,0,1,0},
{0,M,1,M,1,0,1},
{0,1,M,1,M,1,0}};
例えば、c[1][3]が1というのは、頂点①と頂点③が繋がっている、という意味で、0とMは繋がっていない、
という意味です。
加えて、頂点の分割の情報
V0={1,2}
V1={3,4}
V2={5,6}
を与えます。これは頂点を①②のグループと③④のグループと⑤⑥のグループに分けるという意味です。
またこのグループに依存した変数
H(V0)=4
H(V1)=3
H(V2)=2
を与えます。
このとき、H(Vk)が一番大きいグループに属する頂点r(この場合では①と②)に関して、それぞれ制約式を作ります。
制約式は、一般化すると、
Σ{(Vkに属する頂点以外+頂点r)の頂点集合に"出入り"する辺}>=H(Vk)
(この場合、k=1or2※0は除く)と書けます。"(Vkに属する頂点以外+頂点r)の頂点集合"は様々なパターンがあるので、場合分けをしなければなりません。
具体的にはrが①の場合は、
(k=1)(①) x1_2+x1_4+x1_6>=3;
(①②) x1_6+x1_4+x2_5+x2_3>=3;
(①⑤) x1_2+x1_4+x1_6+x2_5+x4_5+x5_6>=3;
(①⑥) x1_2+x1_4+x3_6+x5_6>=3;
(①②⑤) x1_4+x1_6+x2_3+x4_5+x5_6>=3;
(①②⑥) x1_4+x2_3+x2_5+x3_6+x5_6>=3;
(①⑤⑥) x1_2+x1_4+x3_6+x2_5+x4_5>=3;
(①②⑤⑥)x1_4+x2_3+x3_6+x4_5>=3;
(k=2)(②) x1_2+x1_4+x1_6>=2;
(②①) x1_6+x1_4+x2_5+x2_3>=2;
(②③) x1_2+x1_4+x1_6+x2_3+x3_6+x3_4>=2;
(②④) x1_2+x1_6+x3_4+x4_5>=2;
(②①③) x1_6+x1_4+x2_5+x3_4+x3_6>=2;
(②①④) x1_6+x2_3+x2_5+x3_4+x4_5>=2;
(②③④) x1_2+x1_6+x2_3+x3_6+x4_5>=2;
(①②③④)x1_6+x2_5+x3_6+x4_5>=2;
となります。要は、この右側の部分が書き出せればいいのですが、どのように情報をインプットさせて、どのようにループさせていけばいいのか全くわかりません。自分でやってみたのは、
k=1;
for(i=1;i<=C;i++){
if(i==r){
for(j=1;j<=C;j++){
if(c[j]>0&&c[j]!=M){
if(i<j)
printf("+x%d_%d",i,j);
if(i>j)
printf("+x%d_%d",j,i);
}
}
printf(">=%d;\n",HVk[k]);
}
}
for(i=1;i<=C;i++){
if(i!=r&&i!=3&&i!=4){
for(j=1;j<=C;j++){
if(c[[/url][j]>0&&c[[/url][j]!=M&&i!=j){
if(r<j)
printf("+x%d_%d",r,j);
if(r>j)
printf("+x%d_%d",j,r);
}
if(c[j]>0&&c[j]!=M&&r!=j){
if(i<j)
printf("+x%d_%d",i,j);
if(i>j)
printf("+x%d_%d",j,i);
}
}
printf(">=%d;\n",HVk[k]);
}
}
のようなほぼ全列挙のような形なのですが、これだと頂点数が増えたときに場合分けが多すぎてどうしようもなくなってしまうと思います。
何かうまい方法はないでしょうか?ちなみに環境はVista/VisualC++2008です。
つたない説明で意図が伝わるかどうか怪しいですが・・・何卒よろしくお願いします。