Implementing Binomial DIstribution in JavaScript error? - javascript

I'm just hoping someone could double check my implementations to make sure everything is correct on the math and JS sides. I'm wondering where my implementation went wrong b/c the formulas do not compute the same probabilities, specifically probability_three seems to have a computation error I cannot find.
Here's the JSFiddle: https://jsfiddle.net/s73Lh1fk/5/
There are 3 functions I'm hoping to get reviewed.
probability_one
probability_two
probability_three
The formulas are as follows.
P(d, n) = 1 - (d - 1 / d)^n
P(d, n, o) = 1 - (d - (d - o + 1) / d)^n
P(d, n, o, k) = nCk * (p * k) * q^(n−k)
for nCk, I implemented https://www.calculatorsoup.com/calculators/discretemathematics/combinations.php
function factorialize(num) {
// Step 1. Create a variable result to store num
var result = num;
// If num = 0 OR num = 1, the factorial will return 1
if (num === 0 || num === 1)
return 1;
// Instatiate the while loop
while (num > 1) {
// Decrement by 1
num--
// Update the result
result *= num
}
// Return
return result;
}
function nCr(n, r){
// Compute n factorial
var nFact = factorialize(n)
// Compute r factorial
var rFact = factorialize(r)
// Compute n - r factorial
var nrFact = factorialize(n - r)
// Compute nCr
var result = (nFact / (rFact * nrFact))
// Return
return result
}
If you need any more information, please don't hesitate to ask. I'll do my best to provide it.
For example:
The result here is 9.75%
function probability_one() {
// Get number of dice
var n = document.getElementById("n1").value
// Get sides on each die
var d = document.getElementById("d1").value
// Calculate
var prob = 1 - ((d - 1) / d)**n
prob = parseFloat(prob*100).toFixed(2)+"%"
prob = `<B>${prob}</B>`
// Print the probability
var p = document.createElement('p')
p.innerHTML = prob
document.getElementById("output1").appendChild(p)
}
The result here is 9.75%
function probability_two() {
// Get number of dice
var n = document.getElementById("n2").value
// Get sides on each die
var d = document.getElementById("d2").value
// Get the specific outcome
var o = document.getElementById("o2").value
// Calculate
var prob = 1 - ((d - (d - o + 1)) / d)**n
prob = parseFloat(prob*100).toFixed(2)+"%"
prob = `<B>${prob}</B>`
// Print the probability
var p = document.createElement('p')
p.innerHTML = prob
document.getElementById("output2").appendChild(p)
}
The result here is 18.00%, not 9.75%
function probability_three(){
// USER INPUTS
// Get number of dice
var n = document.getElementById("n3").value
// Get sides on each die
var d = document.getElementById("d3").value
// Get the value that defines a success
var o = document.getElementById("o3").value
// Get the number of success needed
var k = document.getElementById("k3").value
// CALCULATIONS
// p
var p = ((d - o) + 1)/10
console.log(`p: ${p}`)
// q
var q = (1 - p)
console.log(`q: ${q}`)
// nCr
var vnCr = nCr(n, k)
console.log(`nCr: ${vnCr}`)
// p**k
var pk = p**k
console.log(`p**k: ${pk}`)
// q**n-k
var pnk = q**(n-k)
console.log(`q**n-k: ${pnk}`)
// Probability
var prob = (vnCr * pk * pnk) / (p + q)**n
console.log(`Prob.: ${prob}`)
prob = parseFloat(prob*100).toFixed(2)+"%"
prob = `<B>${prob}</B>`
// Print the probability
var p = document.createElement('p')
p.innerHTML = prob
document.getElementById("output3").appendChild(p)
}

Related

Am I using the proper predicate for my lambda?

