RabbitFarm
2024-10-14
The Weekly Challenge 290 (Prolog Solutions)
The examples used here are from the weekly challenge problem statement and demonstrate the working solution.
Part 1: Double Exist
You are given an array of integers, @ints. Write a script to find if there exist two indices $i and $j such that:
- $i≠$j
- 0 ≥ $i < size @ints and 0 ≥ $j < size @ints
- $ints[$i] = 2 ∗ $ints[$j]
This is a nice problem for Constraint Logic Programming. The solution is contained in just a single predicate which uses GNU Prolog’s finite domain (FD) constraint solver.
-
double_exist(L, I, J):-
length(L, Length),
fd_domain([I, J], 1, Length),
I #\= J,
fd_element(I, L, X),
fd_element(J, L, Y),
X #= 2 * Y,
fd_labeling([I, J]).
◇
-
Fragment referenced in 2.
The rest of the code just wraps this predicate into a file.
Sample Run
$ gprolog --consult-file prolog/ch-1.p | ?- double_exist([6, 2, 3, 3], I, J). I = 1 J = 3 yes | ?- double_exist([3, 1, 4, 13], I, J). no | ?- double_exist([2, 1, 4, 2], I, J). I = 1 J = 2 ? (1 ms) yes | ?- double_exist([2, 1, 4, 2], I, J). I = 1 J = 2 ? ; I = 3 J = 1 yes | ?-
Part 2: Luhn’s Algorithm
You are given a string $str containing digits (and possibly other characters which can be ignored). The last digit is the payload; consider it separately. Counting from the right, double the value of the first, third, etc. of the remaining digits. For each value now greater than 9, sum its digits. The correct check digit is that which, added to the sum of all values, would bring the total mod 10 to zero. Return true if and only if the payload is equal to the correct check digit.
This is essentially a list processing problem and is quite amenable to a DCG solution. Here I just use some ordinary predicates though.
-
luhn(L):-
reverse(L, [Check|T]),
luhn(Check, T, DigitSums),
sum_list(DigitSums, S0),
M is (S0 + Check) mod 10, !,
M == 0.
luhn(_, [], []).
luhn(Check, [H0, H1|T], [DigitSum|DigitSums]):-
DS is H0 * 2,
sum_digits(DS, Sum),
DigitSum is Sum + H1,
luhn(Check, T, DigitSums).
luhn(Check, [H|T], [[DigitSum|DigitSums]]):-
DS is H * 2,
sum_digits(DS, Sum),
DigitSum is Sum,
luhn(Check, T, DigitSums).
◇
-
Fragment referenced in 5.
For convenience we’ll have a predicate for summing the digits of numbers > 10.
-
sum_digits(N, Sum):-
N < 10,
Sum = N.
sum_digits(N, Sum):-
number_chars(N, [C0, C1]),
number_chars(N0, [C0]),
number_chars(N1, [C1]),
Sum is N0 + N1.
◇
-
Fragment referenced in 5.
Finally, let’s assemble our completed code into a single file.
Sample Run
$ gprolog prolog/ch-2.p | ?- luhn([1, 7, 8, 9, 3, 7, 2, 9, 9, 7, 4]). yes | ?- luhn([4, 1, 3, 7, 8, 9, 4, 7, 1, 1, 7, 5, 5, 9, 0, 4]). yes | ?- luhn([4, 1, 3, 7, 8, 9, 7, 4, 1, 1, 7, 5, 5, 9, 0, 4]). no | ?-
References
posted at: 00:32 by: Adam Russell | path: /prolog | permanent link to this entry