Prolog 搜索从列表中减去 2 个元素的可能组合

Prolog search for possible combination for subtracting 2 elements from a list

这是此页面的扩展问题。

例如,对于列表 X = [1,2,3],我可以像下面这样减去:

第1/2个元素减1,X变成[1-1,2-1,3]=[0,1,3]=[1,3]

从第 1 个/第 3 个元素中减去 1:X 变为 [2, 2]

从第 2 个/第 3 个元素中减去 1:X 变为 [1, 1, 2]

从第 2 个/第 3 个元素中减去 2:X 变为 [1, 1]

因此,它总是减去 2 个元素,并且具有相同的数字,但始终是实数。有人对此有任何想法吗?

这个看起来更好:

subt_comb(X, Y).
X = [1,2,3]
Y = [1,3]
Y = [2,2]
Y = [1,1,2]
Y = [1,1]

看了 lurker 和 gusbro 的解决方案后,我创建了这样的东西。

remove2([HX|T], S2):-
    between(1, HX, Y),
    remove2__([HX|T], Y, S2).
remove2([HX|T], [HX|TY]):-
    remove2(T, TY).

% remove2__(S1, Y, S2), this procedure is to execute 0 after
% subtraction. Y is generated from remove2(S1, S2).
remove2__([Y|T], Y, TY):- remove2_(Y, T, Y, TY).
remove2__([HX|T], Y, [HY|TY]):-
    HX>Y,
    HY is HX - Y,
    remove2_(HX, T, Y, TY).


% remove2_(HX, L, Y, S2).
% HX is the first element from the origin list. L is the tail of the
% origin list. Y is the number to subtract. S2 is the result.
remove2_(_, [H|T], H, T).
remove2_(_, [H|T], Y, [HY|T]):-   %for list with descending order
    HY is H - Y, HY >0.
remove2_(HX, [H|T], Y, [H|TY]):-   %going for another element.
    remove2_(HX, T, Y, TY).


?- remove2([3,2,1],X).
X = [2, 1, 1] ;
X = [2, 2] ;
X = [1, 1] ;
X = [3, 1] ;
false.

?- remove2([1,2,3],X).
X = [1, 3] ;
X = [2, 2] ;
X = [1, 1, 2] ;
X = [1, 1] ;
false.

我会使用 2 个程序。

第一个从列表中取出一个项目,计算出一个等于或小于它的数字,然后调用第二个过程从剩余列表中的另一个项目中减去该数字。

你必须处理边界情况(如果计算的数量等于项目,则完全删除项目),因此我对这些情况使用不同的子句。

subt_comb([N|Tail], [NX|NTail]):-
  succ(N1, N),
  between(1, N1, NX),
  subt_comb1(Tail, NX, NTail).
subt_comb([N|Tail], NTail):-
  subt_comb1(Tail, N, NTail).
subt_comb([N|Tail], [N|NTail]):-
  subt_comb(Tail, NTail).

subt_comb1([M|Tail], N, [NM|Tail]):-
  M > N,
  NM is M - N.
subt_comb1([N|Tail], N, Tail).
subt_comb1([M|Tail], N, [M|NTail]):-
  subt_comb1(Tail, N, NTail).

样本:

?- subt_comb([1,2,3], Y).
Y = [1, 3] ;
Y = [2, 2] ;
Y = [1, 1, 2] ;
Y = [1, 1] ;
false.

这是使用更基本操作的替代解决方案。与@gusbro 的解决方案一样,它将问题分解为(1)检查列表中的每个元素作为减少的范围值,以及(2)根据定义范围内的当前值减少列表其余部分中的一个元素在 (1).

reduce([H|T], R) :-
    reduce_by([H|T], H, R).   % Reduce the list [H|T] by H yielding R
reduce([H|T], [H|R]) :-
    reduce(T, R).             % Do the same for the rest of the list

% reduce_by(L, X, R) reduces two elements in L by each value in the range 1 to X
reduce_by([X|T], X, R) :-
    reduce_once(T, X, R).     % Drop the element if diff is 0, reduce one more from rest
reduce_by([H|T], X, [Y|R]) :-
    H > X,
    Y is H - X,               % reduce current element by X, reduce one more from rest
    reduce_once(T, X, R).
reduce_by(L, X, R) :-
    X1 is X - 1,              % decrement reduction amount and reduce by that amount
    X1 > 0,
    reduce_by(L, X1, R).

% reduce_once(L, X, R) finds one element in L which is >= X and reduces it, else fails
reduce_once([X|T], X, T).     % same value, it's just removed (difference is 0)
reduce_once([H|T], X, [Y|T]) :-
    H > X,                    % reduce the value by X if we can
    Y is H - X.
reduce_once([H|T], X, [H|R]) :-
    reduce_once(T, X, R).     % skip this value and reduce a different value

结果:

| ?- reduce([1,2,3], L).

L = [1,3] ? a

L = [2,2]

L = [1,1]

L = [1,1,2]

no
| ?-

Prolog 与使用 Java 或 C# 或其他过程语言编程时有一个关键区别。 Prolog 通过回溯,将尝试找到使谓词成功的参数的所有实例化。有关详细信息,请参阅 。在编写 Prolog 谓词(或规则)时,您需要考虑如何陈述规则,以便它们在您希望它们成功的情况下成功。 Prolog 将通过回溯完成所有工作,以迭代所有可能的成功解决方案。

例如,在 reduce_once 的情况下,我们有一个子句:

reduce_once([X|T], X, T).

所以只要参数可以和输入统一,这就成功了。具体来说,reduce_once([1,2,3], 1, T). 之类的查询将在 T = [2,3] 时成功。但是一旦成功,Prolog 就会发现同一个谓词还有其他规则,并且也会尝试这些规则。因此,reduce_once([1,2,3], 1, [1|R]) :- reduce_once([2,3], 1, R).将被执行,等等