Perl Weekly Challenge 136: Fibonacci Sequence

by Abigail

Challenge

You are given a positive number $n.

Write a script to find how many different sequences you can create using Fibonacci numbers where the sum of unique numbers in each sequence are the same as the given number.

Fibonacci Numbers: 1,2,3,5,8,13,21,34,55,89, …

Example 1

Input:  $n = 16
Output: 4

Reason: There are 4 possible sequences that can be created using Fibonacci numbers
i.e. (3 + 13), (1 + 2 + 13), (3 + 5 + 8) and (1 + 2 + 5 + 8).

Example 2

Input:  $n = 9
Output: 2

Reason: There are 2 possible sequences that can be created using Fibonacci numbers
i.e. (1 + 3 + 5) and (1 + 8).

Example 3

Input:  $n = 15
Output: 2

Reason: There are 2 possible sequences that can be created using Fibonacci numbers
i.e. (2 + 5 + 8) and (2 + 13).

Discussion

Sequence A000119 in The On-Line Encyclopedia Of Integer Sequences gives the number of partitions of distinct Fibonnacci parts (ignoring 0, and not duplicating the 1).

This is exactly what we want.

One definition this page gives is (Reinhard Zumkeller, Nov 11, 2009):

\[ a(n) = f(n, 1, 1), \text{with } f(x, y, z) = \begin{cases} 0^x & \text{if } x < y \\ f (x - y, y + z, y) + f (x, y + z, y) & \text{if } x \geq y \end{cases} \]

To avoid having to deal with \(0^0\) which may not always be \(1\) in each of the language we will use (and we're too lazy to check), we use a slightly different, but equivalent, definition of \(f\):

\[ f(x, y, z) = \begin{cases} 0 & \text{if } x < y \\ 1 & \text{if } x = y \\ f (x - y, y + z, y) + f (x, y + z, y) & \text{if } x > y \end{cases} \]

Solution

A straight implementation of the formula above is easy. Here is one in Perl:

sub count;
sub count ($target, $this_fib = 1, $prev_fib = 1) {
      $this_fib >  $target ? 0
    : $this_fib == $target ? 1
    : count ($target - $this_fib, $this_fib + $prev_fib, $this_fib) +
      count ($target,             $this_fib + $prev_fib, $this_fib)
}

The problem is that it recuses. A lot. Here's a log-log graph showing how often it calls itself when called with a specific value:

Adding a cache, and not recursing if we are to calculate a value we have calculated before is easy:

sub count;
sub count ($target, $this_fib = 1, $prev_fib = 1) {
      state $cache = {};
      $$cache {$target, $this_fib} //=
          $this_fib >  $target ? 0
        : $this_fib == $target ? 1
        : count ($target - $this_fib, $this_fib + $prev_fib, $this_fib) +
          count ($target,             $this_fib + $prev_fib, $this_fib)
}

This reduces the number of calls dramatically, as shown by the graph below:

Since the graph is log-log, the difference looks less impressive that it is, but for \(n = 1,000,000\), the non-caching algorithm uses \(472,630,993\) calls, while the caching algorithm uses no more than \(3,999,941\) calls.

But while the number of recursive calls rapidly grows, even with caching, the number of different ways to partition an integer into different Fibonacci numbers only grows very slowly. In the graph below, we plotted the number of ways to partition the numbers up to 10,000. No number below 10,000 has more than 89 different ways to be partitioned into different sums of Fibonacci numbers.

Do it yourself

Input a number below, and it shows in how many ways it can be written as a sum of distinct Fibonacci Numbers:

Compare cached and uncached implementations:

Other languages

Implementation we made in other languages are very similar to the one we made in Perl. We'll show a selection of implementations, without further comments.

Go

var cache map [int] map [int] int

func _count (target int, this_fib int, prev_fib int) int {
    if _, ok := cache [target]; !ok {
        cache [target] = make (map [int] int)
    }

    if _, ok := cache [target] [this_fib]; !ok {
        var result int
               if target <  this_fib {
            result = 0
        } else if target == this_fib {
            result = 1
        } else {
            result = _count (target - this_fib, this_fib + prev_fib, this_fib) +
                     _count (target,            this_fib + prev_fib, this_fib)
        }
        cache [target] [this_fib] = result
    }

    return cache [target] [this_fib]
}

func count (target int) int {
    return _count (target, 1, 1)
}

Node.js

let cache = {}

function count (target, this_fib, prev_fib) {
    if (!this_fib) {this_fib = 1}
    if (!prev_fib) {prev_fib = 1}
    let key = target + ";" + this_fib
    if (!(key in cache)) {
        cache [key] = target <  this_fib ? 0
                    : target == this_fib ? 1
                    : count (target - this_fib, this_fib + prev_fib, this_fib) +
                      count (target,            this_fib + prev_fib, this_fib)
    }
    return cache [key]
}

Python

cache = {}

def _count (target, this_fib, prev_fib):
    key = str (target) + ";" + str (this_fib)
    if not (key in cache):
        if target <  this_fib:
            cache [key] = 0
        elif target == this_fib:
            cache [key] = 1
        else:
            cache [key] = \
                _count (target - this_fib, this_fib + prev_fib, this_fib) + \
                _count (target,            this_fib + prev_fib, this_fib)

    return cache [key]

def count (target):
    return (_count (target, 1, 1))

Links to complete programs

We have implementations in AWK, Bash bc, C, Go, Java, Lua, Node.js, Pascal, Perl, Python, R, Ruby, Scheme, and Tcl on GitHub.


Please leave any comments as a GitHub issue.