【问题标题】:Prolog search for possible combination for subtracting 2 elements from a listProlog搜索从列表中减去2个元素的可能组合
【发布时间】:2016-02-15 06:33:06
【问题描述】:

这是此页面的一个扩展问题。 Prolog possible removal of elements in a list

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

从第一个/第二个元素中减去 1,X 变为 [1-1, 2-1, 3] = [0, 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.

【问题讨论】:

  • 您能否举例说明您希望查询的外观?以及您真正想要如何定义谓词的作用?不是很清楚。
  • 我已经尝试使用permutation([1,2,3], X) 来获取所有可能的组合,这样我就可以简单地从列表中获取前两个元素。减法后,我可以简单地通过排序得到结果。但是,我发现这种方式有一些重复..
  • @lurker 所以,我总是可以对列表中的任意 2 个元素进行减法运算。但是在减法之后,元素必须始终大于或等于 0。并且对于减法,两个元素必须用相同的数字相减,即E1 - N = R1 >= 0E2 - N = R2 >= 0。和我之前的问题一样,如果 R1 或 R2 = 0,它必须从列表中删除。它更有意义吗?
  • @lurker 很抱歉描述的混乱。实际上我已经研究这个问题一个星期了...我完全不知道..
  • 不,我的错。我只是愚蠢。现在似乎很清楚了。

标签: list search prolog subtraction


【解决方案1】:

这是一个使用更多基本操作的替代解决方案。与@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 通过回溯,将尝试找到使谓词成功的参数的所有实例。有关详细信息,请参阅this answer。在编写 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). 将被执行,等等。

【讨论】:

  • 有趣。您的解决方案适用于升序或降序的列表。其实这也是我想做的。而且我之前尝试过..但是我从尾部获取一个元素失败了..并且在减法后如何统一列表中苦苦挣扎..
  • @user3390652 你对我的实现有疑问吗?我很乐意回答。我对解决方案做了一些解释,但显然并不详尽(这将是很多材料:))。
  • 我有一个关于我的问题的更新。大声笑我写了一些东西,它和你的代码和 gusbro 的代码做同样的事情。但是,我担心我的第一个程序的第一个子句太乱了。你会如何修改它?大声笑
  • @user3390652 为什么你觉得它太乱了?使用between 是我在解决方案中进行的计数器维护的替代方法。您可以选择其中一个。
  • 对不起,实际上我已经编辑了我的代码 3 次.. 这是我能得到的最简单的版本。大声笑
【解决方案2】:

我会使用 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.

【讨论】:

  • 这……太有技巧了。。开始写第一个程序有什么技巧吗?我可以很容易地在 Java/C++/C# 或类似语言上完成这个问题。但是对于 prolog,我总是在努力如何开始......
  • @user3390652:Prolog 非常适合使用递归编程算法。在这种情况下,您尝试“看到”解决问题的递归算法。所以在这种情况下,你知道你必须取出一个项目,用它做一些事情,然后从剩余的列表中取出另一个元素。这就是为什么我把它分成两个程序。第一个负责选择一个元素并应用您的第一个转换(减去数字),然后调用第二个过程负责选择另一个项目并应用相同的转换。
  • Prolog 的回溯会为您完成其余的工作(递归地获取问题的所有解决方案)。
  • 刚刚提出一个问题。当我在玩这个解决方案时,我发现这与升序列表完美配合,但不适用于降序列表。如果不对输入进行排序,是否可以使其同时编译两种情况?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-09-11
  • 2019-01-22
  • 2016-02-12
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多