How to randomize the order of buttons in a div? - javascript

I'm making a education website and I need some help.
I have this code:
challenge1 = num1*num2;
challenge2 = num1*2+Math.floor(Math.random() *3) + 1;;
challenge3 = num1*num2+Math.floor(Math.random() *9) + 1;
challenge4 = num2+Math.floor(Math.random() *9) + 1;
makeButton(challenge1);
makeButton(challenge2);
makeButton(challenge3);
makeButton(challenge4);
function makeButton(number) {
btncount = btncount+1;
/* create a button */
var b = document.createElement('button');
b.setAttribute('class', 'pure-button');
b.setAttribute('onclick', `check(${number})`);
b.setAttribute('id', btncount);
b.textContent = number;
var buttons = document.getElementById("buttons");
buttons.appendChild(b);
}
<div id='buttons' class="pure-button-group" role="group"></div>
and it works but it's very easy to spot that the correct answer will always be the first button.
Is there a way to randomize the order of those? Thanks.

It's about shuffling a list. you should create an array of elements first(without appending them to the parent), shuffle it, and then parent.append(...list).
You can find out how to shuffle a list here:
How to randomize (shuffle) a JavaScript array?

following your snippet you can swap first and last randomly
let btncount = 0;
function makeButton(number) {
btncount = btncount+1;
/* create a button */
var b = document.createElement('button');
b.setAttribute('class', 'pure-button');
b.setAttribute('onclick', `check(${number})`);
b.setAttribute('id', btncount);
b.textContent = number;
var buttons = document.getElementById("buttons");
buttons.appendChild(b);
//randomly swap first and last
if(Math.round(Math.random())) {
// swap first and last
buttons.appendChild(buttons.firstElementChild);
}
}
const num1 = 1, num2 = 2;
const challenge1 = num1*num2;
const challenge2 = num1*2+Math.floor(Math.random() *3) + 1;;
const challenge3 = num1*num2+Math.floor(Math.random() *9) + 1;
const challenge4 = num2+Math.floor(Math.random() *9) + 1;
makeButton(challenge1);
makeButton(challenge2);
makeButton(challenge3);
makeButton(challenge4);
<div id="buttons">
</div>

You could make an array of challenges and sort this array with a custom comparator:
challenges = [
num1*num2,
num1*2+Math.floor(Math.random() *3) + 1,
num1*num2+Math.floor(Math.random() *9) + 1,
num2+Math.floor(Math.random() *9) + 1
]
function randomizer () { return Math.random() > 0.5 }
challenges.sort(randomizer)
for (challenge of challenges) makeButton(challenge)
EDIT
To have a better randomization you can make a list of object with a random index and sort it normally:
challenges = [
{ challenge: num1*num2, index: Math.random() },
{ challenge: num1*2+Math.floor(Math.random() *3) + 1, index: Math.random() },
{ challenge: num1*num2+Math.floor(Math.random() *9) + 1, index: Math.random() },
{ challenge: num2+Math.floor(Math.random() *9) + 1, index: Math.random() }
]
function compare(a,b) { return a.index == b.index ? 0 : a.index > b.index ? 1 : -1 }
challenges.sort(compare)
for (i in challenges) makeButton(challenges[i].challenge)

Put the "challenges" into an array and shuffle it. You can shuffle in plain javascript as described in this answer, but I prefer to shuffle with rando.js, like this:
console.log( randoSequence(["a", "b", "c"]) );
<script src="https://randojs.com/2.0.0.js"></script>
Apply that shuffle to your buttons, and you have this:
var num1 = 2;
var num2 = 4;
var btncount = 0;
challenge1 = num1 * num2;
challenge2 = num1 * 2 + Math.floor(Math.random() * 3) + 1;;
challenge3 = num1 * num2 + Math.floor(Math.random() * 9) + 1;
challenge4 = num2 + Math.floor(Math.random() * 9) + 1;
//------------CHANGED PART------------
var shuffledChallenges = randoSequence([challenge1, challenge2, challenge3, challenge4]);
makeButton(shuffledChallenges[0].value);
makeButton(shuffledChallenges[1].value);
makeButton(shuffledChallenges[2].value);
makeButton(shuffledChallenges[3].value);
//--------END OF CHANGED PART.--------
function makeButton(number) {
btncount = btncount + 1;
/* create a button */
var b = document.createElement('button');
b.setAttribute('class', 'pure-button');
b.setAttribute('onclick', `check(${number})`);
b.setAttribute('id', btncount);
b.textContent = number;
var buttons = document.getElementById("buttons");
buttons.appendChild(b);
}
<script src="https://randojs.com/2.0.0.js"></script>
<div id='buttons' class="pure-button-group" role="group"></div>

