JS create an array with unique random numbers - javascript

Full code looks like this, ideally we have 4 div boxes that need to be randomly filled with random numbers ansValue, one of them (rightAnsValue with its rightAnsId) is already done and works fine, I've managed to make it unique in comparison to others (code without commented section). But met a problem with making others unique, I keep having some identical values in my boxes. In comments is one way I tried to solve this, but pretty sure there is a much simpler and smarter solution that actually works. I would appreciate if you could help to find an understandable solution to this problem.
(P.S. I've seen similar questions but they are either too dificult or done without JS.)
function createAnswers(){
for(ansId=1; ansId<5; ansId++){
if(ansId!=rightAnsId){
for(i=1; i<10; i++){
digitArray[i-1] = i;
}
genNewRandNum();
// ansArray.length = 3;
// ansArray.push(ansValue);
// for(k=0; k<3; k++){
// if(ansArray[k] == ansArray[k+1] || ansArray[k] == ansArray[k+2]){
// genNewRandNum();
// ansArray[k] = ansValue;
// }else if(ansArray[k+1] == ansArray[k+2]){
// genNewRandNum();
// ansArray[k+1] = ansValue;
// }else{
// break;
// }
// }
if(ansValue!=rightAnsValue){
document.getElementById("box" + ansId).innerHTML = ansValue;
}else{
genNewRandNum();
document.getElementById("box" + ansId).innerHTML = ansValue;
}
}
}
}
The way I generate new numbers:
function genNewRandNum(){
rand1 = digitArray[Math.floor(Math.random() * digitArray.length)];
rand2 = digitArray[Math.floor(Math.random() * digitArray.length)];
ansValue = rand1 * rand2;
}

Replace your genNewRandNum() with below code. I have used IIFE to create a closure variable alreadyGeneratedNumbers thats available inside the function generateRandomNumber() thats returned.
So everytime genNewRandNum() is executed, it checks against alreadyGeneratedNumbers to make sure it always returns a unique between 1 and 9.
var genNewRandNum = (function(){
var alreadyGeneratedNumbers = {};
return function generateRandomNumber() {
var min = Math.ceil(1),
max = Math.floor(9);
randomNumber = Math.floor(Math.random() * (max - min + 1)) + min;
if(alreadyGeneratedNumbers[randomNumber]) {
return generateRandomNumber();
} else {
alreadyGeneratedNumbers[randomNumber] = randomNumber;
return randomNumber;
}
}
})();
console.log(genNewRandNum());
console.log(genNewRandNum());
console.log(genNewRandNum());
console.log(genNewRandNum());
console.log(genNewRandNum());
console.log(genNewRandNum());
console.log(genNewRandNum());
console.log(genNewRandNum());
console.log(genNewRandNum());
Note: If you call genNewRandNum() for the 10th time it will throw error. So if you have a use case where you would need to reset it after all numbers from 1 to 9 are returned, then you need to add code to handle that

The easiest way to brute-force this is to use accept/reject sampling. You can do something like so:
uniqueRandomNumbers = function(n, nextRandom)
{
var nums = {}; var m = 0;
while(m < n)
{
var r = nextRandom();
if(! nums.hasOwnProperty(r))
{
nums[r] = true; m++;
}
}
return Object.keys(nums);
}
Here I'm using the fact that js objects are implemented as hashmaps to get a hashset. (This has the downside of converting the numbers to strings, but if you're not planning on imediately doing arithmetic with them this is not a problem.)
In order to get four unique integers between 0 and 9 you can then do something like:
uniqueRandomNumbers(4, function() { return Math.floor(Math.random() * 10); })
If you want something a little better than brute force (which probably isn't relevant to your use case but could help someone googling this), one option is to go through each element and either take or leave it with an appropriate probability. This approach is outlined in the answers to this question.

Related

Fibonacci for large numbers in Javascript

I have the following code:
function fib(n) {
let first=BigInt(0);
let snd=BigInt(1);
let currentNumber;
let countMax=Math.abs(n)+1;
let counter=2;
if(n==0){
return first;
}
else if (n==1||n==-1){
return snd;
}
while(counter<countMax)
{
currentNumber=first+snd;
first=snd;
snd=currentNumber;
counter++;
}
if((n<0) && (n % 2 ==0))
{
return -currentNumber;
}
return currentNumber;
}
That returns the fibonacci number for the given (n).
My issue is that I have to improve the performance of this code. I tried to use different fibonacci formulas (exponential ones) but I lose a lot of precision cause phi number has infinite decimals, so I have to truncate and for big numbers I lost a lot of precision.
When I execute for instance fib(200000) I get the huge number but the code spends more than 12000 ms.
For other hand I tried using recursion but the performance decreases.
Could you provide me an article or clue to follow?
Thanks & Regards.
First of all, you can refer the answer here which says that
Fib(-n) = -Fib(n)
Here's the recursive implementation which is not efficient as you mentioned
function fib(n) {
// This is to handle positive and negative numbers
var sign = n >= 0 ? 1 : -1;
n = Math.abs(n);
// Now the usual Fibonacci function
if(n < 2)
return sign*n;
return sign*(fib(n-1) + fib(n-2));
}
This is pretty straightforward and I leave it without explaining because if you know Fibonacci series, you know what the above code does. As you already know, this is not good for very large numbers as it recursively calculate the same thing again and again. But we'll use it in our approach later on.
Now coming towards a better approach. See the below code similar to your code just a bit concise.
function fib(n) {
if(n == 0)
return 0;
var a = 1;
var b = 1;
while(n > 2) {
b = a + b;
a = b - a;
}
// If n is negative then return negative of fib(n)
return n < 0 ? -1*b : b;
}
This code is better to use when you want to call this function only a few times. But if you want to call it for frequently, then you'll end up calculating the same thing many times. Here you should keep track of already calculated values.
For example, if you call fib(n) it will calculate nth Fibonacci number and return it. For the next time if you call fib(n) it will again calculate it and return the result.
What if we store this value somewhere and next time retrieve it whenever required?
This will also help in calculating Fibonacci numbers greater than nth Fibonacci number.
How?
Say we have to calculate fib(n+1), then by definition fib(n+1) = fib(n) + fib(n-1). Because, we already have fib(n) calculated and stored somewhere we can just use that stored value. Also, if we have fib(n) calculated and stored, we already have fib(n-1) calculated and stored. Read it again.
We can do this by using a JavaScript object and the same recursive function we used above (Yes, the recursive one!). See the below code.
// This object will store already calculated values
// This should be in the global scope or atleast the parent scope
var memo = {};
// We know fib(0) = 0, fib(1) = 1, so store it
memo[0] = 0;
memo[1] = 1;
function fib(n) {
var sign = n >= 0 ? 1 : -1;
n = Math.abs(n);
// If we already calculated the value, just use the same
if(memo[n] !== undefined)
return sign*memo[n];
// Else we will calculate it and store it and also return it
return sign*(memo[n] = fib(n-1) + fib(n-2));
}
// This will calculate fib(2), fib(3), fib(4) and fib(5).
// Why it does not calculate fib(0) and fib(1) ?
// Because it's already calculated, goto line 1 of this code snippet
console.log(fib(5)); // 5
// This will not calculate anything
// Because fib(-5) is -fib(5) and we already calculated fib(5)
console.log(fib(-5)); // -5
// This will also not calculate anything
// Because we already calculated fib(4) while calculating fib(5)
console.log(fib(4)); // 3
// This will calculate only fib(6) and fib(7)
console.log(fib(7)); // 13
Try out some test cases. It's easy to understand why this is faster.
Now you know you can store the already calculated values, you can modify your solution to use this approach without using recursion as for large numbers the recursive approach will throw Uncaught RangeError. I leave this to you because it's worth trying on your own!
This solution uses a concept in programming called Dynamic Programming. You can refer it here.
If you just add the previous value to the current one and then use the old current value as the previous one you get a significant improvement in performance.
function fib(n) {
var current = 1;
var previous = 0;
while (--n) {
var temp = current;
current += previous;
previous = temp;
}
return current;
}
console.log(fib(1)); // 1
console.log(fib(2)); // 1
console.log(fib(3)); // 2
console.log(fib(4)); // 3
console.log(fib(5)); // 5
You can also use an array in the parent scope to store the previous values to avoid redoing the same calculations.
var fibMap = [1, 1];
function fib(n) {
var current = fibMap[fibMap.length - 1];
var previous = fibMap[fibMap.length - 2];
while (fibMap.length < n) {
var temp = current;
current += previous;
previous = temp;
fibMap.push(current);
}
return fibMap[n - 1];
}
console.log(fib(1)); // 1
console.log(fib(2)); // 1
console.log(fib(3)); // 2
console.log(fib(4)); // 3
console.log(fib(5)); // 5
Benchmark for getting the 1000th number 3 times

How to find the missing next character in the array?

I have an array of characters like this:
['a','b','c','d','f']
['O','Q','R','S']
If we see that, there is one letter is missing from each of the arrays. First one has e missing and the second one has P missing. Care to be taken for the case of the character as well. So, if I have a huge Object which has all the letters in order, and check them for the next ones, and compare?
I am totally confused on what approach to follow! This is what I have got till now:
var chars = ("abcdefghijklmnopqrstuvwxyz"+"abcdefghijklmnopqrstuvwxyz".toUpperCase()).split("");
So this gives me with:
["a","b","c","d","e","f","g","h","i","j","k","l","m",
"n","o","p","q","r","s","t","u","v","w","x","y","z",
"A","B","C","D","E","F","G","H","I","J","K","L","M",
"N","O","P","Q","R","S","T","U","V","W","X","Y","Z"]
Which is awesome. Now my question is, how do I like check for the missing character in the range? Some kind of forward lookup?
I tried something like this:
Find the indexOf starting value in the source array.
Compare it with each of them.
If the comparison failed, return the one from the original array?
I think that a much better way is to check for each element in your array if the next element is the next char:
function checkMissingChar(ar) {
for (var i = 1; i < ar.length; i++) {
if (ar[i].charCodeAt(0) == ar[i-1].charCodeAt(0)+1) {
// console.log('all good');
} else {
return String.fromCharCode(ar[i-1].charCodeAt(0)+1);
}
}
return true;
}
var a = ['a','b','c','d','f']
var b = ['O','Q','R','S']
console.log(checkMissingChar(a));
console.log(checkMissingChar(b));
Not that I start to check the array with the second item because I compare it to the item before (the first in the Array).
Forward Look-Ahead or Negative Look-Ahead: Well, my solution would be some kind of that. So, if you see this, what I would do is, I'll keep track of them using the Character's Code using charCodeAt, instead of the array.
function findMissingLetter(array) {
var ords = array.map(function (v) {
return v.charCodeAt(0);
});
var prevOrd = "p";
for (var i = 0; i < ords.length; i++) {
if (prevOrd == "p") {
prevOrd = ords[i];
continue;
}
if (prevOrd + 1 != ords[i]) {
return String.fromCharCode(ords[i] - 1);
}
prevOrd = ords[i];
}
}
console.log(findMissingLetter(['a','b','c','d','f']));
console.log(findMissingLetter(['O','Q','R','S']));
Since I come from a PHP background, I use some PHP related terms like ordinal, etc. In PHP, you can get the charCode using the ord().
As Dekel's answer is better than mine, I'll try to propose somewhat more better answer:
function findMissingLetter (ar) {
for (var i = 1; i < ar.length; i++) {
if (ar[i].charCodeAt(0) != ar[i-1].charCodeAt(0)+1) {
return String.fromCharCode(ar[i-1].charCodeAt(0)+1);
}
}
return true;
}
var a = ['a','b','c','d','f']
var b = ['O','Q','R','S']
console.log(findMissingLetter(a));
console.log(findMissingLetter(b));
Shorter and Sweet.

Attempt to filter array items that were recently used

Array.prototype.spil = function (x,max) {
if (this.length===max) {
this.shift();
this.push(x);
} else {
this.push(x);
}
return this;
}
var heard = [0],
freash = [];
$('#speak').bind('touchstart', function() {
var sounds = [
1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,
21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40
];
var temp = sounds;
for (var i=0; i<heard.length; i++) {
temp.splice(temp.indexOf(heard[i]), 1);
}
freash = temp;
var say = Math.floor(Math.random() * freash.length+1) + 1;
heard.spil(say,10);
say = document.getElementById('c'+say); // audio element
// use the sound...
});
My attempt to make the sound that plays 'an un-recent one' is not working! I want to make the last sound that played not play again for 10 button clicks.
heard is the array of sounds that you have already heard, this has a Array.spil function which will fill the array via push until it reaches its max length of ten when it will shift then push instead.
freash is the array of sounds with no members of the heard array present.
You can see that heard and freash are declared outside of the click (touchstart) scope.
If I click/tap over-and-over sometimes I am hearing sounds that have been played already much sooner than I am suppose to, often following each other.
This has been bothering me for days but, I can't see the problem in my logic. Can you?
I think this line
var say = Math.floor(Math.random() * freash.length+1) + 1;
should be
var say = freash[Math.floor(Math.random() * freash.length)];
Also consider the shortcut
var say = freash[Math.random() * freash.length | 0];

Trouble pushing to an array in JS

Below is just a section of my code but I know it's problematic because I can't get it to return any value except 'undefined'. I have been over this for hours and cannot figure it out.
I want to be able to input a number and have its factors pushed to an array. I have tested it by alerting the first item in the array and I get nothing. I'm sure this is a pretty easy but I just can't figure it out. Here is the code:
var numberInQuestion = prompt("Of what number are you wanting to find the largest prime factor?");
//determine factors and push to array for later use
var factorsArray = [];
function factors(numberInQuestion){
for(var i = 2; i < numberInQuestion-1; i++){
if(numberInQuestion % i === 0){
return factorsArray.push[i];
} else {
continue;
}
}
};
factors(numberInQuestion);
alert(factorsArray[0]);
Thanks for any help!
you can only return one value
you must use (), not [] for calling push
factorsArray should be local to factors (put the definition inside the function)
the else { continue; } is useless
Here is the fully corrected code:
var numberInQuestion = prompt("Of what number are you wanting to find the factors of?");
//determine factors
function factors(numberInQuestion){
var factorsArray = []; // make it local
for (var i = 2; i < numberInQuestion-1; i++){
if(numberInQuestion % i === 0){
factorsArray.push(i); // use (), and don't return here
} // no need for else { continue; } because it's a loop anyway
}
return factorsArray; // return at the end
};
var result = factors(numberInQuestion); // assign the result to a variable
alert(result);
Here's a JSFiddle.
You have an error in your pushing syntax. Correct syntax for pushing is -
factorsArray.push(i);
Also returning immediately from the function after finding the first divisor will not give you the full list. You probably want to return after you've found out all the divisors.
Taking all of the above into consideration, you should rewrite your function as follow -
function factors(numberInQuestion){
for(var i = 2; i < numberInQuestion - 1; i++){
if(numberInQuestion % i === 0) {
factorsArray.push(i);
}
}
}
and you will be OK.
You've coded this so that when you find the first factor your function returns immediately. Just get rid of the return keyword in that statement. (What "return" means in JavaScript and other similar languages is to immediately exit the function and resume from where the function was called.)
Oh, also, you call functions (like .push()) with parentheses, not square brackets.
The function should not return when pushing to the array. Return the array after executing the loop. The else clause is also unnecessary.
var numberInQuestion = prompt("Of what number are you wanting to find the largest prime factor?");
function factors(numberInQuestion){
var factorsArray = [];
for(var i = 2; i < numberInQuestion-1; i++){
if(numberInQuestion % i === 0 && isPrime(i)){
factorsArray.push(i);
}
}
return factorsArray;
};
var factors = factors(numberInQuestion);
alert(factors[factors.length-1]);
//From: http://stackoverflow.com/questions/11966520/how-to-find-prime-numbers
function isPrime (n)
{
if (n < 2) return false;
var q = Math.sqrt (n);
for (var i = 2; i <= q; i++)
{
if (n % i == 0)
{
return false;
}
}
return true;
}
Given the purpose of the example two items must be considered
The code does not determine if the number is actually prime. The code will return the smallest factor possible since the loop starts at two and increments, then returns the first element in the array. The largest factor would actually be the last element in the array. I have corrected the example to find the greatest prime factor. You can test it via this fiddle: http://jsfiddle.net/whKGB/1/

Javascript: Math.floor(Math.random()*array.length) not producing a random number?

This on e is a doozey.
I have while loop to generate a random number that is not the same as any other random number produced before. The random number is used to select a text value from an object.
for example:
quoteArray[1] = "some text"
quoteArray[2] = "some different text"
quoteArray[3] = "text again"
quoteArray[4] = "completely different text"
quoteArray[5] = "ham sandwich"
This is part of a larger function and after that function has cycled through = quoteArray.length it resets and starts the cycle over again. The issue I am hitting is that the following code is SOMETIMES producing an infinite loop:
//Note: at this point in the function I have generated a random number once already and stored it in 'randomnumber'
//I use this while statement to evaluate 'randomnumber' until the condition of it NOT being a number that has already been used and NOT being the last number is met.
while(randomnumber === rotationArray[randomnumber] || randomnumber === lastnumber){
randomnumber = Math.floor(Math.random() * (quoteArray.length));
}
When I console.log(randomnumber) - when I am stuck in the loop - I am just getting '0' as a result. When stuck in the loop it doesn't appear as though Math.floor(Math.random() * (quoteArray.length)) is producing a random number but rather just '0' infinitely.
can anyone tell me why I am running into this problem?
EDIT: Here is the complete pertinent code with function + variable declarations
// Function to initialize the quoteObj
function quoteObj(text,cname,ccompany,url,height) {
this.text=text;
this.cname=cname;
this.ccompany=ccompany;
this.url=url;
this.height=height;
}
// Populate my quotes Object with the quotation information from the XML sheet.
var qObj = new quoteObj('','','','');
var quoteArray = new Array();
var counter = 0;
//cycles through each XML item and loads the data into an object which is then stored in an array
$.ajax({
type: "GET",
url: "quotes.xml",
dataType: "xml",
success: function(xml) {
$(xml).find('quote').each(function(){
quoteArray[counter] = new quoteObj('','','','');
console.log(quoteArray[counter]);
quoteArray[counter].text = $(this).find('text').text();
quoteArray[counter].cname = $(this).find('customer_name').text();
quoteArray[counter].ccompany = $(this).find('customer_company').text();
quoteArray[counter].url = $(this).find('project').text();
++counter;
});
}
});
// This is the setion that is generating my infinite loop issue.
// I've included all of the other code in case people are wondering specific things about how an item was initialized, etc.
// Generate a random first quote then randomly progress through the entire set and start all over.
var randomnumber = Math.floor(Math.random() * (quoteArray.length));
var rotationArray = new Array(quoteArray.length);
var v = 0;
var lastnumber = -1;
bHeight = $('#rightbox').height() + 50;
var cHeight = 0;
var divtoanim = $('#customerquotes').parent();
//NOT RELATED//
// Give the innershadow a height so that overflow hidden works with the quotations.
$(divtoanim).css({'height' : bHeight});
// Rotate the Quotations Randomly function.
setInterval(function(){
randomnumber = Math.floor(Math.random() * (quoteArray.length));
//checks to see if the function loop needs to start at the beginning.
if(v == (quoteArray.length)){
rotationArray.length = 0;
v = 0;
}
//determines if the random number is both different than any other random number generated before and that is is not the same as the last random number
while(randomnumber === rotationArray[randomnumber] || randomnumber === lastnumber){
randomnumber = Math.floor(Math.random() * (quoteArray.length));
}
lastnumber = randomnumber;
rotationArray[randomnumber] = randomnumber;
++v;
//NOT RELATED//
//animation sequence
$('#ctext, #cname').animate({'opacity':'0'},2000, function(){
$('#ctext').html(quoteArray[randomnumber].text);
$('#cname').html('- ' + quoteArray[randomnumber].cname);
cHeight = $('#customerquotes').height() + 50;
adjustHeight(bHeight,cHeight,divtoanim);
$('#ctext').delay(500).animate({'opacity':'1'},500);
$('#cname').delay(1500).animate({'opacity':'1'},500);
});
},15000);
This is an asynchronous problem: the array quoteArray is empty when the code runs, because it fires off the ajax request, and moves on. Anything that depends on quoteArray should be inside the success function of $.ajax.
The array has a length when you type quoteArray.length in the console, only because by that time the Ajax request has completed.
have you tried something like
Math.floor(Math.random() * (5));
To make sure the array length is being found properly?
First, since you updated your question, be sure that you are handling asynchronous data properly. Since an ajax call is asynchronous, you will need to be sure to only run the randomizer once the call is successful and data has been returned.
Second, assuming you are handling the asyc data properly, the size of your result set is likely it is too small. Thus, you are probably randomly getting the same number too often. Then, you can't use this number because you have already done so.
What you need to do is pop off the parts that are already used from the results array each time. Recalculate the array length, then pull a random from that. However, the likelihood of this feeling random is very slim.
There is probably a more efficient way to do this, but here's my go:
var results = ['some text','some text2','some text3','some text4','some text5', /* ...etc */ ],
randomable = results;
function getRandomOffset( arr )
{
var offset,
len;
if( arr.length < 1 )
return false;
else if( arr.length > 1 )
offset = Math.floor(Math.random() * arr.length );
else
offset = 0;
arr.splice( offset, 1 );
return [
offset,
arr
];
}
while( res = getRandomOffset( randomable ) )
{
// Set the randomable for next time
randomable = res[1];
// Do something with your resulting index
results[ res[0] ];
}
The arrgument sent to the function should be the array that is returned form it (except the first time). Then call that function as you need until it returns false.

Categories

Resources