述語の説明が addUnique/2
あれば、制約ロジック プログラミングを使用してソリューションを実装できます。これは初心者向けとはほど遠いですが、とにかく説明を投稿します。
まず、制約論理プログラミングとは何か、および実装の使用方法 (例: SWI-PL clpfd ) を調べることは価値があるかもしれません。基本的に、制約ロジック プログラミング (特に有限領域ソルバー) では、入力リストの変数に対して次の制約を指定できますaddUnique/2
。
- 入力リストの変数を特定の数値 (つまり、0 から指定された値までの整数) にバインドする方法
- 入力リストの異なる変数が同時に同じ値にバインドできない方法 (つまり、!= どこが異なるか)
- 入力リスト内の変数と任意の数値の合計が、指定された値 (つまり、上記の 1 で変数が取り得る最大値) になる方法。
これらの仕様を一緒に使用すると、基礎となる制約ソルバーは、上記の制約が与えられたときに変数が同時に取ることができる許容値を自動的に決定し、ソリューションを提供できます (複数、1 つ、またはまったくない場合もあります)。
SWI-PROLOG で前述の制約ソルバー (clpfd ソルバー) を使用したソリューションを次に示します。
:- use_module(library(clpfd)). % import and use the constraint solver library
addUnique([A|As], Val) :-
unique_vars([A|As], UVs), % determine all unique variables
UVs ins 0..Val, % (1) domain of all unique variables is 0 to Val
pairwise_inequ(UVs), % (2) all unique variables are pairwise !=
construct_sum_constr(As, Val, A, Program), % (3) construct the sum constraint
Program, % assert the sum constraint
label(UVs). % label domains to enumerate a solution (backtracks)
% predicate to return a list of unique vars, if present
unique_vars([], []).
unique_vars([V|Vs], [V|Uniq]) :-
var(V),
\+ var_memberchk(V, Vs), !,
unique_vars(Vs, Uniq).
unique_vars([_|Vs], Uniq) :-
unique_vars(Vs, Uniq).
% predicate to test if a variable appears in a list (possibly including variables)
var_memberchk(V0, [V1|_]) :-
V0 == V1, !.
var_memberchk(V0, [_|V1s]) :-
var_memberchk(V0, V1s).
% create constraints that assert each in the input list != to each other
pairwise_inequ([]).
pairwise_inequ([V|Vs]) :-
map_inequ(Vs, V),
pairwise_inequ(Vs).
% predicate to pairwise assert inequality between all list members
map_inequ([], _).
map_inequ([V1|V1s], V0) :-
V0 #\= V1, % the inequality constraint
map_inequ(V1s, V0).
% predicate to construct a summation constraint, whereby all variables in the
% input list are constructed into a sum with +/2 and finally equated to a value
construct_sum_constr([], Val, Sum, (Sum #= Val)).
construct_sum_constr([V|Vs], Val, Sum, Program) :-
construct_sum_constr(Vs, Val, (V + Sum), Program).
たとえば、このコードを実行すると、次のようになります。
?- addUnique([A,B,B], 6).
A = 0,
B = 3 ;
A = 4,
B = 1 ;
A = 6,
B = 0.
;
変数間の許容されるバインディングの次のソリューションを列挙します。必要に応じて と が同じ値になることはA
ありませんが、入力リスト内のすべての出現箇所の合計は常に になることに注意してください。別のクエリ:B
6
?- addUnique([A,A,A],4).
false.
A
ここでは、合計すると totaledにバインドする単一の整数が見つからなかったため、結果は失敗です4
。
?- addUnique([A,A,A,A],4).
A = 1.
...予想通り。また、あなたは試したかった:
?- addUnique([A,B,C,D],4).
false.
A
繰り返しますが、すべての変数、B
、C
およびD
がすべて異なるとアサートされ、すべてを にバインドできないため、ここでも結果は失敗です1
。
編集ps。また、試してみたかった:
?- addUnique([A,A,A,1],4).
A = 1.
上記のコードを簡単に変更すると、ドメインをアサートするために呼び出すときに変数のみが使用されるようins
になります (入力リスト内の数値は使用されません)。