I've been working on this program for a few hours, and I finally got it to output - NaN. I dont know how this could be, I'm pushing a product of real numbers into the array... Somebody help! What did I miss? The problem is to find the largest product produced by 13 adjacent digits within the 1000 digit number assigned to _1000digits.
// what is the largest product of 13 adjacent digits within this 1000 digit number
function largestProduct() {
_1000digits = 7316717653133062491922511967442657474235534919493496983520312774506326239578318016984801869478851843858615607891129494954595017379583319528532088055111254069874715852386305071569329096329522744304355766896648950445244523161731856403098711121722383113622298934233803081353362766142828064444866452387493035890729629049156044077239071381051585930796086670172427121883998797908792274921901699720888093776657273330010533678812202354218097512545405947522435258490771167055601360483958644670632441572215539753697817977846174064955149290862569321978468622482839722413756570560574902614079729686524145351004748216637048440319989000889524345065854122758866688116427171479924442928230863465674813919123162824586178664583591245665294765456828489128831426076900422421902267105562632111110937054421750694165896040807198403850962455444362981230987879927244284909188845801561660979191338754992005240636899125607176060588611646710940507754100225698315520005593572972571636269561882670428252483600823257530420752963450;
separateDigits = _1000digits.toString().split("");
products = [];
var a = 0;
var b = 1;
var c = 2;
var d = 3;
var e = 4;
var f = 5;
var g = 6;
var h = 7;
var i = 8;
var j = 9;
var k = 10;
var l = 11;
var m = 12;
while (m <= 999) {
products.push(
separateDigits[a] *
separateDigits[b] *
separateDigits[c] *
separateDigits[d] *
separateDigits[e] *
separateDigits[f] *
separateDigits[g] *
separateDigits[h] *
separateDigits[i] *
separateDigits[j] *
separateDigits[k] *
separateDigits[l] *
separateDigits[m]
);
a++;
b++;
c++;
d++;
e++;
f++;
g++;
h++;
i++;
j++;
k++;
l++;
m++;
}
products.sort((a, b) => a - b);
console.log(products.pop());
}
largestProduct();
Short:
To work with such huge numbers you'll want to use a special data structure, like BigInt.
Long:
There are a few issues with your code, the first one is trying to store such a huge number in a variable without any treatment. A JavaScript number can only store values up to 25^3 - 1, your number is a lot bigger than that.
If you run:
_1000digits = 7316717653133062491922511967442657474235534919493496983520312774506326239578318016984801869478851843858615607891129494954595017379583319528532088055111254069874715852386305071569329096329522744304355766896648950445244523161731856403098711121722383113622298934233803081353362766142828064444866452387493035890729629049156044077239071381051585930796086670172427121883998797908792274921901699720888093776657273330010533678812202354218097512545405947522435258490771167055601360483958644670632441572215539753697817977846174064955149290862569321978468622482839722413756570560574902614079729686524145351004748216637048440319989000889524345065854122758866688116427171479924442928230863465674813919123162824586178664583591245665294765456828489128831426076900422421902267105562632111110937054421750694165896040807198403850962455444362981230987879927244284909188845801561660979191338754992005240636899125607176060588611646710940507754100225698315520005593572972571636269561882670428252483600823257530420752963450
console.log(_1000digits)
You'll see the output is "Infinity" because that's such a huge number JavaScript doesn't know how to store it entirely.
You're also not checking if the numbers you're accessing actually exist, so if you put a smaller number in _1000digits you'll end up multiplying by undefined, which will result in NaN:
_1000digits = 700
separateDigits = _1000digits.toString().split("")
var f = 5
console.log(separateDigits[f])

How to make a function that will calculate the area under the curve of the graph using node.js?

