これは興味深い問題だと思い、正しいと思われる MiniZinc (非常に高レベルの制約プログラミング システム) で概念実証バージョンをモデル化しました。それが役に立つかどうかはわかりませんし、正直なところ、非常に大きな問題のインスタンスに対して強力かどうかもわかりません.
このモデルによると、最初の問題のインスタンスには 4 つの解決策があります。
B A _
E D C
B A C
----------
B A _
D E C
B A C
----------
A B _
E D C
A B C
----------
A B _
D E C
A B C
2 番目の例は、満足できないと見なされます (そうあるべきです)。
完全なモデルはこちら: http://www.hakank.org/minizinc/ordering_a_list_of_lists.mzn
基本的なアプローチは、行列を使用することです。短い行は null 値 (ここでは 0、ゼロ) で埋められます。問題のインスタンスは、マトリックス「マトリックス」です。結果として得られる解は、行列 "x" (出力で文字列に変換される整数としての決定変数) にあります。次に、「x」の各行が「matrix」の対応する行の順列であることを保証するために使用されるヘルパー マトリックス「perms」があり、述語「permutation3」で実行されます。制約を簡素化するヘルパー配列/セットが他にもいくつかあります。
主な MiniZinc モデル (出力なし) を以下に示します。
モデルを役に立たなくする可能性のあるコメント/仮定を次に示します。
興味深い問題だと思ったので、これは単なる概念実証モデルです。
行列 (問題データ) の行は既にサイズ (下三角) で並べられていると仮定します。これは、制約プログラミングが不要な前処理ステップとして簡単に実行できるはずです。
短いリストは 0 (ゼロ) で満たされているため、行列を操作できます。
MiniZinc は厳密に型指定された言語であり、記号をサポートしていないため、文字 A..E を表す整数 1..5 を定義するだけです。整数の操作は、従来の制約プログラミング システムを使用する場合にも役立ちます。
% MiniZinc モデル (sans 出力)
「globals.mzn」を含めます。
整数: 行 = 3;
整数: 列 = 3;
整数: A = 1;
整数: B = 2;
整数: C = 3;
整数: D = 4;
整数: E = 5;
int: max_int = E;
文字列の配列[0..max_int]: str = array1d(0..max_int, ["_", "A","B","C","D","E"]);
% 問題 A (充足可能)
配列 [1..行、1..列] の int: 行列 =
array2d(1..行、1..列、
[
A,B,0, % この短い配列を「0」で埋める
E、D、C、
A、B、C、
]);
% 有効な値 (0、ゼロをスキップします)
int のセット: 値 = {A,B,C,D,E};
% 特定の値がどの行であるかを識別します。
% 問題 A の例:
% value_rows: [{1, 3}, {1, 3}, 2..3, 2..2, 2..2]
int のセットの配列 [1..max_int]: value_rows =
[ {i | i は 1..rows、j は 1..cols where matrix[i,j] = v} | v in 値];
% 決定変数
% 結果の行列
var 0..max_intの配列[1..行、1..列]: x;
% 行列から x への順列
var 0..max_int の配列 [1..rows、1..cols]: perms;
%
% permutation3(a,p,b)
%
% 順列 p を使用して ab から順列を取得します。
%
predicate permutation3(array[int] of var int: a,
var int: p の array[int]
var int の array[int]: b) =
forall(index_set(a) の i) (
b[i] = a[p[i]]
)
;
解決する;満足する;
制約
forall(i in 1..rows) (
% x と perms の行の値の単一性を保証します (0 を除く)
alldifferent_except_0([x[i,j] | j in 1..cols]) /\
alldifferent_except_0([perms[i,j] | j in 1..cols]) /\
permutation3([matrix[i,j] | j in 1..cols], [perms[i,j] | j in 1..cols], [x[i,j] | j in 1..cols])
)
/\ % x のゼロは、行列にゼロがある場所です
forall(i in 1..rows, j in 1..cols) (
行列[i、j] = 0の場合
x[i,j] = 0
そうしないと
真実
終了
)
/\ % 同じ値が同じ列にあることを確認します:
% - 各値
% - 1 つの列に配置されていることを確認します c
forall(k in 1..max_int where k in values) (
exists(j in 1..cols) (
forall(value_rows[k] の i) (
x[i,j] = k
)
)
)
;
% 出力
% ...