Out of curiosity, I want to verify that Newton is indeed faster than
bisection (for the cases it successfully converges) for solving nonlinear equations.
I implemented both out of textbook algorithms.
The function tested is:
f(x) = 5*(x-0.4)*(x^2 - 5x + 10), with a simple real root 0.4
The convergence accuracy is set to 1e-4.
Newton starts at x0 = 0.5, converges in 2 iterations.
bisection starts with an interval [0,1], converges in 14 iterations.
I use performance.now() to measure the elapsed time of both methods.
SURPRISINGLY, with many tries, Newton is always slower than bisection.
Newton time: 0.265 msec: [0.39999999988110857,2]
bisection time: 0.145 msec: [0.399993896484375,14]
I ported the program to C (visual C): Newton is a lot faster than bisection.
These numerical codes are so simple that I cannot spot any weird thing going on.
Can anyone help?
http://jsfiddle.net/jmchen/8wvhzjmn/
// Horner method for degree-n polynomial
function eval (a, t) {
// f(x) = a0+ a1x + ... + anxn
var n = a.length - 1;// degree (n)
var b = [];
var c = [];
var i, k;
for (i = 0; i <= n; i++)
b.push(0), c.push(0);
b[n] = a[n];
c[n] = b[n];
for (k = n-1; k >= 1; k--) {
b[k] = a[k] + t*b[k+1];
c[k] = b[k] + t*c[k+1];
}
b[0] = a[0] + t*b[1];
return [b[0],c[1]];
}
// simple Newton
function Newton (eval, x0, epsilon) {
var eps = epsilon || 1e-4;
var imax = 20;
for (var i = 0; i < imax; i++) {
var fdf = eval (coeff, x0);
x1 = x0 - fdf[0]/fdf[1];
if (Math.abs(x1 - x0) < eps)
break;
x0 = x1;
}
return [x1, i]; // return [approx. root, iterations]
}
// simple bisection
function bisection (func, interval, eps) {
var xLo = interval[0];
var xHi = interval[1];
fHi = func(coeff,xHi)[0]; // fb
fLo = func(coeff,xLo)[0]; // fa
if (fLo * fHi > 0)
return undefined;
var xMid, fHi, fLo, fMid;
var iter = 0;
while (xHi - xLo > eps) {
++iter;
xMid = (xLo+xHi)/2;
fMid = func(coeff,xMid)[0]; // fc
if (Math.abs(fMid) < eps)
return [xMid, iter];
else if (fMid*fLo < 0) { // fa*fc < 0 --> [a,c]
xHi = xMid;
fHi = fMid;
} else { // fc*fb < 0 --> [c,b]
xLo = xMid;
fLo = fMid;
}
}
return [(xLo+xHi)/2, iter];
}
// f(x) = 5x^3 - 27x^2 + 60x - 20
// = 5*(x-0.4)*(x^2 - 5x + 10)
var coeff = [-20,60,-27,5];
var t0 = performance.now();
var sol1 = Newton (eval, 0.5, 1e-4);
var t1 = performance.now();
var sol0 = bisection (eval, [0,1], 1e-4);
var t2 = performance.now();
console.log ('Newton time: '+ (t1-t0).toFixed(3) + ': ' + sol1);
console.log ('bisection time: '+ (t2-t1).toFixed(3) + ': ' + sol0);
There are many external factors that can influence that test, including the order in which your code gets JIT compiled, and caching. Measuring time on such a small number of iterations isn't very meaningful, as those external factors may end up being bigger than what you're trying to measure.
For example, if you invert the order so it calculates bisection before it calculates Newton, you get the opposite result.
If you want to do it better, perhaps run both once, then do a loop to run both again N times, and measure how long it takes to run that loop.
Related
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)
}
I wrote a javascript version of Lagrange algorithm, but it kept going wrong when I run it, I don't know what went wrong.
I use this to calculate time.
When I pass a cSeconds as a variable, sometimes it returns a minus value which is obviously wrong...
function LagrangeForCat(cSeconds){
var y = [2592000,7776000,15552000,31104000,93312000,155520000,279936000,404352000,528768000,622080000,715392000,870912000,995328000,1119744000,1244160000,1368576000,1492992000,1617408000,1741824000,1866240000,1990656000,2115072000,2239488000,2363904000,2488320000,2612736000,2737152000,2861568000,2985984000,3110400000,3234816000,3359232000,3483648000,3608064000];
var x = [604800,1209600,1814400,2592000,5184000,7776000,15552000,23328000,31104000,46656000,62208000,93312000,124416000,155520000,186624000,217728000,248832000,279936000,311040000,342144000,373248000,404352000,435456000,466560000,497664000,528768000,559872000,590976000,622080000,653184000,684288000,715392000,746496000,777600000];
var l = 0.0;
for (var j = 0; j < 34; j++) {
var s = 1.0;
for (var i = 0; i < 34; i++) {
if (i != j)
s = s * ((cSeconds - x[i]) / (x[j] - x[i]));
}
l = l + s * y[j];
}
var result = l / (24 * 60 * 60);
var Days = Math.floor(result);
//get float seconds data
var littleData = String(result).split(".")[1];
var floatData = parseFloat("0."+littleData);
var second = floatData *60*60*24;
var hours = Math.floor(second/(60*60));
var minutes = Math.floor(second % 3600/60);
var seconds = Math.floor(second % 3600) % 60;
var returnData = {days:Days,hours: hours + ':' + minutes + ':' + seconds}
return returnData;
}
I don't believe the issue is with your code but with the data set.
I tried a few things, for instance if you have cSeconds = one of the x values, then you get the correct result (I could check that it was equal to the matching y value).
I put all the data in open office and drew the graph it was like the square root function but more extreme (the 'straight' part look very straight) then I remembered that when you interpolate you usually get a polynomial that crosses the points you want but can be very wild outside between the point.
To test my theory I modified the algorithm to control at which x/y index to start and tried for all the values:
for (let i = 0; i < 35; ++i) {
LagrangeForCat(63119321, i, 34)
}
Together with a console.log inside LagrangeForCat it gives me the interpolated y value if I use all the x/y arrays (i=0), if I ignore the first x/y point (i=1), the first two (i=2), ...
00-34 -6850462776.278063
01-34 549996977.0003194
02-34 718950902.7592317
03-34 723883771.1443908
04-34 723161627.795225
05-34 721857113.1756063
06-34 721134873.0889213
07-34 720845478.4754647
08-34 720897871.7910147
09-34 721241470.2886044
10-34 722280314.1033486
11-34 750141284.0070543
12-34 750141262.289736
13-34 750141431.2562406
14-34 750141089.6980047
15-34 750141668.8768387
16-34 750142353.3267975
17-34 750141039.138794
18-34 750141836.251831
19-34 750138039.6240234
20-34 750141696.7529297
21-34 750141120.300293
22-34 750141960.4248047
23-34 750140874.0966797
24-34 750141337.5
25-34 750141237.4694824
26-34 750141289.2150879
27-34 750141282.5408936
28-34 750141284.2094421
29-34 750141283.987999
30-34 750141284.0002298
31-34 750141284.0000689
32-34 750141283.9999985
33-34 3608064000
34-34 0
Exclude 33-34 and 34-34 (there's just not enough data to interpolate).
For the example x=63119321 you'd expect y to be between 715392000 and 870912000 you can see that if you ignore the first 2-3 values the interpolation is "believable", if you ignore more values you interpolate based off the very straight part of the curve (see how consistent the interpolation is from 11-34 onward).
I use to work on a project where interpolation was needed, to avoid those pathological cases we opted for linear interpolation trading accuracy for security (and we could generate all the x/y points we wanted). In your case I'd try to use a smaller set, for instance only two values smaller than cSeconds and two greater like this:
function LagrangeForCat(cSeconds) {
var x = [...];
var y = [...];
let begin = 0,
end = 34
for (let i = 0; i < 34; ++i) {
if (cSeconds < x[i]) {
begin = (i < 3) ? 0 : i - 2
end = (i > (x.length - 1)) ? x.length : i + 1
break
}
}
let result = 0.0;
for (let i = begin; i < end; ++i) {
let term = y[i] / (24 * 60 * 60)
for (let j = begin; j < end; ++j) {
if (i != j)
term *= (cSeconds - x[j]) / (x[i] - x[j]);
}
result += term
}
var Days = Math.floor(result);
// I didn't change the rest of the function didn't even looked at it
}
If you find this answer useful please consider marking it as answered it'd be much appreciated.
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.
I recently tackled a coding problem. I came up with a solution to the following problem.
A non-empty zero-indexed array A consisting of N integers is given. Array A represents numbers on a tape.
Any integer P, such that 0 < P < N, splits this tape into two non-empty parts: A[0], A[1], ..., A[P − 1] and A[P], A[P + 1], ..., A[N − 1].
The difference between the two parts is the value of: |(A[0] + A[1] + ... + A[P − 1]) − (A[P] + A[P + 1] + ... + A[N − 1])|
In other words, it is the absolute difference between the sum of the first part and the sum of the second part.
For example, consider array A such that:
A[0] = 3
A[1] = 1
A[2] = 2
A[3] = 4
A[4] = 3
We can split this tape in four places:
P = 1, difference = |3 − 10| = 7
P = 2, difference = |4 − 9| = 5
P = 3, difference = |6 − 7| = 1
P = 4, difference = |10 − 3| = 7
Write a function:
function solution(A);
that, given a non-empty zero-indexed array A of N integers, returns the minimal difference that can be achieved.
For example, given:
A[0] = 3
A[1] = 1
A[2] = 2
A[3] = 4
A[4] = 3
the function should return 1, as explained above.
Assume that:
N is an integer within the range [2..100,000];
each element of array A is an integer within the range [−1,000..1,000].
Complexity:
expected worst-case time complexity is O(N);
expected worst-case space complexity is O(N), beyond input storage (not counting the storage required for input arguments).
Elements of input arrays can be modified.
The following is the feedback I obtained from testing the solution:
CORRECTNESS:
small_range range sequence, length = ~1,000 1.900 s RUNTIME ERROR
tested program terminated unexpectedly
PERFORMANCE:
Detected time complexity: O(N * N)
So I am getting one runtime error for ranges around 1000. And most importantly, I am not getting O(n). I am getting O(n * n) as I am using nested for loops.
(1) How could I fix the runtime error?
(2) How could one construct O(n) algorithm for the same problem? Any suggestions?
This is my solution:
function solution(A){
var len = A.length;
var diff = []; // Array to store the differences
var sumLeft = 0; // Sum of array elements from index 0 to index p - 1
var sumRight = 0; // Sum of array elements from index p to index n - 1
for(var p = 1; p < len; p++){
sumLeft = 0;
sumRight = 0;
// Calculate sumLeft:
for(var i = 0; i < p; i++){
sumLeft += A[i];
}
// Calculate sumRight:
for(var j = p; j < len; j++){
sumRight += A[j];
}
// Calculate differences, compute absolute values, and push into diff array:
diff.push(Math.abs(sumLeft - sumRight));
}
// Return the minimum of diff array by sorting it and returning the first element:
return bubbleSort(diff)[0];
}
function bubbleSort(array){
var len = array.length;
for(var i = 0; i < len; i++){
for(var j = i + 1; j < len; j++){
if(array[i] > array[j]){
var temp = array[i];
array[i] = array[j];
array[j] = temp;
}
}
}
return array;
}
Let me try to explain you how you can think about improving the space and time complexity of your algorithm. You realize where clearly that you're using nested for loops and it greatly increases the iterations and might also be causing the run-time error for sufficiently large inputs.
The first step should be reducing the redundancy of your operations. Now you calculate the left and right sums repeatedly for different values of p. You don't need that at all. I'll give you an example for how the algorithm will flow:
Array indices -> A [0, 1, 2, 3, ....,p ,p+1, ....n-1] for a size n array
At any point A[p] would act as a pivot as it breaks the array into two.
For p = 1, You just take the first element i.e A[0] and the right part of the sum is
A[1] + A[2] + .... A[n-1]
Let S1 = A[0] and S2 = A[1] + A[2] + .... A[n-1] for p = 1
The pivot or the break point here is A[p] i.e A[1]
Calculate the absolute difference |S1- S2| and store it in a variable min-diff
For p = 2,
S1 will simply be S1 + A[1] i.e the previous value of S1 including the last pivot
S2 = S2 - A[1], as we have moved on to the next element.
The sum of the remaining elements would not account the element we just crossed.
Formally,
S1 = S1 + A[p-1] and S2 = S2 - A[p-1]
Calculate the new difference i.e |S1 - S2| and just check
if it is smaller than the value of our variable min-diff.
If it is, update the value of min-diff with the present difference,
otherwise move on to the next element.
At any value of p, S1 represents sum of left half,
S2 represents sum of right half and
min-diff represents the minium absolute difference till now.
Complexity for this algorithm
Time complexity
The only time we calculate the sum of the elements is the first time
when we calculate A[1]+...A[n-1]. After that we just traverse the
elements of the array one by one.
So we traverse the elements of the array at max twice. So time
complexity is clearly O(N)
Space complexity
We use three extra variables i.e S1, S2 and min-diff all through
this algorithm to accumulate the sum and store the minimum absolute
difference along with the value of p and the n elements of the
array.
So space complexity of this algorithm is again O(N)
On a side note- Although you don't require sorting for this problem at all since you're to output the minimum difference only, but whenever sorting, please don't use bubble-sort at it is clearly the least efficient sorting method. You're better off with merge sort or quick sort which have a run time of O(NlogN)
I hope I was able to explain myself. Try to code this into a simple function, shouldn't take long. It should probably fix the run-time error as well.
You don't need to calculate the sum of the vector pieces when you test a new value of P. If you calculated leftSum and rightSum for both parts for P=(p-1), when you have to calculate it for P=p you just need to:
Remove array[p] from rightSum; and
Add array[p] to leftSum.
This both are O(1). If you do it (n-1) times, you are still under O(n) complexity.
Hope that helps.
Code with java : O(N)
import java.math.*;
class Solution {
public int solution(int[] A) {
long sumright = 0;
long sumleft = 0;
long ans;
for (int i =1;i<A.length;i++)
{
sumright += A[i];
}
sumleft = A[0];
ans =Math.abs(sumright+sumleft);
for (int P=1; P<A.length; P++)
{
if (Math.abs(sumleft - sumright)<ans)
{
ans = Math.abs(sumleft - sumright);
}
sumleft += A[P];
sumright -=A[P];
}
return (int) ans;
}
}
Without debugging, this solution gets a 100% task score on Codility (with 100% for both correctness and performance):
function solution(A) {
var sum_right = 0;
for (int of A.slice(1)) {
sum_right += int;
}
var sum_left = A[0];
var diff_of_sums = sum_left - sum_right;
var lowest_diff = Math.abs(diff_of_sums);
var diff_new;
// we assume the length is at least 2
if (A.length == 2) {
return lowest_diff;
}
for (var int of A.slice(1)) {
diff_new = Math.abs(sum_left - sum_right);
if (diff_new < lowest_diff) {
lowest_diff = diff_new;
}
sum_left += int;
sum_right -= int;
}
return lowest_diff;
}
With debugging:
// you can write to stdout for debugging purposes, e.g.
// console.log('this is a debug message');
function solution(A) {
var sum_right = 0;
for (int of A.slice(1)) {
sum_right += int;
}
var sum_left = A[0];
var diff_of_sums = sum_left - sum_right;
// var total = Math.abs(sum_left + sum_right);
var lowest_diff = Math.abs(diff_of_sums);
var diff_new;
// we assume the length is at least 2
if (A.length == 2) {
return lowest_diff;
}
// console.log("lowest_diff initially:", lowest_diff)
// var diff_of_sums_new = diff_of_sums;
// console.log("diff_of_sums initially:", diff_of_sums)
// console.log("A.slice(1):", A.slice(1))
for (var int of A.slice(1)) {
// console.log("lowest_diff", lowest_diff)
diff_new = Math.abs(sum_left - sum_right);
if (diff_new < lowest_diff) {
lowest_diff = diff_new;
}
sum_left += int;
sum_right -= int;
}
// if (Math.abs(sumleft - sumright)<ans)
// {
// ans = Math.abs(sumleft - sumright);
// }
// sumleft += A[P];
// sumright -=A[P];
// // console.log("int === -1:", int === -1);
// // diff_of_sums = diff_of_sums_new;
// console.log("lowest_diff =", lowest_diff);
// // console.log("A[index + 1] =", A[parseInt(index) + 1]);
// // console.log("parseInt(index) === 1", parseInt(index) === 1)
// diff_of_sums = Math.abs(lowest_diff - 2 * Math.abs(int));
// // console.log("diff_of_sums =", diff_of_sums);
// // console.log("diff_of_sums = Math.abs(diff_of_sums - 2 * A[index + 1]) = ", diff_of_sums_new);
// if (diff_of_sums < lowest_diff) {
// lowest_diff = diff_of_sums;
// // console.log("lowest_diff = diff_of_sums =", diff_of_sums_new)
// } else {
// return lowest_diff;
// }
// }
// console.log("lowest_diff before returning", lowest_diff);
return lowest_diff;
}
// Note that it's better to use test cases in Codility for this, but I've left here to show some.
// console.log("solution([-1000, 1000])", solution([-1000, 1000]));
// console.log("solution([2, 7, 20, 30, 1])", solution([2, 7, 20, 30, 1])); // sum 60, smallest diff = |29 - 31| = 2
// console.log("solution([-2, -7, -20, -30, -1])", solution([-2, -7, -20, -30, -1])); // sum -60, smallest diff = 2
// console.log("solution([-1, -1]):", solution([-1, -1]));
// console.log("solution([-2, -1]):", solution([-2, -1]));
// console.log("solution([-2, -1, -3]):", solution([-2, -1, -3]));
// console.log("solution([]):", solution([]))
Initially I tried starting from halfway, but this made the implementation more complicated. This is what I came up with before I ditched that approach (and I can't be bothered with hacking on a solution):
function solution(A) {
// const sum = A.reduce((partial_sum, a) => partial_sum + a);
// console.log(sum);
var size = A.length;
if (size % 2 == 0) {
mid = size/2;
} else {
mid = Math.floor(size/2);
}
console.log("mid initially", mid);
var sum1 = A.slice(0, mid).reduce((partial_sum, a) => partial_sum + a);
// console.log("sum1:",sum1);
var sum2 = A.slice(mid).reduce((partial_sum, a) => partial_sum + a);
// console.log("sum2:", sum2);
var sum_diff = sum1 - sum2;
// console.log("sum_diff:", sum_diff);
if (sum_diff === 0) {
return sum_diff;
}
// sum_diff = function() {Math.abs(sum2 - sum1)};
// sum_diff = sum_diff();
var lowest_diff = Math.abs(sum_diff);
var diff_negative = (sum_diff < 0);
console.log("diff_negative initially:", diff_negative)
var crossed_over = false;
var sum_diff_new;
while (diff_negative) {
mid++;
if (mid === size) {
return lowest_diff;
}
// var sum1_new = sum1 + A[mid];
// var sum2_new = sum2 - A[mid];
// sum_diff_new = sum1_new - sum2_new = sum1 - sum2 + 2*A[mid] = sum_diff - 2*A[mid];
sum_diff_new = sum_diff - 2*A[mid];
diff_negative = (sum_diff_new < 0);
if (diff_negative = false) {
crossed_over = true;
if (lowest_diff <= sum_diff_new) {
return lowest_diff;
} else {
return sum_diff_new;
}
}
}
while(!diff_negative) {
mid--;
if (mid === -1) {
return lowest_diff;
}
// var sum1_new = sum1 - A[mid];
// var sum2_new = sum2 + A[mid];
// sum_diff_new = sum1_new - sum2_new = sum1 - sum2 - 2*A[mid] = sum_diff - 2*A[mid];
console.log("sum_diff:", sum_diff);
sum_diff_new = sum_diff + 2*A[mid];
console.log("sum_diff_new:", sum_diff_new);
diff_negative = (sum_diff_new < 0);
if (diff_negative = true) {
crossed_over = true;
var sum_diff_new_pos = Math.abs(sum_diff_new);
if (lowest_diff <= sum_diff_new_pos) {
return lowest_diff;
} else {
return sum_diff_new_pos;
}
}
}
}
// Issues: doesn't work e.g. with [-2, -1, -3] and [-2, -7, -20, -30, -1]
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