我试图找到低于1000的所有正数3和5的总和。在添加了应该从5的倍数之和中去除3的倍数的部分之后,gprolog将继续吐出“否” “对于查询?- sigma(1000,N).
问题显然在于sigma5,但我无法发现它:
sigma(Num, Result) :- sigma3(Num, 3, Result3),
sigma5(Num, 5, Result5),
Result is Result3 + Result5.
sigma3(Num, A, Result) :- A < Num,
Ax is A+3,
sigma3(Num, Ax, ResultX),
Result is ResultX + A.
sigma3(Num, A, Result) :- A >= Num,
Result is 0.
sigma5(Num, A, Result) :- A < Num,
mod3 is A mod 3,
0 \= mod3,
Ax is A+5,
sigma5(Num, Ax, ResultX),
Result is ResultX + A.
sigma5(Num, A, Result) :- A < Num,
mod3 is A mod 3,
0 == mod3,
Ax is A+5,
sigma5(Num, Ax, ResultX),
Result is ResultX.
sigma5(Num, A, Result) :- A >= Num,
Result is 0.
我的代码有什么问题?
答案 0 :(得分:4)
由于涉及整数,请考虑使用finite domain constraints。例如,使用SWI-Prolog:
?- use_module(library(clpfd)).
true.
?- findall(N, (N mod 3 #= 0 #\/ N mod 5 #= 0, N in 0..999, indomain(N)), Ns),
sum(Ns, #=, Sum).
Ns = [0, 3, 5, 6, 9, 10, 12, 15, 18|...],
Sum = 233168.
答案 1 :(得分:2)
Prolog从未因其算术能力而受欢迎。
这是因为需要为符号处理表示'术语构造函数'而不进行过度评估,因此当需要实际算术时,我们必须为结果显式分配'space'(变量),而不是'传递' ' 一种表达。这导致相当冗长和令人不愉快的代码。
但是使用一些流行的扩展,如CLP(FD),可以在GProlog和SWI-Prolog中使用,我们得到了更好的结果,在其他语言中不容易获得:即整数域的闭包超过通常的算术操作。例如,来自SWI-Prolog CLP(FD)库的“双向”因子
n_factorial(0, 1).
n_factorial(N, F) :- N #> 0, N1 #= N - 1, F #= N * F1, n_factorial(N1, F1).
?- n_factorial(X, 3628800).
X = 10 .
无论如何,这是对原始问题的简单解决方案,类似于您尝试的方法,但使用累加器来计算结果。这个简单的技巧允许编写尾递归过程,效率更高。
sigma(Num, Result) :-
sigma(1, Num, 0, Result).
sigma(N, M, Acc, Tot) :-
( N < M, !,
( (0 is N mod 3 ; 0 is N mod 5)
-> Sum is Acc + N
; Sum is Acc
),
N1 is N + 1,
sigma(N1, M, Sum, Tot)
; Tot is Acc
).
测试:
?- sigma(1000, X).
X = 233168 .
答案 2 :(得分:1)
mod3 is A mod 3,
(以及mod3的所有其他出现)应该是Mod3,因为它是一个变量。 使用该修复程序,程序正确运行(至少N = 1000)
这是我的解决方案(使用高阶谓词):
sum(S):-
findall(X,between(1,999,X),L), % create a list with all numbers between 1 and 999
include(div(3),L,L3), % get the numbers of list L which are divisible by 3
include(div(5),L,L5), % get the numbers of list L which are divisible by 5
append(L3,L5,LF), % merge the two lists
list_to_set(LF,SF), % eliminate double elements
sumlist(SF,S). % find the sum of the members of the list
div(N,M):-
0 is M mod N.
当然效率较低但输入太小而无法产生显着差异
答案 3 :(得分:0)
这对我来说似乎很复杂。
sum_of( L , S ) :-
L > 0 ,
sum_of( 0 , L , 0 , S )
.
sum_of( X , X , S , S ) . % if we hit the upper bound, we're done.
sum_of( X , L , T , S ) :- % if not, look at it.
X < L , % - backtracking once we succeeded.
add_mult35( X , T , T1 ) , % - add any multiple of 3 or 5 to the accumulator
X1 is X + 1 , % - next X
sum_of( X1 , L , T1 , S ) % - recurse
.
add_mult35( X , T , T ) :- % no-op if X is
X mod 3 =\= 0 , % - not a multiple of 3, and
X mod 5 =\= 0 , % - not a multiple of 5
!. %
add_mult35( X , T , T1 ) :- % otherwise,
T1 is T + X % increment the accumulator by X
.
这可能比它更简洁。
除了我的代码可能非常可怕(它实际上比我的C解决方案更长,这本身就是一个壮举),
ANSI C:
int sum_multiples_of_three_and_five( int lower_bound , int upper_bound )
{
int sum = 0 ;
for ( int i = lower_bound ; i <= upper_bound ; ++i )
{
if ( 0 == i % 3 || 0 == i % 5 )
{
sum += i ;
}
}
return sum ;
}