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.)

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

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) {
\$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
} ifelse
end
} bind def

Full code on github.