Related

Change chances of picking a random string

I would like Anna to have 67% chance of being picked randomly, Bob to have a 30% change, and Tom to have a 3% chance. Is there a simpler way to do this?
This is what I have so far:
var nomes = ['Anna', 'Bob', 'Tom'];
var name = nomes[Math.ceil(Math.random() * (nomes.length - 1))];
console.log(name);
Based on this Stack Overflow I think the following code will work for you:
function randomChoice(p) {
let rnd = p.reduce((a, b) => a + b) * Math.random();
return p.findIndex(a => (rnd -= a) < 0);
}
function randomChoices(p, count) {
return Array.from(Array(count), randomChoice.bind(null, p));
}
const nomes = ['Anna', 'Bob', 'Tom'];
const selectedIndex = randomChoices([0.67, 0.3, 0.03], nomes);
console.log(nomes[selectedIndex]);
// Some testing to ensure that everything works as expected:
const odds = [0, 0, 0];
for (let i = 0; i < 100000; i++) {
const r = randomChoices([0.67, 0.3, 0.03], nomes);
odds[r] = odds[r] + 1;
}
console.log(odds.map(o => o / 1000));
Here's a short solution for you:
function pick(){
var rand = Math.random();
if(rand < .67) return "Anna";
if(rand < .97) return "Bob";
return "Tom";
}
console.log( pick() );

Create a function that returns unique random numbers and assigns that number to an individual variable

I am just getting started in programming and ran into this issue. The answers posted on the forum don't really help me out so here goes my question.
I am trying to create a function that generates four random numbers between 1-12. Once the numbers are generated, they are assigned to a individual variable.
var gem1;
var gem2;
var gem3;
var gem4;
function updateGem() {
gem1 = Math.floor(Math.random() * 12 + 1);
gem2 = Math.floor(Math.random() * 12 + 1);
gem3 = Math.floor(Math.random() * 12 + 1);
gem4 = Math.floor(Math.random() * 12 + 1);
}
As you can see sometimes the numbers generated are matching, which is not what I need. I need every var to have it's own unique number.
The value of gem 1 is 8
The value of gem 2 is 9
The value of gem 3 is 9
The value of gem 4 is 8
You could do something like this:
function generateRandomUnique(size) {
const unique = new Set();
while (unique.size < size) {
unique.add(Math.floor(Math.random() * 12 + 1));
}
return [...unique];
}
const [gem1, gem2, gem3, gem4] = generateRandomUnique(4);
console.log(gem1, gem2, gem3, gem4);
Store the variables in an array and check for the number in the array
var gem1;
var gem2;
var gem3;
var gem4;
var arr=[];
function updateGem() {
arr=[];
gem1 = Math.floor(Math.random() * 12 + 1);
if(arr.includes(gem1))
updateGem();
else
arr.push(gem1);
gem2 = Math.floor(Math.random() * 12 + 1);
if(arr.includes(gem2))
updateGem();
else
arr.push(gem2);
gem3 = Math.floor(Math.random() * 12 + 1);
if(arr.includes(gem3))
updateGem();
else
arr.push(gem3);
gem4 = Math.floor(Math.random() * 12 + 1);
if(arr.includes(gem4))
updateGem();
else
arr.push(gem4);
}
updateGem()
console.log(gem1)
console.log(gem2)
console.log(gem3)
console.log(gem4)
This is what I did to solve the issue:
function updateGem() {
var numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12];
numbers.sort(() => {
return 0.5 - Math.random();
});
gem1 = numbers[0];
gem2 = numbers[1];
gem3 = numbers[2];
gem4 = numbers[3];
}
Generate a random number based on an array length. Swap with the last index and now again generate a number based on array length reducing by 1.
var numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12];
var count = numbers.length;
function updateGem() {
gem1 = getRandomNumber();
gem2 = getRandomNumber();
gem3 = getRandomNumber();
gem4 = getRandomNumber();
console.log(gem1, gem2, gem3, gem4);
}
function getRandomNumber() {
if (count <= 0) {
count = numbers.length;
}
var randomIndex = Math.round(count * Math.random());
console.log("Random Number="+randomIndex);
var temp = numbers[randomIndex];
numbers[randomIndex] = numbers[count-1];
numbers[count-1] = temp;
count--; // next time do not generate this random number
return temp;
}

JavaScript - Generate a random number between 1 and 5, but never the same number twice consecutively

