平面分割の列挙アルゴリズム
10月末に綾@ (@ayaHSYKZ)さんが,以下のような問題をツイートしました.
問題、深夜にひっそりあげてみる pic.twitter.com/xE8UcrztR3
— 綾@ (@ayaHSYKZ) 2018年10月27日
消えたときのために文章でも書いておくと,
以下の二条件を満たす行列 の個数を求めよという問題です.
- の各要素はかかである.
- 全ての整数に対して かつ が成立.つまりは各行各列について単調非増加である.
この問題は実は,「平面分割 (plane partition)」という名前でより一般に研究されています.
Plane partition - Wikipedia
今回は,一般に行列サイズがで,各要素がからまでの整数だとしたとき,上記の条件を満たす行列を列挙するアルゴリズムを設計し,c++で実装しました.
詳細は書くのがめんどくさいので省きますが,コードを載せておきます.
ちなみに,このアルゴリズムを使って求めた結果,綾@さんの問題の答えは「通り」でした.
#include <iostream> #include <vector> #include <utility> #define MIN(x,y) ((x<y)? x: y) using namespace std; void vector_vanish(vector<int> &vec); void vector_inspect(vector<int> vec); int vector_increment(vector<int> sup, vector<int> &data); int level_increment(pair< vector<int>, vector<int> > sup, pair< vector<int>, vector<int> > &data); int PP_increment(vector< pair< vector<int>, vector<int> > > &pp); void PP_inspect(vector< pair< vector<int>, vector<int> > > pp); int main(){ const int r=5; const int c=5; //r<=cを仮定する. const int n=2; vector< pair< vector<int>,vector<int> > > pp; for(int i=r-1; i>=0; i--){ vector<int> hor(c-i,0); vector<int> var(r-1-i,0); pair< vector<int>,vector<int> > adj(hor, var); pp.push_back(adj); } //平面分割を入れる箱を準備 vector<int> hor(c+1,n); vector<int> var(r,n); pair< vector<int>, vector<int> > adj(hor, var); pp.push_back(adj); //平面分割の高さの上限を設定 int count=0; do{ PP_inspect(pp); count++; cout << endl; }while(PP_increment(pp)); cout << "count = " << count << endl; double prod = 1; for(int i=0; i<=r-1; i++){ for(int j=0; j<=c-1; j++){ for(int k=0; k<=n-1; k++){ prod *= ((double)(i+j+k+2))/((double)(i+j+k+1)); } } } cout << "prod = " << prod << endl; return 0; } void PP_inspect(vector< pair< vector<int>, vector<int> > > pp){ int r = pp.size()-1; int c = pp[r-1].first.size(); vector< vector<int> > kekka; for(int i=0; i<=r-1; i++){ vector<int> a(c,0); kekka.push_back(a); } for(int i=0; i<=r-1; i++){ for(int j=0; j<=c-1; j++){ if(i<=j){ kekka[i][j] = (pp[r-1-i].first)[j-i]; }else{ kekka[i][j] = (pp[r-1-j].second)[i-j-1]; } } } for(int i=0; i<=r-1; i++){ for(int j=0; j<=c-1; j++){ cout << kekka[i][j] << " "; } cout << endl; } //平面分割になってるかチェックする for(int i=0; i<=r-2; i++){ for(int j=0; j<=c-2; j++){ if(kekka[i][j] < kekka[i+1][j] || kekka[i][j] < kekka[i][j+1]){ cout << "Error: 平面分割になってません." << i << j << endl; exit(1); } } } return ; } int PP_increment(vector< pair< vector<int>, vector<int> > > &pp){ int r = pp.size()-1; for(int i=0; i<=r-1; i++){ if(level_increment(pp[i+1],pp[i])){ //レベルiがインクリメントできたとき //レベルiより前を全部消す for(int j=0; j<=i-1; j++){ vector_vanish(pp[j].first); vector_vanish(pp[j].second); } //mainに戻る return 1; } } //これ以上インクリメントできないとき return 0; } int level_increment(pair< vector<int>, vector<int> > sup, pair< vector<int>, vector<int> > &data){ //verがインクリメントできるならverをインクリメント //horをインクリメントできるならhorをインクリメントしvarを消す //インクリメントできたなら1を返す //まず,関数vector_incrementに突っ込むためのベクトルを作る //verについて int ver_size = data.second.size(); vector<int> vec_ver_sup(ver_size); for(int i=0; i<=ver_size-1; i++){ vec_ver_sup[i] = MIN((sup.second)[i+1], (data.first)[0]); } //horについて int hor_size = data.first.size(); vector<int> vec_hor_sup(hor_size); for(int i=0; i<=hor_size-1; i++){ vec_hor_sup[i] = MIN((sup.first)[i+1], (sup.second)[0]); } //どちらもインクリメントできないなら0を返す if(vec_hor_sup == data.first && vec_ver_sup == data.second){ return 0; } //varをインクリメント if(vector_increment(vec_ver_sup, data.second)){ return 1; } //varがインクリメントできなかったとき //horをインクリメント vector_vanish(data.second); if(vector_increment(vec_hor_sup, data.first)){ return 1; } //ここまで達するのはおかしい cout << "Error" << endl; exit(1); } int vector_increment(vector<int> sup, vector<int> &data){ if(sup == data){ return 0; } int r = sup.size(); for(int j=r-1; 1<=j; j--){ if(data[j-1] > data[j]){ if(sup[j] > data[j]){ data[j]++; for(int i=r-1; j<i; i--){ data[i] = 0; } return 1; } } } if(sup[0] > data[0]){ data[0]++; for(int i=r-1; 0<i; i--){ data[i] = 0; } return 1; }else{ cout << "Error" << endl; exit(1); } } void vector_vanish(vector<int> &vec){ int size = vec.size(); for(int i=0; i<=size-1; i++){ vec[i] = 0; } return; } void vector_inspect(vector<int> vec){ int size = vec.size(); for(int i=0; i<=size-1; i++){ cout << vec[i] << " "; } cout << endl; return; }