RogerBW's Blog

Perl Weekly Challenge 136: Fibonacci Friends 29 October 2021

I’ve been doing the Weekly Challenges. The latest involved GCDs, powers of two, and an unusual numeric sequence. (Note that this is open until 31 October 2021.)

Task 1: Two Friendly

You are given 2 positive numbers, $m and $n.

Write a script to find out if the given two numbers are Two Friendly.

Two positive numbers, m and n are two friendly when gcd(m, n) = 2 ^ p where p > 0. The greatest common divisor (gcd) of a set of numbers is the largest positive number that divides all the numbers in the set without remainder.

Very clearly there are two parts to this: calculate the gcd, then determine whether it's a power of 2 (and not less than 2). Therefore each part becomes a separate function.

Some of the languages offer a built-in gcd calculator, in which case I don't need to define that part; where that isn't the case, I use a Euclidean algorithm.

sub gcd {
  my ($m,$n)=@_;
  while ($n!=0) {
    ($m,$n)=($n,$m % $n);
  }
  return $m;
}

For the power-of-two detection, a binary approach seems reasonable, first checking that the number is large enough:

sub ispower2 {
  my $n=shift;
  if ($n<2) {
    return 0;
  }

If it is, bitwise AND the number with itself-minus-1, which will produce a 0 result if and only if there is only one bit set. (This should be quicker than using a general-purpose set bit counter.)

  return ($n & ($n-1))==0;
}

Then the actual function to solve the task simply becomes:

sub twofriendly {
  my ($m,$n)=@_;
  return ispower2(gcd($m,$n))?1:0;
}

The other languages work basically the same way. Ruby and Raku have gcd built in; Python has it as part of math, a core module. In Rust I'd need to link in another crate to use the official one, so to keep the task in one file I wrote my own. In PostScript I use the same modular approach (yes, this is the same algorithm as the above)…

/gcd {
    {
        dup
        3 1 roll
        mod
        dup 0 eq {
            pop exit
        } if
    } loop
} bind def

/ispower2 {
    dup 2 lt {
        pop false
    } {
        dup 1 sub and 0 eq
    } ifelse
} bind def

/twofriendly {
    gcd ispower2
} bind def

Task 2: Fibonacci Sequence

You are given a positive number $n.

Write a script to find out how many different sequence you can create using Fibonacci numbers when adds up is same as given number without repeating a number.

Well now.

There are two ways of answering this. (Probably more really.) One is to work it through: generate a list of all Fibonacci numbers up to and including the target, then search through combinations of them to find the ones that add up correctly. Most simply one could do this with a bitmask, though clearly there are lots of optimisations possible. In Perl:

sub fib {
  my $max=shift;
  my @s=(1,2);
  while ($s[-1] < $max) {
    push @s,$s[-1]+$s[-2];
  }
  return \@s;
}

sub fibseq {
  my $m=shift;
  my @pattern=@{fib($m)};
  my $o=0;
  foreach my $mask (1..(1 << scalar @pattern)-1) {
    my $s=0;
    foreach my $bit (0..$#pattern) {
      if (1<<$bit & $mask) {
        $s+=$pattern[$bit];

Short-cut to avoid pointless mask calculation; more short-cuts are certainly possible (for example, any mask value that has all the same bits set that this one does will also exceed the target, so we needn't look at those at all and could delete them from the list of candidate masks, if we were keeping that as a list rather than a for-loop).

        if ($s>$m) {
          last;
        }
      }
    }
    if ($s==$m) {
      $o++;
    }
  }
  return $o;
}

But I didn't get into that optimisation route, because doing this for each number 1..n will obviously produce a sequence of integers, so off I went to the OEIS, and there it is: A000119 Number of representations of n as a sum of distinct Fibonacci numbers. And that gives a selection of formulae to generate the desired result directly (after all, we're not being asked to generate all the sequences, just the number of them). I choose to use a recursive formula from the late Reinhard Zumkeller, given as:

a(n) = f(n,1,1) with f(x,y,z) = if x<y then 0^x else f(x-y,y+z,y)+f(x,y+z,y).

Because I don't want to mess with exponentiation and potential non-integer values, I format that 0^x as a second conditional (1 if x is 0, otherwise 0). And thus the codebase comes down to:

sub fibseq {
  my $m=shift;
  return f($m,1,1);
}

sub f {
  my ($x,$y,$z)=@_;
  if ($x < $y) {
    return ($x==0)?1:0;
  } else {
    return f($x-$y,$y+$z,$y)+f($x,$y+$z,$y);
  }
}

and works the same way across all languages, even PostScript. (Yeah, I could do stack manipulation with e.g. index as a deep version of dup rather than using a dict for local variables, but the code would be horrid.)

/f {
    3 dict begin
    /z exch def
    /y exch def
    /x exch def
    x y lt {
        x 0 eq {
            1
        } {
            0
        } ifelse
    } {
        x y sub y z add y f
        x y z add y f
        add
    } ifelse
    end
} bind def

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.

Search
Archive
Tags 1920s 1930s 1940s 1950s 1960s 1970s 1980s 1990s 2000s 2010s 3d printing action advent of code aeronautics aikakirja anecdote animation anime army astronomy audio audio tech base commerce battletech beer boardgaming book of the week bookmonth chain of command children chris chronicle church of no redeeming virtues cold war comedy computing contemporary cornish smuggler cosmic encounter coup covid-19 crime crystal cthulhu eternal cycling dead of winter doctor who documentary drama driving drone ecchi economics en garde espionage essen 2015 essen 2016 essen 2017 essen 2018 essen 2019 essen 2022 essen 2023 essen 2024 existential risk falklands war fandom fanfic fantasy feminism film firefly first world war flash point flight simulation food garmin drive gazebo genesys geocaching geodata gin gkp gurps gurps 101 gus harpoon historical history horror hugo 2014 hugo 2015 hugo 2016 hugo 2017 hugo 2018 hugo 2019 hugo 2020 hugo 2021 hugo 2022 hugo 2023 hugo 2024 hugo-nebula reread in brief avoid instrumented life javascript julian simpson julie enfield kickstarter kotlin learn to play leaving earth linux liquor lovecraftiana lua mecha men with beards mpd museum music mystery naval noir non-fiction one for the brow opera parody paul temple perl perl weekly challenge photography podcast politics postscript powers prediction privacy project woolsack pyracantha python quantum rail raku ranting raspberry pi reading reading boardgames social real life restaurant reviews romance rpg a day rpgs ruby rust scala science fiction scythe second world war security shipwreck simutrans smartphone south atlantic war squaddies stationery steampunk stuarts suburbia superheroes suspense television the resistance the weekly challenge thirsty meeples thriller tin soldier torg toys trailers travel type 26 type 31 type 45 vietnam war war wargaming weather wives and sweethearts writing about writing x-wing young adult
Special All book reviews, All film reviews
Produced by aikakirja v0.1