I have some simple code to generate a random number between 1 and 5.
Depending on what number is pulled, a certain sound file will play. However, how could I prevent this simple program from generating the same number twice in a row consequently making the same audio to play twice?
For example: a roll of the dice is never the same twice consecutively, meaning you cannot roll a 2 if you just rolled a 2 etc.
I was experimenting with if else statements and kind of backed myself into a corner. I know this may seem like a simple problem but I'm at my wits end on this one.
<!DOCTYPE html>
<html>
<body>
<p>Click the button to display a random number between 1 and 5.</p>
<button onclick="myFunction()">ROLL DICE</button>
<p id="demo"></p>
<script>
function myFunction() {
var x = Math.floor((Math.random() * 5) + 1);
document.getElementById("demo").innerHTML = x;
}
</script>
</body>
</html>
you need to get a new number and compare with the old one, if it's the same one, get a new one and repeat. a little recursuion could help here!
let lastNumber // start with undefined lastNumber
function getRandNumber() {
var x = Math.floor((Math.random() * 5) + 1); // get new random number
if (x === lastNumber) { // compare with last number
return getRandNumber() // if they are the same, call the function again to repeat the process
}
return x // if they're not the same, return it
}
function myFunction() {
const number = getRandNumber()
lastNumber = number
document.getElementById("demo").innerHTML = number;
}
How about creating a list to pick from with the last number removed?
lastNumber = 0; // Initialize it to a number outside the list so first pick is from full five.
sourceList = [1,2,3,4,5];
function noRepeatRandom(){
nextList = sourceList.filter(function(x){return x!=lastNumber});
randomIndex = Math.floor(Math.random() * nextList.length);
lastNumber = nextList[randomIndex]
return lastNumber
}
This should pick from the full five for the first call.
var lastRoll = null;
function myFunction() {
var thisRoll = lastRoll;
while (thisRoll === lastRoll) {
thisRoll = Math.floor((Math.random() * 5) + 1);
}
document.getElementById("demo").innerHTML = thisRoll;
lastRoll = thisRoll;
}
<p>Click the button to display a random number between 1 and 5.</p>
<button onclick="myFunction()">ROLL DICE</button>
<p id="demo"></p>
var curr = 0;
function myFunction() {
var x = Math.floor((Math.random() * 5) + 1);
if(x == curr) {
myFunction();
}
else {
curr = x;
document.getElementById("demo").innerHTML = x;
}
}
This should do it if you don't mind using a global variable.
Demo: http://jsfiddle.net/jkrb837p/1/
<!DOCTYPE html>
<html>
<body>
<p>Click the button to display a random number between 1 and 5.</p>
<button onclick="myFunction()">ROLL DICE</button>
<span id="previousNumber" style="display:none"></span>
<p id="demo"></p>
<script>
function myFunction() {
var x = getRandomNum();
var temp = document.getElementById("previousNumber").innerHTML;
while(x == temp){
x = getRandomNum();
}
document.getElementById("demo").innerHTML = x;
document.getElementById("previousNumber").innerHTML = x;
}
function getRandomNum(){
var x = Math.floor((Math.random() * 5) + 1);
return x;
}
</script>
Simple math trick to exclude excessive random calls:
In the first time generate random in range 1..5
In the next times call random in range 1..4, add it to the current value and use modulo operation - to preserve result range 1..5 but exclude current value
Code (note it uses x in range 0..4 but output is shifted by one to simplify math)
var s = ""
var x = Math.floor((Math.random() * 5));
for (i = 0; i < 40; i++) {
s = s + (x + 1) + " "
x = (x + Math.floor((Math.random() * 4) + 1 )) % 5;
}
print(s)
1 4 3 2 1 5 4 5 2 1 4 2 4 5 1 4 3 1 4 5 2 1 3 5 2 4 5 3 1 5 3 5 4 5 1 5 3 5 4 2
Here is a function that you can use. It's quite simple:
min: minimum range;
max: maximum range;
length: how many numbers to generate.
Function and usage:
const randomUniqueNumbers = (min, max, length=1) =>
{
const limit = max-min+1;
if (min > max || max < min){
throw new Error(`Parameter "min" has to be smaller than "max" and vice-versa.`)
}
else if (length > limit){
throw new Error(`The length between ${min} and ${max} cannot exceed ${limit}.`)
}
let uniqueNumbers = [];
let number;
for(var i=0; i<length; i++){
do
number = Math.floor(Math.random() * limit) + min;
while(uniqueNumbers.indexOf(number) !== -1);
uniqueNumbers[i] = number;
}
return uniqueNumbers;
}
console.log(randomUniqueNumbers(20, 30, 3)); //[24, 27, 26]
console.log(randomUniqueNumbers(100, 1000, 5)) //[865, 438, 798, 247, 232]
console.log(randomUniqueNumbers(1, 5, 5)) //[5, 2, 1, 3, 4]

