I’ve been doing the Perl Weekly
Challenges. The
latest
involved GCDs and magic squares. (Note that this is open until 6
December 2020.)
TASK #1 › GCD Sum
You are given a positive integer $N.
Write a script to sum GCD of all possible unique pairs between 1 and
$N.
There doesn't appear to be an obvious optimisation listed at
OEIS, so I did this naïvely.
sub gs {
  my $n=shift;
  my $tot=$n-1; # gcd(1,2)+gcd(1,3)+...+gcd(1,n)
  foreach my $a (2..$n-1) {
    $tot+=sum(map {gcd($a,$_)} ($a+1..$n));
  }
  return $tot;
}
Perl doesn't have a built-in gcd, so rather than use one from CPAN I
implemented Stein's
algorithm (on the
basis that I've written Euclid's before). This is rather more complex
than the main problem, but I made it non-recursive for clarity..
sub gcd {
  my ($a,$b)=@_;
  if ($a==0) {
    return $b;
  }
  if ($b==0) {
    return $a;
  }
  my $d=0;
  while (1) {
    if ($a == $b) {
      return $a << $d;
    }
    my $aa=($a % 2 == 0);
    my $bb=($b % 2 == 0);
We do different things based on whether they're both even, only one
is, or neither is.
    if ($aa && $bb) {
      $a >>= 1;
      $b >>= 1;
      $d++;
    } elsif ($aa) {
      $a >>= 1;
    } elsif ($bb) {
      $b >>= 1;
    } else {
      my $c=abs($a-$b);
      $a=min($a,$b);
      $b=$c >> 1;
    }
  }
}
Raku came out basically the same, except that sum and min are
built-in and its binary shift operators look weird. Python has its own
gcd, as does Ruby, and they basically look the same too. But Rust…
all right, I had to write a min function too. (Or steal it out of
the Blandy/Orendorff Programming Rust.)
TASK #2 › Magical Matrix
Write a script to display matrix as below with numbers 1 - 9. Please
make sure numbers are used once.
i.e. a 3×3 magic square. But there is only one 3×3 magic square
(modulo reflections and rotations) and that would have been boring. So
instead I used the Siamese algorithm (de la Loubère, 1688), with a
minor variation so that indices were always incremented (so as to
avoid, when I got to the Rust version, the conversions I had to do
last time).
This should produce a magic square of any odd-numbered size, with any
starting number and increment.
sub ms {
  my ($order,$start,$inc)=@_;
  my $m;
  foreach (1..$order) {
    push @{$m},[(0) x $order];
  }
  my ($x,$y)=(int($order/2)+1,int($order/2));
  my $n=$start;
  while (1) {
    $m->[$x][$y]=$n;
    $n+=$inc;
    my ($xa,$ya)=(($x+1) % $order,($y+1) % $order);
    if ($m->[$xa][$ya]>0) {
      ($xa,$ya)=(($x+2) % $order,$y);
      if ($m->[$xa][$ya]>0) {
        last;
      }
    }
    ($x,$y)=($xa,$ya);
  }
  return $m;
}
Actually printing it out is a job for library code.
Raku, Python and Ruby were similar, and even Rust is recognisably the
same algorithm:
fn ms(order: usize, start: i64, inc: i64) -> Vec<Vec<i64>> {
    let mut m=vec![vec![0; order]; order];
    let mut x: usize=(order/2)+1;
    let mut y: usize=order/2;
    let mut n=start;
    loop {
        m[x][y]=n;
        n += inc;
        let mut xa=(x+1) % order;
        let mut ya=(y+1) % order;
        if m[xa][ya] > 0 {
            xa=(x+2) % order;
            ya=y;
            if m[xa][ya] > 0 {
                break;
            }
        }
        x=xa;
        y=ya;
    }
    return m;
}
Full code on
github.
 
Comments on this post are now closed. If you have particular grounds for adding a late comment, comment on a more recent post quoting the URL of this one.