RabbitFarm
2024-10-14
Double Luhn
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]
The majority of the work can be done in a few lines. If there is a more elegant way to do this, it escaped me when I was writing this code!
-
sub double_exist{
my(
@a) =
@_;
do{
my $i = $_;
do{
my $j = $_;
if($i != $j){
return 1 if $a[$i] == 2 * $a[$j];
}
} for 0 ..
@a - 1;
} for 0 ..
@a - 1;
return 0;
}
◇
-
Fragment referenced in 2.
The rest of the code just tests this function.
-
MAIN:{
say double_exist 6, 2, 3, 3;
say double_exist 3, 1, 4, 13;
say double_exist 2, 1, 4, 2;
}
◇
-
Fragment referenced in 2.
Sample Run
$ perl perl/ch-1.pl 1 0 1
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 can also be done in relatively few lines. There are no real special cases here.
-
sub luhn{
my($digits) =
@_;
my
@digits = $digits =~ m/([0-9])/g;
my $sum = 0;
my $check = pop
@digits;
{
my $x = pop
@digits;
my $y = pop
@digits;
if(defined $x && defined $y){
$sum += $y + sum_digits 2 * $x;
}
else{
$sum += sum_digits 2 * $x;
}
redo if
@digits;
}
return 1 if 0 == ($sum + $check) % 10;
return 0;
}
◇
-
Fragment referenced in 7.
For convenience we’ll put the summing of digits for numbers > 10 in a separate function.
-
sub sum_digits{
my($x) =
@_;
if($x >= 10){
my
@a = split //, $x;
return $a[0] + $a[1];
}
return $x;
}
◇
-
Fragment referenced in 7.
The rest of the code drives some tests.
-
MAIN:{
say luhn q/17893729974/;
say luhn q/4137 8947 1175 5904/;
say luhn q/4137 8974 1175 5904/;
}
◇
-
Fragment referenced in 7.
Sample Run
$ perl perl/ch-2.pl 1 1 0
References
posted at: 01:19 by: Adam Russell | path: /perl | permanent link to this entry
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