Two Times Zero is Zero: Weekly Challenge #290
Here we start Weekly Challenge #290
290 is the telephone country code for Saint Helena, a remote island in the South Atlantic Ocean where Napoleon Bonaparte spent his second exile. There’s a video maker who made a documentary discussing the creation of an airport on the island.
Task 1: Double Exist
Submitted by: Mohammad Sajid Anwar
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, $j) < scalar @ints
$ints[$i] == 2 \* $ints[$j]
Let’s Talk About It!
At first, I thought that point #1 was not particularly limiting, because it’s a rare case where $i == 2 * $1, but then I remember that this is true when $i == 0, so it is a valid constraint.
Here I loop across the whole array twice. For larger data sets, starting the second loop at $i + 1 and comparing 2 * $i == $j as well as 2 * $j == $i would get better performance, but at the sizes we’re dealing with, that’s not significant.
And as is common with these sort of things, it returns true when truth can be proved, and returns false at the end of the function once all the options have been tested.
Show Me The Code!
#!/usr/bin/env perl
use strict;
use warnings;
use experimental qw{ say state postderef signatures };
use List::Util qw{ uniq };
my @examples = (
    [ 6, 2, 3, 3 ],
    [ 3, 1, 4, 13 ],
    [ 2, 1, 4, 2 ],
    [ 1, 1, 0 ],
    [ 1, 1, 0, 0 ],
);
# [ 1, 1, 0 ] is false, because 1 isn't 2 * 0
# [ 1, 1, 0, 0 ] is true, because 0 is 2 * 0
for my $example (@examples) {
    my $output = doubles_exist( $example->@* );
    my $input  = join ', ', $example->@*;
    say <<"END";
    Input:  \$ints = ($input)
    Output: $output
END
}
sub doubles_exist (@array) {
    for my $i ( 0 .. -1 + scalar @array ) {
        for my $j ( 0 .. -1 + scalar @array ) {
            next if $i == $j;
            my ( $ii, $jj ) = map { $array[$_] } $i, $j;
            return 'true' if $ii == 2 * $jj;
        }
    }
    return 'false';
}
PS C:\Users\jacob\Documents\GitHub\perlweeklychallenge-club\challenge-290\dave-jacoby> .\perl\ch-1.pl
    Input:  $ints = (6, 2, 3, 3)
    Output: true
    Input:  $ints = (3, 1, 4, 13)
    Output: false
    Input:  $ints = (2, 1, 4, 2)
    Output: true
    Input:  $ints = (1, 1, 0)
    Output: false
    Input:  $ints = (1, 1, 0, 0)
    Output: true
Task 2: Luhn’s Algorithm
Submitted by: Andrezgz
You are given a string$strcontaining 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.
It was originally posted on reddit.
Let’s Talk About It!
This is how we know a credit card number is valid, without going so far as to seeing if there’s a specific card with this number. I think that checksums generally live lower on the stack than I normally work, because I don’t see them in the client code I’ve dealt with, but it’s a cool idea.
I’ll highlight one constraint: For each value now greater than 9, sum its digits. That’ll only be a worry when you’re on the odd-numbered entries, but guess what? The sum of the digits of a single-digit number is that digit, so split and sum every number. I of course use sum0 from List::Util. sum would work, but I like the behavior when you hit an empty list with sum0 better.
Show Me The Code!
#!/usr/bin/env perl
use strict;
use warnings;
use experimental qw{ say state postderef signatures };
use List::Util qw{sum0};
my @examples = (
    "17893729974",
    "4137 8947 1175 5904",
    "4137 8974 1175 5904",
);
for my $input (@examples) {
    my $output = luhns_algorithm($input);
    say <<"END";
    Input:  "$input"
    Output: $output
END
}
sub luhns_algorithm ($str) {
    $str =~ s/\D+//gmx;
    my @str     = split //, $str;
    my $payload = pop @str;
    my $x   = 0;
    my $sum = 0;
    for my $i ( reverse 0 .. -1 + scalar @str ) {
        my $d = $str[$i];
        $d *= 2 if $x % 2 == 0;
        my $e = sum0( split //, $d );
        $sum += $e;
        $x++;
    }
    return  ( ( ( ( $sum + $payload ) % 10 ) == 0 ) ? 'true' : 'false' );
}
PS C:\Users\jacob\Documents\GitHub\perlweeklychallenge-club\challenge-290\dave-jacoby> .\perl\ch-2.pl
    Input:  "17893729974"
    Output: true
    Input:  "4137 8947 1175 5904"
    Output: true
    Input:  "4137 8974 1175 5904"
    Output: false
PS C:\Users\jacob\Documents\GitHub\perlweeklychallenge-club\challenge-290\dave-jacoby>