Large integers in memorable recursive fibonacci (Y)

I wrote version Y that automatically caches old values ​​in a closure using memoization.

var Y = function (f, cache) {
    cache = cache || {};
    return function (x) {
        if (x in cache) return cache[x];
        var result = f(function (n) {
            return Y(f, cache)(n);
        })(x);
        return cache[x] = result;
    };
};

      

Now when almostFibonacci

(defined below) is passed into the above function, it returns a large Fibonacci number comfortably.

var almostFibonacci = function (f) {
    return function (n) {
        return n === '0' || n === '1' ? n : f(n - 1) + f(n - 2);
    };
};

      

However, after a certain value ( Number.MAX_SAFE_INTEGER

), integers in JavaScript (due to their IEEE-754 double precision format ) are inaccurate. So, given the fact that the only math operations in the Fibonacci function above are additions and subtractions, and since the operators cannot be overloaded in JavaScript, I wrote naive implementations of sum and difference functions (which use strings to support large integers) that are in the following.

String.prototype.reverse = function () {
    return this.split('').reverse().join('');
};

var difference = function (first, second) {
    first = first.reverse();
    second = second.reverse();
    var firstDigit,
    secondDigit,
    differenceDigits = [],
        differenceDigit,
        carry = 0,
        index = 0;
    while (index < first.length || index < second.length || carry !== 0) {
        firstDigit = index < first.length ? parseInt(first[index], 10) : 0;
        secondDigit = index < second.length ? parseInt(second[index], 10) : 0;
        differenceDigit = firstDigit - secondDigit - carry;
        differenceDigits.push((differenceDigit + (differenceDigit < 0 ? 10 : 0)).toString());
        carry = differenceDigit < 0 ? 1 : 0;
        index++;
    }
    differenceDigits.reverse();
    while (differenceDigits[0] === '0') differenceDigits.shift();
    return differenceDigits.join('');
};

var sum = function (first, second) {
    first = first.reverse();
    second = second.reverse();
    var firstDigit,
    secondDigit,
    sumDigits = [],
        sumDigit,
        carry = 0,
        index = 0;
    while (index < first.length || index < second.length || carry !== 0) {
        firstDigit = index < first.length ? parseInt(first[index], 10) : 0;
        secondDigit = index < second.length ? parseInt(second[index], 10) : 0;
        sumDigit = firstDigit + secondDigit + carry;
        sumDigits.push((sumDigit % 10).toString());
        carry = sumDigit > 9 ? 1 : 0;
        index++;
    }
    sumDigits.reverse();
    while (sumDigits[0] === '0') sumDigits.shift();
    return sumDigits.join('');
};

      

Both of these functions now work fine on their own. 1

I have now updated the function almostFibonacci

as follows to use the sum function instead of + and the difference function instead of - .

var almostFibonacci = function (f) {
    return function (n) {
        return n === '0' || n === '1' ? n : sum(f(difference(n, '1')), f(difference(n, '2')));
    };
};

      

As you may have guessed, this works. It breaks the violin in the case of even a small number such as 10.

Question: What could be wrong? All functions here work great individually. But in tandem, they seem to fail. Can anyone here help me debug this particularly difficult scenario?

1 Except for the case of the edge for the difference function. This requires the first argument to be greater than the second.

+3


source to share


2 answers


Now, on their own, both of these functions work fine - except for the edge case for the difference function. This requires the first argument to be greater than the second.

And what a problem. In your fibonacci algorithm, you calculate at some point difference("2", "2")

which should work "0"

. However, it returns an empty string ""

, which is not checked as a protection condition for recursion. When at the next stage of the calculation difference("", "1")

, the function will end up in an infinite loop.



Solutions:

  • Fix that extreme edge (you don't have to deal with negative numbers anyway)
  • Don't use strings for the ordinal, but only for the fibonacci number itself. You are unlikely to try to calculate the number (2 53 +1) th fibonacci, right? I would guess that this is also a significant improvement in speed.

    var fibonacci = Y(function(fib) {
        return function(n) {
            if (n == 0) return "0";
            if (n == 1) return "1";
            return sum(fib(n-1), fib(n-2));
        };
    });
    
          

+2


source


This is how I solved the problem.

Changes

  • I removed the operator while (differenceDigits[0] === '0') differenceDigits.shift();

    . Although this outputs the differences without truncated leading zeros, it outputs '0'

    in the case of an edge case such as difference('2', '2')

    .
  • I edited the return statement in the function almostFibonacci

    before return n == 0 || n == 1 ? n : sum(f(difference(n, '1')), f(difference(n, '2')));

    . Note that I am checking for 0, not "0" with the lax equality operator. 1


1 The reason that I'm doing n == 0

, as opposed to n === '0'

, is that in JavaScript, '00000' == 0

, but '00000' !== '0'

and in my new updated features differences, truncated without leading zeros, I can not guarantee the number of zeros to the zero output. Well, actually I can. There would be as many zeros as length n.

100th Fibonacci - JSFiddle

-1


source







All Articles