Big Integers in Memoized Recursive Fibonacci (Y) - javascript

I've written a version of 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 the value of 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 (owing to their IEEE-754 double precision format) are not accurate. So, considering the fact that the only mathematical operations in the Fibonacci function above are addition and subtraction and since operators cannot be overloaded in JavaScript, I wrote naïve implementations of the sum and difference functions (that both use strings to support big integers) which are as follows.
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('');
};
Now, by themselves, both these functions work perfectly.1
I have now updated the almostFibonacci function to as follows to use the sum function instead of + and the difference function instead of the - operator.
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 does work. It crashes the fiddle in case of even a small number like 10.
Question: What could be wrong? All the functions here work perfectly individually. But in tandem, they seem to fail. Can anyone here help me debug this particularly complex scenario?
1Except an edge case for the difference function. It requires the first argument to be larger than the second.

Now, by themselves, both these functions work perfectly - Except an edge case for the difference function. It requires the first argument to be larger than the second.
And that's the problem. In your fibonacci algorithm you're at some point calculating difference("2", "2"), which needs to yield "0" to work. It does however return the empty string "", which is not tested against as your guard condition for the recursion. When in the next step computing difference("", "1"), the function will fall into an infinite loop.
Solutions:
Fix that edge case (you still won't need to cope with negative numbers)
Don't use strings for the ordinal number, but only for the fibonacci number itself. You hardly will try the compute the (253+1)th fibonacci number, will you? I would assume this to be a significant speed improvement as well.
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));
};
});

Here is how I solved the problem at hand.
Changes:
I removed the while (differenceDigits[0] === '0') differenceDigits.shift(); statement. Even though this outputs differences without truncated leading zeros, it outputs a '0' in case of an edge case like difference('2', '2').
I edited the return statement in the almostFibonacci function to return n == 0 || n == 1 ? n : sum(f(difference(n, '1')), f(difference(n, '2')));. Notice that I'm checking for 0 and not '0' with a non strict equality operator.1
1The reason I'm doing n == 0 as opposed to n === '0' is because in JavaScript, '00000' == 0 but '00000' !== '0' and in my new updated difference function, without truncated leading zeros, I can't guarantee the number of zeros for a zero output. Well, actually I can. There would be as many zeros as the length of n.
100th Fibonacci - JSFiddle

Related

Trying to understand recursion in JS... check my simple code

I am trying to wrap my head around recursive functions. I've tried a number of entry level exercises with no success. For example, I put this code into JS fiddle. I intended for it to take the sum of all numbers from 1 to n.
function sumAll(n) {
if (n == 1 ) {
return 1;
} else if (n > 1) {
return sumAll(n--) + n;
}
}
console.log(sumAll(3));
feeding 3 to the function should give me '6'. I get an error, though.
The -- suffix operator will evaluate to the original value of n, and the subtraction from n will happen afterwards (which is also not desired as you still want to do + n). That means the recursive call gets the same value for n as the caller, and so you don't get closer to the end...
Don't use -- here, but -1:
function sumAll(n) {
if (n == 1 ) {
return 1;
}
else if (n > 1) {
return sumAll(n-1) + n;
}
}
console.log(sumAll(3));
Perhaps you will enjoy a repeatable technique that can guide you through designing your recursive function. Let's solve sumAll using inductive reasoning -
If n is zero, return the empty sum, zero
(inductive) n is negative or positive. If n is negative, return the negative result of the subproblem sumAll(-n)
(inductive) n is positive. Return n plus the result of the subproblem sumAll(n - 1)
function sumAll(n) {
if (n == 0) // 1
return 0
else if (n < 0) // 2
return -sumAll(-n)
else
return n + sumAll(n - 1) // 3
}
console.log(sumAll(-10), sumAll(0), sumAll(10))
// -55 0 55
Here is sumAll written using a switch instead. It behaves identically but maybe you will find the syntax nicer to read -
function sumAll(n) {
switch (true) {
case n == 0: return 0 // 1
case n < 0: return -1 * sumAll(-n) // 2
default: return n + sumAll(n - 1) // 3
}
}
console.log(sumAll(-10), sumAll(0), sumAll(10))
// -55 0 55
Here is sumAll again as a pure, functional expression -
const sumAll = (n = 0) =>
n == 0 // 1
? 0
: n < 0 // 2
? -1 * sumAll(-n)
: n + sumAll(n - 1) // 3
console.log(sumAll(-10), sumAll(0), sumAll(10))
// -55 0 55

I have made a simple calculator but How I can prevent users from entering dot several times in one number ? so far i can only prevent double dots