How can i make sure each 2 random colors are used two times?

Guys i want to get 2 random colors and make background-color for my 4 divs from those 2 selected colors.
What i want is how can i make sure each that 2 colors are used as background-color 2 times.
(In my code sometimes i see one random color as background-color 3 times.)
JSFIDDLE
$(function() {
function getRandomArrayElements(arr, count) {
var shuffled = arr.slice(0),
i = arr.length,
min = i - count,
temp, index;
while (i-- > min) {
index = Math.floor((i + 1) * Math.random());
temp = shuffled[index];
shuffled[index] = shuffled[i];
shuffled[i] = temp;
}
return shuffled.slice(min);
}
var randomColor1 = '#' + (Math.random() * 0xFFFFFF << 0).toString(16);
var randomColor2 = '#' + (Math.random() * 0xFFFFFF << 0).toString(16);
var colors = [randomColor1, randomColor2];
$(".first").css("background-color", getRandomArrayElements(colors, 1));
$(".second").css("background-color", getRandomArrayElements(colors, 1));
$(".third").css("background-color", randomColor1);
$(".fourth").css("background-color", randomColor2);
});
div {
width: 100px;
height: 100px;
border: solid;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="first"></div>
<div class="second"></div>
<div class="third"></div>
<div class="fourth"></div>
How about this:
function removeRandomElement(colors) {
return colors.splice(Math.floor(Math.random() * colors.length), 1)[0];
}
var colors = [randomColor1, randomColor1, randomColor2, randomColor2];
$(".first").css("background-color", removeRandomElement(colors));
$(".second").css("background-color", removeRandomElement(colors));
$(".third").css("background-color", removeRandomElement(colors));
$(".fourth").css("background-color", removeRandomElement(colors));
Even this answer comes a bit later I want to tell you about the conceptual mistake in your code above.
You have mixed 2 random picks and 2 fix picks. So it can happen that 3 colors can be the same.
// Possible picks: color1 | color2
$(".first").css("background-color", getRandomArrayElements(colors, 1));
// Possible picks: color1 | color2
$(".second").css("background-color", getRandomArrayElements(colors, 1));
// Fix pick: color1
$(".third").css("background-color", randomColor1);
// Fix pick: color2
$(".fourth").css("background-color", randomColor2);
This is why it can happen that 3 colors could be the same. So you can either remove the picked colors above (like the previous answer was), or put all 4 colors in an array, shuffle it and pick one by one. (like in the following example)
$(function() {
// Code from this SO question:
// http://stackoverflow.com/questions/6274339/how-can-i-shuffle-an-array-in-javascript
function shuffle(array) {
let counter = array.length;
// While there are elements in the array
while (counter > 0) {
// Pick a random index
let index = Math.floor(Math.random() * counter);
// Decrease counter by 1
counter--;
// And swap the last element with it
let temp = array[counter];
array[counter] = array[index];
array[index] = temp;
}
return array;
}
var randomColor1 = '#' + (Math.random() * 0xFFFFFF << 0).toString(16);
var randomColor2 = '#' + (Math.random() * 0xFFFFFF << 0).toString(16);
var colors = shuffle([randomColor1, randomColor2, randomColor1, randomColor2]);
var elements = ['.first','.second','.third','.fourth'];
elements.forEach(function(selector, index) {
$(selector).css('background-color', colors[index]);
});
});
div {
width: 100px;
height: 100px;
border: solid;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class="first"></div>
<div class="second"></div>
<div class="third"></div>
<div class="fourth"></div>
function getRandomArrayElements(arr, count) {
var shuffled = arr.slice(0),
i = arr.length,
min = i - count,
temp, index,color;
while (i-- > min) {
index = Math.floor((i + 1) * Math.random());
temp = shuffled[index];
shuffled[index] = shuffled[i];
shuffled[i] = temp;
color = shuffled.slice(min);
if(arr.length == getRandomArrayElements["time"].length)
{
break;
}
if(getRandomArrayElements["time"][color] != undefined)
{
i++;
}
else
{
getRandomArrayElements["time"][color] = "1"
}
}
return color;
}
getRandomArrayElements["time"] = [];

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