I need to make the function so that it calculate the area when I enter function f and coefficients a, b and n on the command line.
For now I have this:
module.exports = function (f,a,b,n,next) {
parseFloat(a);
parseFloat(b);
parseInt(n);
var h = (b-a)/n;
var s = 0;
var suma = function () {
for (var i = 0; i <= n; i++) {
var ximj = a+h*(i-1);
var xi = a+h*i;
var x = (ximj + xi)/2;
s += eval(f,x);
}
return s;
};
if (n<1) {
next(new Error("Koeficijent n je broj ekvidistantnih točaka."))
}
else next(null, {povrsina : function () {return h*suma();}}
);
I think that the function suma() isn't working like it should be working.
In command line this should be look like :
f: x*x-2*x+7
a: 2
b: 4
n: 10
Povrsina ispod grafa funkcije f je 20.66000...
My prompt file looks like this:
var pov = require('./integrator.js');
var prompt = require('prompt');
prompt.get (['f','a','b','n'],function (err,koef) {
if (err) return console.log(err);
console.log("f: ",koef.f);
console.log("a: ",koef.a);
console.log("b: ",koef.b);
console.log("n: ",koef.n);
parseFloat(koef.a);
parseFloat(koef.b);
parseInt(koef.n);
pov(koef.f,koef.a,koef.b,koef.n,function(err,rj){
if (err)
console.log(err);
else
console.log("Povrsina ispod grafa funkcije f je " + rj.povrsina());
});
Thank you for help :)
First of all, if you want to calculate the area under a curve you are going to need to provide a lower and upper x limit, otherwise the answer will always be infinite. Personally I use this function that I wrote:
function findArea(lowerLimit, upperLimit, coefficients) {
let lower = 0
let upper = 0
for (let i = 0; i < coefficients.length; i++) {
lower += (coefficients[i] * Math.pow(lowerLimit, i + 1)) / (i + 1)
upper += (coefficients[i] * Math.pow(upperLimit, i + 1)) / (i + 1)
}
return Math.abs(upper - lower)
}
If you pass in two numbers as the limits and an array of coefficients in the format, a + bx + cx^2 + dx^3 ... and so on, you will get the correct answer. For example, say you have the equation y = 5 + 4x + 3x^2 + 2x^3 and you want to find the area under the curve between x = 1 and x = 3 you can call this function like so, findArea(1, 3, [5, 4, 3, 2]) and you will get the answer 92 which represents the number of units squared. Keep in mind that this works only if the area you are evaluating is either entirely above or entirely below the x axis. If your area crosses the x axis you will need to calculate the area above and below separately!
You can check the results against the Symbolab Area Under The Curve Calculator but this should work for any curve.
The eval function reads a string and evaluates it. If you want to evaluate the function f with the parameter x, then you can just write f(x) instead of calling eval(). This is because, in JavaScript, functions are first-class values and can be passed around just like numbers, strings, and so on.
In suma, particularly the line var ximj = a+h*(i-1);, your for-loop multiplies h by -1, then by 0, then by 1. I suspect you meant var ximj = a+h*(i+1); since h is the width of your differential. But that would only produce small errors.
As Lends wrote, you also need to correct your Parse* calls:
a = parseFloat(a);
b = parseFloat(b);
n = parseInt(n);
You should use math.js to eval a string math expression, for example:
var math = require('mathjs');
var integral = function(f, a, b, n, next) {
a = parseFloat(a);
b = parseFloat(b);
n = parseInt(n);
var h = (b - a) / n;
var s = 0;
var suma = function() {
for (var i = 0; i <= n; i++) {
var ximj = a + h * (i - 1);
var xi = a + h * i;
var x = (ximj + xi) / 2;
s += math.eval(f, { x: x });
}
return s;
};
if (n < 1) {
next(new Error("Koeficijent n je broj ekvidistantnih točaka."))
} else {
next(null, { povrsina: function() { return h * suma(); } });
}
};
var f = 'x * x - 2 * x + 7';
var a = '2';
var b = '4';
var n = '10000';
integral(f, a, b, n, function(err, rj) {
if (err)
console.log(err);
else
console.log("Povrsina ispod grafa funkcije f je " + rj.povrsina());
});
/* output:
Povrsina ispod grafa funkcije f je 20.66806662000201
*/
You integral function seems to have an has an issue though, n must be very high to get near the expected result (like 10000). Or is it supposed to work that way? For n = 10, it gives 22 for example.
Another solution is to use an existing package or at least read the source code to get an idea.

ACE Text Editor displays text characters in place of spaces

I've written the following Javascript:
(function () {
var computationModule = (function foo1(stdlib, foreign, heap) {
"use asm";
var sqrt = stdlib.Math.sqrt,
heapArray = new stdlib.Int32Array(heap),
outR = 0.0,
outI = 0.0;
function computeRow(canvasWidth, canvasHeight, limit, max, rowNumber, minR, maxR, minI, maxI) {
canvasWidth = +canvasWidth;
canvasHeight = +canvasHeight;
limit = +limit;
max = max | 0;
rowNumber = +rowNumber;
minR = +minR;
maxR = +maxR;
minI = +minI;
maxI = +maxI;
var columnNumber = 0.0,
zReal = 0.0,
zImaginary = 0.0,
numberToEscape = 0;
var columnNumberInt = 0;
// Compute the imaginary part of the numbers that correspond to pixels in this row.
zImaginary = +(((maxI - minI) * +rowNumber) / +canvasHeight + minI);
// Iterate over the pixels in this row.
// Compute the number of iterations to escape for each pixel that will determine its color.
for (columnNumber = +0; + columnNumber < +canvasWidth; columnNumber = +(+columnNumber + 1.0)) {
// Compute the real part of the number for this pixel.
zReal = +(((maxR - minR) * +columnNumber) / +canvasWidth + minR);
numberToEscape = howManyToEscape(zReal, zImaginary, max, limit) | 0;
columnNumberInt = columnNumberInt + 1 | 0;
heapArray[(columnNumberInt * 4) >> 2] = numberToEscape | 0;
}
}
// Function to determine how many iterations for a point to escape.
function howManyToEscape(r, i, max, limit) {
r = +r;
i = +i;
max = max | 0;
limit = +limit;
var j = 0,
ar = 0.0,
ai = 0.0;
ar = +r;
ai = +i;
for (j = 0;
(j | 0) < (max | 0); j = (j + 1) | 0) {
iteratingFunction(ar, ai, r, i)
ar = outR;
ai = outI;
if (+(ar * ar + ai * ai) >= +(limit * limit))
return j | 0;
}
return j | 0;
}
// The iterating function defining the fractal to draw
// r and i are the real and imaginary parts of the value from the previous iteration
// r0 and i0 are the starting points
function iteratingFunction(r, i, r0, i0) {
r = +r;
i = +i;
r0 = +r0;
i0 = +i0;
computePower(r, i, 2);
// Set the output from this function to t
outR = +(r0 + outR);
outI = +(i0 + outI);
}
// Compute the result of [r,i] raised to the power n.
// Place the resulting real part in outR and the imaginary part in outI.
function computePower(r, i, n) {
// Tell asm.js that r, i are floating point and n is an integer.
r = +r;
i = +i;
n = n | 0;
// Declare and initialize variables to be numbers.
var rResult = 0.0;
var iResult = 0.0;
var j = 0;
var tr = 0.0;
var ti = 0.0;
// Declare and initialize variables that will be used only in the
// event we need to compute the reciprocal.
var abs = 0.0;
var recr = 0.0;
var reci = 0.0;
if ((n | 0) < (0 | 0)) {
// For n less than 0, compute the reciprocal and then raise it to the opposite power.
abs = +sqrt(r * r + i * i);
recr = r / abs;
reci = -i / abs;
r = recr;
i = reci;
n = -n | 0;
}
rResult = r;
iResult = i;
for (j = 1;
(j | 0) < (n | 0); j = (j + 1) | 0) {
tr = rResult * r - iResult * i;
ti = rResult * i + iResult * r;
rResult = tr;
iResult = ti;
}
outR = rResult;
outI = iResult;
} // end computePower
return {
computeRow: computeRow
};
})(self, foreign, heap);
// Return computationModule that we just defined.
return computationModule;
})();
There's nothing particularly unusual about this Javascript, except that I want to make my web application display the Javascript in an ACE text editor (http://ace.c9.io/) so that the user can modify the code at runtime.
I load the Javascript using jQuery AJAX and then set the contents of the ACE Editor to the Javascript code. After the user modifies the code, he can click a button to run the code. (This uses eval)
The problem I'm having is that ACE is displaying strange characters instead of spaces.
Oddly enough, if I try to copy text from the ACE editor, the strange characters disappear and the spaces are normal. Furthermore, the code runs fine even with these strange characters being displayed.
I also noticed that the problem does not appear when using Firefox, but it appears for Chrome and IE 11.
Finally, the problem only occurs when I put the code on a real web server. It doesn't reproduce in my development environment.
Looking at this some more, I can see that it's not just the text I'm loading via AJAX. Even when I type new spaces, more text characters appear!
What could be going wrong so that the text doesn't display properly?
Here's a link to the application: http://danielsadventure.info/html5fractal/
Use charset="utf-8" in the script tag where you include ace.
<script src="path/to/ace.js" charset="utf-8">
This may have something to do with this:
When no explicit charset parameter is provided by the sender, media
subtypes of the "text" type are defined to have a default charset
value of "ISO-8859-1" when received via HTTP. Data in character sets
other than "ISO-8859-1" or its subsets MUST be labeled with an
appropriate charset value. See section 3.4.1 for compatibility
problems.
http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.7.1
So the string passed to the script are in an encoding different than what ACE (or JS in general) expects, which is UTF-8.

is there a JavaScript implementation of the Inverse Error Function, akin to MATLAB erfinv()?

is there a JavaScript implementation of the Inverse Error Function?
This would implement the Gauss inverse error function. Approximations are ok.
Why yes. There is.
The following code uses built-in JavaScript functions and implments Abramowitz and Stegun's algorithm as described here:
function erfinv(x){
var z;
var a = 0.147;
var the_sign_of_x;
if(0==x) {
the_sign_of_x = 0;
} else if(x>0){
the_sign_of_x = 1;
} else {
the_sign_of_x = -1;
}
if(0 != x) {
var ln_1minus_x_sqrd = Math.log(1-x*x);
var ln_1minusxx_by_a = ln_1minus_x_sqrd / a;
var ln_1minusxx_by_2 = ln_1minus_x_sqrd / 2;
var ln_etc_by2_plus2 = ln_1minusxx_by_2 + (2/(Math.PI * a));
var first_sqrt = Math.sqrt((ln_etc_by2_plus2*ln_etc_by2_plus2)-ln_1minusxx_by_a);
var second_sqrt = Math.sqrt(first_sqrt - ln_etc_by2_plus2);
z = second_sqrt * the_sign_of_x;
} else { // x is zero
z = 0;
}
return z;
}
function provided earlier in this post did not work for me... NaN result on a 33meter circle with confidence 65% represented as 65.0 ... I wrote the following based on an equation listed here https://en.wikipedia.org/wiki/Error_function#Inverse_functions and it worked fine:
var _a = ((8*(Math.PI - 3)) / ((3*Math.PI)*(4 - Math.PI)));
function erfINV( inputX )
{
var _x = parseFloat(inputX);
var signX = ((_x < 0) ? -1.0 : 1.0 );
var oneMinusXsquared = 1.0 - (_x * _x);
var LNof1minusXsqrd = Math.log( oneMinusXsquared );
var PI_times_a = Math.PI * _a ;
var firstTerm = Math.pow(((2.0 / PI_times_a) + (LNof1minusXsqrd / 2.0)), 2);
var secondTerm = (LNof1minusXsqrd / _a);
var thirdTerm = ((2 / PI_times_a) + (LNof1minusXsqrd / 2.0));
var primaryComp = Math.sqrt( Math.sqrt( firstTerm - secondTerm ) - thirdTerm );
var scaled_R = signX * primaryComp ;
return scaled_R ;
}
Here's an alternative implementation of Abramowitz and Stegun's algorithm (equivalent to ptmalcolm's answer, but more succinct and twice as fast):
function erfinv(x) {
// maximum relative error = .00013
const a = 0.147
//if (0 == x) { return 0 }
const b = 2/(Math.PI * a) + Math.log(1-x**2)/2
const sqrt1 = Math.sqrt( b**2 - Math.log(1-x**2)/a )
const sqrt2 = Math.sqrt( sqrt1 - b )
return sqrt2 * Math.sign(x)
}
You can test the speed with console.time("erfinv"); for (let i=0; i<1000000000; i++) {erfinv(i/1000000000)}; console.timeEnd("erfinv")
The if statement optimization is commented out as it doesn't seem to make a difference - presumably the interpreter recognizes that this is all one equation.
If you need a more accurate approximation, check out Wikipedia.

Javascript Fibonacci nth Term Optimization

I've become interested in algorithms lately, and the fibonacci sequence grabbed my attention due to its simplicity.
I've managed to put something together in javascript that calculates the nth term in the fibonacci sequence in less than 15 milliseconds after reading lots of information on the web. It goes up to 1476...1477 is infinity and 1478 is NaN (according to javascript!)
I'm quite proud of the code itself, except it's an utter monster.
So here's my question:
A) is there a faster way to calculate the sequence?
B) is there a faster/smaller way to multiply two matrices?
Here's the code:
//Fibonacci sequence generator in JS
//Cobbled together by Salty
m = [[1,0],[0,1]];
odd = [[1,1],[1,0]];
function matrix(a,b) {
/*
Matrix multiplication
Strassen Algorithm
Only works with 2x2 matrices.
*/
c=[[0,0],[0,0]];
c[0][0]=(a[0][0]*b[0][0])+(a[0][1]*b[1][0]);
c[0][1]=(a[0][0]*b[0][1])+(a[0][1]*b[1][1]);
c[1][0]=(a[1][0]*b[0][0])+(a[1][1]*b[1][0]);
c[1][1]=(a[1][0]*b[0][1])+(a[1][1]*b[1][1]);
m1=(a[0][0]+a[1][1])*(b[0][0]+b[1][1]);
m2=(a[1][0]+a[1][1])*b[0][0];
m3=a[0][0]*(b[0][1]-b[1][1]);
m4=a[1][1]*(b[1][0]-b[0][0]);
m5=(a[0][0]+a[0][1])*b[1][1];
m6=(a[1][0]-a[0][0])*(b[0][0]+b[0][1]);
m7=(a[0][1]-a[1][1])*(b[1][0]+b[1][1]);
c[0][0]=m1+m4-m5+m7;
c[0][1]=m3+m5;
c[1][0]=m2+m4;
c[1][1]=m1-m2+m3+m6;
return c;
}
function fib(n) {
mat(n-1);
return m[0][0];
}
function mat(n) {
if(n > 1) {
mat(n/2);
m = matrix(m,m);
}
m = (n%2<1) ? m : matrix(m,odd);
}
alert(fib(1476)); //Alerts 1.3069892237633993e+308
The matrix function takes two arguments: a and b, and returns a*b where a and b are 2x2 arrays.
Oh, and on a side note, a magical thing happened...I was converting the Strassen algorithm into JS array notation and it worked on my first try! Fantastic, right? :P
Thanks in advance if you manage to find an easier way to do this.
Don't speculate, benchmark:
edit: I added my own matrix implementation using the optimized multiplication functions mentioned in my other answer. This resulted in a major speedup, but even the vanilla O(n^3) implementation of matrix multiplication with loops was faster than the Strassen algorithm.
<pre><script>
var fib = {};
(function() {
var sqrt_5 = Math.sqrt(5),
phi = (1 + sqrt_5) / 2;
fib.round = function(n) {
return Math.floor(Math.pow(phi, n) / sqrt_5 + 0.5);
};
})();
(function() {
fib.loop = function(n) {
var i = 0,
j = 1;
while(n--) {
var tmp = i;
i = j;
j += tmp;
}
return i;
};
})();
(function () {
var cache = [0, 1];
fib.loop_cached = function(n) {
if(n >= cache.length) {
for(var i = cache.length; i <= n; ++i)
cache[i] = cache[i - 1] + cache[i - 2];
}
return cache[n];
};
})();
(function() {
//Fibonacci sequence generator in JS
//Cobbled together by Salty
var m;
var odd = [[1,1],[1,0]];
function matrix(a,b) {
/*
Matrix multiplication
Strassen Algorithm
Only works with 2x2 matrices.
*/
var c=[[0,0],[0,0]];
var m1=(a[0][0]+a[1][1])*(b[0][0]+b[1][1]);
var m2=(a[1][0]+a[1][1])*b[0][0];
var m3=a[0][0]*(b[0][1]-b[1][1]);
var m4=a[1][1]*(b[1][0]-b[0][0]);
var m5=(a[0][0]+a[0][1])*b[1][1];
var m6=(a[1][0]-a[0][0])*(b[0][0]+b[0][1]);
var m7=(a[0][1]-a[1][1])*(b[1][0]+b[1][1]);
c[0][0]=m1+m4-m5+m7;
c[0][1]=m3+m5;
c[1][0]=m2+m4;
c[1][1]=m1-m2+m3+m6;
return c;
}
function mat(n) {
if(n > 1) {
mat(n/2);
m = matrix(m,m);
}
m = (n%2<1) ? m : matrix(m,odd);
}
fib.matrix = function(n) {
m = [[1,0],[0,1]];
mat(n-1);
return m[0][0];
};
})();
(function() {
var a;
function square() {
var a00 = a[0][0],
a01 = a[0][1],
a10 = a[1][0],
a11 = a[1][1];
var a10_x_a01 = a10 * a01,
a00_p_a11 = a00 + a11;
a[0][0] = a10_x_a01 + a00 * a00;
a[0][1] = a00_p_a11 * a01;
a[1][0] = a00_p_a11 * a10;
a[1][1] = a10_x_a01 + a11 * a11;
}
function powPlusPlus() {
var a01 = a[0][1],
a11 = a[1][1];
a[0][1] = a[0][0];
a[1][1] = a[1][0];
a[0][0] += a01;
a[1][0] += a11;
}
function compute(n) {
if(n > 1) {
compute(n >> 1);
square();
if(n & 1)
powPlusPlus();
}
}
fib.matrix_optimised = function(n) {
if(n == 0)
return 0;
a = [[1, 1], [1, 0]];
compute(n - 1);
return a[0][0];
};
})();
(function() {
var cache = {};
cache[0] = [[1, 0], [0, 1]];
cache[1] = [[1, 1], [1, 0]];
function mult(a, b) {
return [
[a[0][0] * b[0][0] + a[0][1] * b[1][0],
a[0][0] * b[0][1] + a[0][1] * b[1][1]],
[a[1][0] * b[0][0] + a[1][1] * b[1][0],
a[1][0] * b[0][1] + a[1][1] * b[1][1]]
];
}
function compute(n) {
if(!cache[n]) {
var n_2 = n >> 1;
compute(n_2);
cache[n] = mult(cache[n_2], cache[n_2]);
if(n & 1)
cache[n] = mult(cache[1], cache[n]);
}
}
fib.matrix_cached = function(n) {
if(n == 0)
return 0;
compute(--n);
return cache[n][0][0];
};
})();
function test(name, func, n, count) {
var value;
var start = Number(new Date);
while(count--)
value = func(n);
var end = Number(new Date);
return 'fib.' + name + '(' + n + ') = ' + value + ' [' +
(end - start) + 'ms]';
}
for(var func in fib)
document.writeln(test(func, fib[func], 1450, 10000));
</script></pre>
yields
fib.round(1450) = 4.8149675025003456e+302 [20ms]
fib.loop(1450) = 4.81496750250011e+302 [4035ms]
fib.loop_cached(1450) = 4.81496750250011e+302 [8ms]
fib.matrix(1450) = 4.814967502500118e+302 [2201ms]
fib.matrix_optimised(1450) = 4.814967502500113e+302 [585ms]
fib.matrix_cached(1450) = 4.814967502500113e+302 [12ms]
Your algorithm is nearly as bad as uncached looping. Caching is your best bet, closely followed by the rounding algorithm - which yields incorrect results for big n (as does your matrix algorithm).
For smaller n, your algorithm performs even worse than everything else:
fib.round(100) = 354224848179263100000 [20ms]
fib.loop(100) = 354224848179262000000 [248ms]
fib.loop_cached(100) = 354224848179262000000 [6ms]
fib.matrix(100) = 354224848179261900000 [1911ms]
fib.matrix_optimised(100) = 354224848179261900000 [380ms]
fib.matrix_cached(100) = 354224848179261900000 [12ms]
There is a closed form (no loops) solution for the nth Fibonacci number.
See Wikipedia.
There may well be a faster way to calculate the values but I don't believe it's necessary.
Calculate them once and, in your program, output the results as the fibdata line below:
fibdata = [1,1,2,3,5,8,13, ... , 1.3069892237633993e+308]; // 1476 entries.
function fib(n) {
if ((n < 0) || (n > 1476)) {
** Do something exception-like or return INF;
}
return fibdata[n];
}
Then, that's the code you ship to your clients. That's an O(1) solution for you.
People often overlook the 'caching' solution. I once had to write trigonometry routines for an embedded system and, rather than using infinite series to calculate them on the fly, I just had a few lookup tables, 360 entries in each for each of the degrees of input.
Needless to say, it screamed along, at the cost of only about 1K of RAM. The values were stored as 1-byte entries, [actual value (0-1) * 16] so we could just do a lookup, multiply and bit shift to get the desired value.
My previous answer got a bit crowded, so I'll post a new one:
You can speed up your algorithm by using vanilla 2x2 matrix multiplication - ie replace your matrix() function with this:
function matrix(a, b) {
return [
[a[0][0] * b[0][0] + a[0][1] * b[1][0],
a[0][0] * b[0][1] + a[0][1] * b[1][1]],
[a[1][0] * b[0][0] + a[1][1] * b[1][0],
a[1][0] * b[0][1] + a[1][1] * b[1][1]]
];
}
If you care for accuracy and speed, use the caching solution. If accuracy isn't a concern, but memory consumption is, use the rounding solution. The matrix solution only makes sense if you want results for big n fast, don't care for accuracy and don't want to call the function repeatedly.
edit: You can even further speed up the computation if you use specialised multiplication functions, eliminate common subexpressions and replace the values in the existing array instead of creating a new array:
function square() {
var a00 = a[0][0],
a01 = a[0][1],
a10 = a[1][0],
a11 = a[1][1];
var a10_x_a01 = a10 * a01,
a00_p_a11 = a00 + a11;
a[0][0] = a10_x_a01 + a00 * a00;
a[0][1] = a00_p_a11 * a01;
a[1][0] = a00_p_a11 * a10;
a[1][1] = a10_x_a01 + a11 * a11;
}
function powPlusPlus() {
var a01 = a[0][1],
a11 = a[1][1];
a[0][1] = a[0][0];
a[1][1] = a[1][0];
a[0][0] += a01;
a[1][0] += a11;
}
Note: a is the name of the global matrix variable.
Closed form solution in JavaScript: O(1), accurate up for n=75
function fib(n){
var sqrt5 = Math.sqrt(5);
var a = (1 + sqrt5)/2;
var b = (1 - sqrt5)/2;
var ans = Math.round((Math.pow(a, n) - Math.pow(b, n))/sqrt5);
return ans;
}
Granted, even multiplication starts to take its expense when dealing with huge numbers, but this will give you the answer. As far as I know, because of JavaScript rounding the values, it's only accurate up to n = 75. Past that, you'll get a good estimate, but it won't be totally accurate unless you want to do something tricky like store the values as a string then parse those as BigIntegers.
How about memoizing the results that where already calculated, like such:
var IterMemoFib = function() {
var cache = [1, 1];
var fib = function(n) {
if (n >= cache.length) {
for (var i = cache.length; i <= n; i++) {
cache[i] = cache[i - 2] + cache[i - 1];
}
}
return cache[n];
}
return fib;
}();
Or if you want a more generic memoization function, extend the Function prototype:
Function.prototype.memoize = function() {
var pad = {};
var self = this;
var obj = arguments.length > 0 ? arguments[i] : null;
var memoizedFn = function() {
// Copy the arguments object into an array: allows it to be used as
// a cache key.
var args = [];
for (var i = 0; i < arguments.length; i++) {
args[i] = arguments[i];
}
// Evaluate the memoized function if it hasn't been evaluated with
// these arguments before.
if (!(args in pad)) {
pad[args] = self.apply(obj, arguments);
}
return pad[args];
}
memoizedFn.unmemoize = function() {
return self;
}
return memoizedFn;
}
//Now, you can apply the memoized function to a normal fibonacci function like such:
Fib = fib.memoize();
One note to add is that due to technical (browser security) constraints, the arguments for memoized functions can only be arrays or scalar values. No objects.
Reference: http://talideon.com/weblog/2005/07/javascript-memoization.cfm
To expand a bit on Dreas's answer:
1) cache should start as [0, 1]
2) what do you do with IterMemoFib(5.5)? (cache[5.5] == undefined)
fibonacci = (function () {
var FIB = [0, 1];
return function (x) {
if ((typeof(x) !== 'number') || (x < 0)) return;
x = Math.floor(x);
if (x >= FIB.length)
for (var i = FIB.length; i <= x; i += 1)
FIB[i] = FIB[i-1] + FIB[i-2];
return FIB[x];
}
})();
alert(fibonacci(17)); // 1597 (FIB => [0, 1, ..., 1597]) (length = 17)
alert(fibonacci(400)); // 1.760236806450138e+83 (finds 18 to 400)
alert(fibonacci(1476)); // 1.3069892237633987e+308 (length = 1476)
If you don't like silent errors:
// replace...
if ((typeof(x) !== 'number') || (x < 0)) return;
// with...
if (typeof(x) !== 'number') throw new TypeError('Not a Number.');
if (x < 0) throw new RangeError('Not a possible fibonacci index. (' + x + ')');
Here is a very fast solution of calculating the fibonacci sequence
function fib(n){
var start = Number(new Date);
var field = new Array();
field[0] = 0;
field[1] = 1;
for(var i=2; i<=n; i++)
field[i] = field[i-2] + field[i-1]
var end = Number(new Date);
return 'fib' + '(' + n + ') = ' + field[n] + ' [' +
(end - start) + 'ms]';
}
var f = fib(1450)
console.log(f)
I've just written my own little implementation using an Object to store already computed results. I've written it in Node.JS, which needed 2ms (according to my timer) to calculate the fibonacci for 1476.
Here's the code stripped down to pure Javascript:
var nums = {}; // Object that stores already computed fibonacci results
function fib(n) { //Function
var ret; //Variable that holds the return Value
if (n < 3) return 1; //Fib of 1 and 2 equal 1 => filtered here
else if (nums.hasOwnProperty(n)) ret = nums[n]; /*if the requested number is
already in the object nums, return it from the object, instead of computing */
else ret = fib( n - 2 ) + fib( n - 1 ); /* if requested number has not
yet been calculated, do so here */
nums[n] = ret; // add calculated number to nums objecti
return ret; //return the value
}
//and finally the function call:
fib(1476)
EDIT: I did not try running this in a Browser!
EDIT again: now I did. try the jsfiddle: jsfiddle fibonacci Time varies between 0 and 2ms
Much faster algorithm:
const fib = n => fib[n] || (fib[n-1] = fib(n-1)) + fib[n-2];
fib[0] = 0; // Any number you like
fib[1] = 1; // Any number you like

Categories

Resources