如何使用Prolog在数字列表中找到N的最小倍数?

时间:2014-04-08 18:01:54

标签: prolog

我需要在数字列表中找到N的最小倍数。

leastMultiple/2
leastMultipleOfThree/2, 
arg1= list of numbers,arg2= X (X is what we want to find, the least multiple of 3 in a list of numbers). 

例如,在[7,9,15,22]中找到3的最小倍数。我已经盯着这一段很长一段时间了,而且我不完全确定从哪里开始。如果你能简单地帮助我解决一下这个问题,我会非常感激。

1 个答案:

答案 0 :(得分:6)

我的答案的早期版本因使用“最小倍数”这个词而感到困惑。您想在列表中找到倍数,并检索最小值。我现在明白了。

首先我们必须检测N的倍数。我们可以通过使用模运算符来划分和查看余数来完成此操作,如下所示:

?- X is 7 mod 3.
X = 1.

?- X is 9 mod 3.
X = 0.

我将为此定义一个方便的方法,is_multiple_of:

% multiple_of(X, N) is true if X is a multiple of N
multiple_of(X, N) :- 0 is X mod N.

现在我们可以简单地说:

?- multiple_of(7, 3).
false.

?- multiple_of(9, 3).
true.

现在有两种方法可以继续。有效的方法,可以很容易地使尾递归以获得更好的性能,就是使用累加器将列表移动一次以保持当前的最小值。代码密集度较低的方法是将列表过滤到所有倍数并对其进行排序。让我们看看这两种方法:

% less code: using setof/3
leastMultipleOfThree(List, Result) :-
    setof(X, (member(X, List), multiple_of(X, 3)), [Result|_]). 

setof/3尽可能多次评估其第二个术语,每次在第一个术语中检索变量以包含在结果中,第三个术语。为了使列表唯一,setof/3对结果进行排序,因此最小值将在第一个位置结束。我们使用member(X, List), multiple_of(X, 3)作为一种非常简单的生成测试模式。所以它很简洁,但它看起来不太好,并且与构建列表和排序相关的成本意味着它不是最佳的。但它很简洁!

% more code: using an accumulator
leastMultipleOfThree(List, Result) :- leastMultipleOfThree(List, null, Result).

% helper
leastMultipleOfThree([], Result, Result) :- Result \= null.
leastMultipleOfThree([X|Xs], C, Result) :-
    multiple_of(X, 3) 
        -> (C = null -> leastMultipleOfThree(Xs, X, Result)
                      ; (Min is min(X, C), 
                         leastMultipleOfThree(Xs, Min, Result)))
         ; leastMultipleOfThree(Xs, C, Result).

这是相当多的代码,因为有几种情况需要考虑。第一条规则是清单熄灭的基本情况;我任意选择null代表我们尚未看到三个倍数的情况。右侧的测试确保如果列表为空并且我们从未找到三个的倍数,则会失败。

第二条规则实际上处理了三种情况。通常我会将它们分解为单独的谓词,但会有很多重复。它看起来像这样:

leastMultipleOfThree([X|Xs], null, Result) :-
  multiple_of(X, 3), 
  leastMultipleOfThree(Xs, X, Result).
leastMultipleOfThree([X|Xs], C, Result) :-
  multiple_of(X, 3),
  C \= null,
  Min is min(X, C),
  leastMultipleOfThree(Xs, Min, Result).
leastMultipleOfThree([X|Xs], C, Result) :-
  \+ multiple_of(X, 3),
  leastMultipleOfThree(Xs, C, Result).

这可能会或可能不会更具可读性(我更喜欢它),但它肯定会表现更差,因为这些规则中的每一个都会创建一个选择点,即规则中的if / else条件表达式不会。使用削减来改善这一点是很诱人的,但如果你尝试的话,你肯定会陷入一个地狱般的迷宫。

我希望在这一点上它是相当不言自明的。 :)