if
(previousNum == "." && currentNum == ".") {
screen.value = screen.value.substring(0, numchar - 1);
You are using javascript. you can split it on "." then count the length of the array which should be 2 or less.
var x = '12.34.56';
var arr = x.split(".")
var number_of_dots = arr.length - 1
console.log(number_of_dots)
I think you can check if the number is Integer or float with this sample function
function isFloat(n){
return Number(n) === n && n % 1 !== 0;
}
function isInt(n) {
return n % 1 === 0;
}
and write catch, so if had error the entered number is not valid for you calculator

Codwars: Array to single value +1

Given an array of integers of any length, return an array that has 1 added to the value represented by the array.
the array can't be empty
only non-negative, single digit integers are allowed
Return nil (or your language's equivalent) for invalid inputs.
Examples
For example the array [2, 3, 9] equals 239, adding one would return the array [2, 4, 0].
My code so far:
function upArray(arr){
let i = parseInt(arr.join('')) + 1;
return arr.some(e => typeof e !== 'number' || e < 0) ?
null : i
.toString()
.split('')
.map(e => parseInt(e));
};
It seems to pass most basic test however fails with larger inputs. Where have I gone wrong?
Just like you have converted the array into a number, you have to convert the number back into an array.
function upArray(arr){
let i = parseInt(arr.join('')) + 1;
return i.toString().split('').map(x => parseInt(x));
};
console.log(upArray([2,3,9]));
Your code won't work if the array length is greater than 100k...
Number type of javascript or any language is not enough big to handle it.
It's better if we calculate the last element with 1. If result is larger than nice ( < 10 ),
we continue to calculate next element with 1 and assign current value to 0. If result is smaller or equal 9, just assign the result to current and exit loop.
Then we print the final array as result:
pseudo code:
for i from: n-1:0
result = arr[i] + 1;
if(result < 10) :
arr[i] = result;
exit loop;// no need to continue calculate
else:
arr[i] = 0;
endif;
endfor;
You can join final array as string.
Here's probably the fastest solution (performance wise) - also there's no need to deal with BigInt, NaN, or Infinity:
function upArray(arr) {
if (!isInputIsNonEmptyArray(arr)) {
return null;
}
const isNumber = num => typeof num === 'number';
const isIntSingleDigit = num => Number.isInteger(num) && num >= 0 && num <10;
let resultArr = [];
let i = arr.length;
let num;
while (i-- > 0) {
num = arr[i];
if (!isNumber(num) || !isIntSingleDigit(num)) {
return null;
}
if (num === 9) {
resultArr[i] = 0;
if (i === 0) { //means we're in the msb/left most digit, so we need to insert 1 to the left
resultArr.unshift(1);
break; //you can leave it out really, as the next check in the while will fail anyway
}
}
else {
resultArr[i] = num + 1; //No more + 1 should be made, just check for validity
//of the rest of the input and copy to the result arr
while (--i > -1) {
num = arr[i];
if (!isNumber(num) || !isIntSingleDigit(num)) {
return null;
}
resultArr[i] = arr[i];
}
break;
}
}
return resultArr;
function isInputIsNonEmptyArray(arr) {
return Array.isArray(arr) && arr.length > 0;
}
}
If the input arg is not an array or an empty array, or if you encounter invalid element during the main while loop you return null.
In the main while loop you go from the right most element (lsd), and add 1 to it (or insert 0 if the number is 9) up the the left most digit.
If a number which is less than 9 is incremented, no need to increment any more (this is the while loop in the else clause).

Recursion counter doesn't work in codewars kata

Currently trying to complete the persistent bugger kata in code wars.
I need to return the number of times the input has to be multiplied until it is reduced to a single number (full task instructions below).
Everything appears to work apart from the count. when I console log, the number of times it logs and runs is correct, but instead of incrementing such as 1,2,3 it will be something like 2,2,4.
So instead of returning 3, it returns 4.
I try to not ask for help with katas, but this time I am completely at a loss as to why the count is firstly skipping numbers and also not incrementing.
Task:
Write a function, persistence, that takes in a positive parameter num and returns its multiplicative persistence, which is the number of times you must multiply the digits in num until you reach a single digit.
For example:
persistence(39) === 3 // because 3*9 = 27, 2*7 = 14, 1*4=4
// and 4 has only one digit
persistence(999) === 4 // because 9*9*9 = 729, 7*2*9 = 126,
// 1*2*6 = 12, and finally 1*2 = 2
persistence(4) === 0 // because 4 is already a one-digit number
My function:
function persistence(num) {
//console.log('here', num)
if(num < 10) return 0;
if(num === 25) return 2
let spl = num.toString().split('');
let result = 1;
let count = 1;
spl.forEach((s) => {
let int = parseInt(s)
result *= int;
//count++;
})
//console.log(result)
if(result > 9) {
persistence(result)
count++;
}
// console.log('count-->', count)
return count;
}
A sub issue is that the input 25 always returns a count 1 less than it should. My fix is poor I know, again any advice would be much appreciated.
Spoiler alert: this contains a solution. If you don't want that, stop before the end.
You don't really want to work with count, since as people point out, it's a local variable. You also don't work too hard to special case the result if it's a single digit. Let the recursion handle it.
Thus:
function persistence(num) {
//console.log('here', num)
if(num < 10) return 0;
//still here, must be 2 or more digits
let spl = num.toString().split('');
let result = 1;
spl.forEach((s) => {
let int = parseInt(s)
result *= int;
})
//console.log(result)
return 1 + persistence(result)
}
As you already have a complete solution posted here with fixes to your implementation, I will offer what I think is a simpler version. If we had a function to create the digit product for us, then persistence could be this simple recursion:
const persistence = (n) =>
n < 10 ? 0 : 1 + persistence (digProduct (n))
You already have code for the digit product, and while it's fine, a mathematical approach, rather than a string-base one, is somewhat cleaner. We could write it -- also recursively -- like
const digProduct = (n) =>
n < 10 ? n : (n % 10) * digProduct (Math .floor (n / 10))
or instead we might choose (n / 10) | 0 in place of the floor call. Either is reasonable.
Putting it together, we have:
const digProduct = (n) =>
n < 10 ? n : (n % 10) * digProduct ((n / 10) | 0)
const persistence = (n) =>
n < 10 ? 0 : 1 + persistence (digProduct (n))
const tests = [39, 999, 4, 25]
tests.forEach (n => console.log (`${n} --> ${persistence(n)}`))

Get Number of Decimal Places with Javascript

How would I calculate the number of decimal places (not digits) of a real number with Javascript?
function countDecimals(number) {
}
For example, given 245.395, it should return 3.
Like this:
var val = 37.435345;
var countDecimals = function(value) {
let text = value.toString()
// verify if number 0.000005 is represented as "5e-6"
if (text.indexOf('e-') > -1) {
let [base, trail] = text.split('e-');
let deg = parseInt(trail, 10);
return deg;
}
// count decimals for number in representation like "0.123456"
if (Math.floor(value) !== value) {
return value.toString().split(".")[1].length || 0;
}
return 0;
}
countDecimals(val);
The main idea is to convert a number to string and get the index of "."
var x = 13.251256;
var text = x.toString();
var index = text.indexOf(".");
alert(text.length - index - 1);
Here is a method that does not rely on converting anything to string:
function getDecimalPlaces(x,watchdog)
{
x = Math.abs(x);
watchdog = watchdog || 20;
var i = 0;
while (x % 1 > 0 && i < watchdog)
{
i++;
x = x*10;
}
return i;
}
Note that the count will not go beyond watchdog value (defaults to 20).
I tried some of the solutions in this thread but I have decided to build on them as I encountered some limitations. The version below can handle: string, double and whole integer input, it also ignores any insignificant zeros as was required for my application. Therefore 0.010000 would be counted as 2 decimal places. This is limited to 15 decimal places.
function countDecimals(decimal)
{
var num = parseFloat(decimal); // First convert to number to check if whole
if(Number.isInteger(num) === true)
{
return 0;
}
var text = num.toString(); // Convert back to string and check for "1e-8" numbers
if(text.indexOf('e-') > -1)
{
var [base, trail] = text.split('e-');
var deg = parseInt(trail, 10);
return deg;
}
else
{
var index = text.indexOf(".");
return text.length - index - 1; // Otherwise use simple string function to count
}
}
You can use a simple function that splits on the decimal place (if there is one) and counts the digits after that. Since the decimal place can be represented by '.' or ',' (or maybe some other character), you can test for that and use the appropriate one:
function countPlaces(num) {
var sep = String(23.32).match(/\D/)[0];
var b = String(num).split(sep);
return b[1]? b[1].length : 0;
}
console.log(countPlaces(2.343)); // 3
console.log(countPlaces(2.3)); // 1
console.log(countPlaces(343.0)); // 0
console.log(countPlaces(343)); // 0
Based on Gosha_Fighten's solution, for compatibility with integers:
function countPlaces(num) {
var text = num.toString();
var index = text.indexOf(".");
return index == -1 ? 0 : (text.length - index - 1);
}
based on LePatay's solution, also take care of the Scientific notation (ex: 3.7e-7) and with es6 syntax:
function countDecimals(num) {
let text = num.toString()
if (text.indexOf('e-') > -1) {
let [base, trail] = text.split('e-')
let elen = parseInt(trail, 10)
let idx = base.indexOf(".")
return idx == -1 ? 0 + elen : (base.length - idx - 1) + elen
}
let index = text.indexOf(".")
return index == -1 ? 0 : (text.length - index - 1)
}
var value = 888;
var valueLength = value.toString().length;

Categories

Resources