First of all, adding at the end of the list with append/3
is pretty slow. If you need to, use the difference lists instead. (Personally, I try to avoid append/3
as much as possible)
Secondly, your prime/2
always checks the entire list when checking a prime. This is too slow. Instead, you can simply check the identifier, you can find the integral coefficient up to the square root of the number you want to check.
problem_010(R) :- p010(3, 2, R). p010(2000001, Primes, Primes) :- !. p010(Current, In, Result) :- ( prime(Current) -> Out is In+Current ; Out=In ), NewCurrent is Current + 2, p010(NewCurrent, Out, Result). prime(2). prime(3). prime(X) :- integer(X), X > 3, X mod 2 =\= 0, \+is_composite(X, 3). % was: has_factor(X, 3) is_composite(X, F) :- % was: has_factor(X, F) X mod F =:= 0, !. is_composite(X, F) :- F * F < X, F2 is F + 2, is_composite(X, F2).
Disclaimer: I found this implementation of prime/1
and has_factor/2
by googling.
This code gives:
?- problem_010(R). R = 142913828922 Yes (12.87s cpu)
Here is another quick code:
problem_010(R) :- Max = 2000001, functor(Bools, [], Max), Sqrt is integer(floor(sqrt(Max))), remove_multiples(2, Sqrt, Max, Bools), compute_sum(2, Max, 0, R, Bools). % up to square root of Max, remove multiples by setting bool to 0 remove_multiples(I, Sqrt, _, _) :- I > Sqrt, !. remove_multiples(I, Sqrt, Max, Bools) :- arg(I, Bools, B), ( B == 0 -> true % already removed: do nothing ; J is 2*I, % start at next multiple of I remove(J, I, Max, Bools) ), I1 is I+1, remove_multiples(I1, Sqrt, Max, Bools). remove(I, _, Max, _) :- I > Max, !. remove(I, Add, Max, Bools) :- arg(I, Bools, 0), % remove multiple by setting bool to 0 J is I+Add, remove(J, Add, Max, Bools). % sum up places that are not zero compute_sum(Max, Max, R, R, _) :- !. compute_sum(I, Max, RI, R, Bools) :- arg(I, Bools, B), (B == 0 -> RO = RI ; RO is RI + I ), I1 is I+1, compute_sum(I1, Max, RO, R, Bools).
This runs an order of magnitude faster than the code above:
?- problem_010(R). R = 142913828922 Yes (0.82s cpu)