I have a question regarding javascript Math.random():
I have (for a game I'm building) to randomly generate every number from a given set (i.e. from 0 to 1000) and every time I have to generate a number, I have to check if that number has already been "generated".
The solution is pretty easy thinking about a simple algorithm that checks if the random integer is already present in the generated set. It loops generating numbers until it can't find it.
A snippet below:
/* ... */
for(var i = 0; i<upperBound; i++){
var randN = Math.floor(Math.random()*upperBound);
while(myRandomNumbers.contains(randN)){
loops++;
randN = Math.floor(Math.random()*upperBound);
}
myRandomNumbers.push(randN);
}
/* ... */
running example here
I'd like to know: is this the best way to achieve this? or are there any ways, instead of looping until it generates a "good" number, to exclude a particular set in the random generation?
Thanks a lot everyone!
Generate the set of numbers in order.
Sort the list randomly.
Here's an example using a naive, biased sort:
for (var nums=[],i=0;i<1000;++i) nums[i]=i+1;
nums.sort(function(){ return Math.random()-0.5 });
Then you can just pop() numbers off of nums to get the next 'random' number, guaranteed to never have been used before.
If your range of number is not prohibitively large, you could simply generate a list with all the numbers, randomise it, then pick it off one by one.
Here's a quick hack of your sample implementation to show this method in action: http://www.jsfiddle.net/ZTLt9/8/
I'd create an array and randomly shuffle it:
function shuffle(arr) {
var shuffled = arr.slice(0), i = arr.length, temp, index;
while (i--) {
index = Math.floor(i * Math.random());
temp = shuffled[index];
shuffled[index] = shuffled[i];
shuffled[i] = temp;
}
return shuffled;
}
// Create the array
var i = 1000, arr = [];
while (i--) arr[i] = i;
// Shuffle it
arr = shuffle(arr);
What about an array of booleans 1001 big.
When you want to check of the number has been generated, all you need to do is check the number at that position in the array:
if (!arr[randomnumber]){
arr[randomnumber] = true;
}
At the end, you can scan the array to find the numbers you need.
This has the added side effect of sorting your numbers, as the scan will pick them out in order.
Something similar to this was the subject of one of my blog posts:
http://www.jameswiseman.com/blog/2010/05/27/generate-and-sort-lottery-numbers/
The best way would probably be to generate an array of numbers, then shuffle it using the Fisher-Yates shuffle.
Here is the JavaScript example given in the Wikipedia article:
var n = a.length;
for(var i = n - 1; i > 0; i--) {
var j = Math.floor(Math.random() * (i + 1));
var tmp = a[i];
a[i] = a[j];
a[j] = tmp;
}
(This assumes you have an array 'a' that contains the items you wish to shuffle.)
Related
I'm trying to solve some 'hackerrank.com' coding challenges.
I'm stuck on this one:
You will be given an array of integers arr and a single integer k. You must create an array arr' of length k from elements of arr such that its unfairness is minimized.
Unfairness is defined as max(arr') - min(arr')
The function should return the minimum possible unfairness
My code works fine for the majority of test cases. However, in three of those test cases - the ones where the size of arr and k is particularly huge - fail due to an excession of the given time limit.
How can I optimize the performance of my code?
Thanks in advance
function maxMin(k, arr) {
// create array to push unfairness values to
var unfairnesses = [];
// sort given array in ascending order
arr.sort(function(a, b) {
return a - b;
});
// loop over sorted array
for(var i = 0; i < arr.length - k + 1; i++) {
// get array with the length of k at position i
var tempArr = arr.slice(i, i + k);
// determine Max and Min of the sliced array
var tempArrMax = Math.max(...tempArr);
var tempArrMin = Math.min(...tempArr);
// get unfairness of the sliced array
var thisUnfairness = tempArrMax - tempArrMin;
// push sliced-array-unfairness to unfairnesses array
unfairnesses.push(thisUnfairness);
}
// return minimal value of unfairnesses array
return Math.min(...unfairnesses);
}
The two first steps could be:
Your array is sorted. Thus there's no need to use Math.max and Math.min - the first element of a slice is the smallest, the last is the largest.
When you eliminate Math.max and Math.min calls, you can remove Array.prototype.slice call. Then you're left with a sort and a single pass over the array.
To sort the array you're already looping one time over the whole thing.
Then you're looping another time to figure out which one is max and which one is the minimum.
You're looping twice as you can only loop once if you do:
function minMax(array) {
const safeArray = array ?? []
// No max or min as array is empty
if(safeArray.length === 0)
return [undefined, undefined]
let max: number = Number.MIN_SAFE_INTEGER
let min: number = Number.MAX_SAFE_INTEGER
for(let item of safeArray) {
max = Math.max(item, max)
min = Math.min(item, min)
}
return [max, min]
}
I'm trying to get the function below to return the average of all elements in array1, but I keep getting null as the result. I can't seem to figure out why.
var array1 = [46,73,-18,0,-442,779,5,1400];
var arrayAverage = function(arrayavg) {
for (var average = 0,answer=0, arrayavg = arrayavg.length;array1 > answer;answer++)
average +=parseInt(arrayavg[answer]);
var calc = average/arrayavg.length;
return calc
};
There are a number of errors, I don't have time to point them all out, hopefully the following is sufficient:
var array1 = [46,73,-18,0,-442,779,5,1400];
var arrayAverage = function(arrayavg) {
I don't know why you using a function expression rather than a function declaration. It doesn't affect the issue, but is more code to write. It's also good to give variables names that express what they are for, so given that the function expects an array:
function arrayAverage(array) {
then:
for (var average = 0,answer=0, arrayavg = arrayavg.length;array1 > answer;answer++)
It's not a good idea to pile all those variable declarations into the for condition, far better to separate concerns and only create variables that you need:
var total = 0;
Now iterate over the array to get the total value. The '{' brackets can be omitted, but it's clearer to include them:
for (var i=0, iLen=array.length; i<iLen; i++) {
total += array[i];
}
Now calculate the average and return it in one statement:
return total/iLen;
}
console.log(arrayAverage(array1)); // 230.375
You need to put brackets after your for loop
I was too fast to answer.
You are re-assigning the passed array to the length of the passed array.
arrayavg = arrayavg.length
this breaks everything.
in the for loop you have assigned arrayavg=arrayavg.length and in the body ,you are accessing average+=arrayavg[answer]. arrayavg is now a primitive type . it will return undefined.
And your loop condition is array1 > answer array1 is an array .you cant compare it like that.it will return false.
modified code.
var array1 = [46,73,-18,0,-442,779,5,1400];
var arrayAverage = function(arrayavg) {
var sum=0;
for (var i=0;i<arrayavg.length;i++)
sum +=parseInt(arrayavg[i]);
return sum/arrayavg.length;
};
You are comparing a number to your array in your for loop. You want to stop the for when answer is the same as array1 length.
Also, don't change your parameter array to its length if you want to get its values in the loop.
var array1 = [46,73,-18,0,-442,779,5,1400];
var arrayAverage = function(arrayavg) {
for (var average = 0,answer=0, len = arrayavg.length;len > answer;answer++)
average +=parseInt(arrayavg[answer]);
var calc = average/len;
return calc
};
And to call it:
arrayAverage(array1);
Your code has two problems in the for loop.
for (var average = 0,answer=0, arrayavg = arrayavg.length;array1 > answer;answer++)
^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^
First thing is you set arrayavg to arrayavg's length BUT in the next line you try to read the index of the array. Well you overwrote the array with a number! Not going to happen.
Second issue you are comparing an array 'array1' to a number 'answer' . What does that check do? Not what you think it is going. You want to be checking the length, but wouldn't you want to be checking the passed in array, not the hardcoded one?
I think the other answers (particularly RobG) have covered most of it. It might help to follow a couple of standard rules (that I use) for your loops:
1) Always have the index as the first declared element, the length of the array (for caching purposes) as the second, and any other variables after them.
2) Always use brackets to separate your loop code from the code in the rest of the function. That way you know when to return your averaged product (ie after the }).
So this is my slightly rewritten code of your problem:
for (var index = 0, len = arrayavg.length, avg = 0; index < len; index++) {
avg += parseInt(arrayavg[index], 10) / len;
}
return avg;
Note also that parseInt should contain a radix (in this case 10). You can leave it out but it's good practice to always include it.
By the way, here's an alternative to your function you might find useful that uses a functional approach using reduce:
var arrayAverage = function (arr) {
return arr.reduce(function (a, b) { return a + b; }) / arr.length;
}
I have a list of numbers and I need to find the n smallest of them.
This is what I have so far, but I'm sure it must be possible to do it faster and cleaner (maybe without having to sort the entire list first?):
var list = [56,34,27,4,78,12,89,1002,45,33,87,90];
var results = [];
var sorted_list = list.slice(); // fastest way to duplicate array
sorted_list.sort(function (a, b) {
return a - b;
});
for (var i = 0; i < n; i++) {
// do stuff with sorted_list[i] and list
// push the result to results
}
return results;
I think if you use a Min Heap to solve this problem, it will be faster. By that I mean
Form a min heap from an array.
Take out the min element and heapify.(This step you will repeat, depending upon how many items you want)
Sorting algorithm will take O(N*logN) time, while Min Heap creation(step 1) will take O(N) time and O(logN){average} will be time taken by the second step.
Note that: Heap is useful when the number of items you needs is less than N. If you repeat the step 2 N times, the total time comes out to O(N*logN) itself same as sorting.
Array.min = function( array ){
return Math.min.apply( Math, array );
};
Source: http://ejohn.org/blog/fast-javascript-maxmin/
Thanks for the clarification. For n elements use:
Array.min = function( array, n ){
return array.sort(function(a, b){return a-b}).slice(0, n);
};
Without sorting it can be done in this way.
Basically the idea is that everytime in each iteration we will push the smallest number in an array.
and will remove that number from the main array
Suppose we have an array of length 12
a=[-11,2,1,5,0,9,-8,6,-10,0,12,4];
and we have to find 4 smallest number
we can find using this function(am is the result array)
function findmin(a) {
var item=Math.min.apply(Math, a);
var index= a.indexOf(item);
am.push(item);
a.splice(index, 1);
if(am.length<n)
findmin(a)
}
Now suppose we have to find 9 smallest number from the array
we can find(12-9=3) max number and remove it from the given array and then that will be our
result.
function findmax(a) {
var item=Math.max.apply(Math, a);
var index= a.indexOf(item);
am.push(item);
a.splice(index, 1);
if(a.length>n)
findmax(a)
}
Here I think the complexity is nm where m is no. of element to be found and n is total no. of element.If I am not wrong.
Actually i am weak in finding complexity.So please suggest if any improvement can be done.
SEE DEMO HERE
I have an array with a finite number of items in it. I want to randomly remove items until all the items have been used once.
Example [1,2,3,4,5]
Random number 5 is used, so I don't want that again.
Random number 2 is used, so I don't want that again.
And so on..
I could have another list of used numbers and check that the new random number is not in it, but that could take a long time when there is only 1 or two numbers left in the array out of 50.
Is there a way to remove an item from an array in javascript? Does it create a new array and would that be inefficient?
Are arrays the wrong way to do this?
EDIT: A few good answers here. I ended up randomizing the array list and then splicing the first item which is the one I took out.
Use the combination of Math.random() and splice():
var arr = [1, 2, 3, 4, 5],
i;
while ( arr.length ) {
i = Math.floor( Math.random() * arr.length );
alert( arr.splice(i, 1) );
}
Live demo: http://jsfiddle.net/simevidas/n2Bmk/
Update: You can use this function to remove a random item from your array:
function popRandomItem(arr) {
return arr.length ?
arr.splice(Math.floor(Math.random() * arr.length), 1)[0] : null;
}
So, if you have an array x, then popRandomItem(x) will remove a random item from that array and return that item. (If x is an empty array, the function will return null.)
Live demo: https://jsbin.com/wetumec/edit?js,console
You can use splice() to remove array elements.
Update
The function below lets you specify an upper and lower bound, and calling the resulting returned function will return a random number until the pool is empty, in which case it will return false (make sure you detect this with getRandom() === false).
var getRandomNumberOnce = function(lower, upper) {
var pool = [];
for (var i = lower; i <= upper; i++) {
pool.push(i);
}
return function() {
if (pool.length == 0) {
return false;
}
var randomIndex = Math.floor(Math.random() * pool.length ),
randomNumber = pool[randomIndex];
pool.splice(randomIndex, 1);
return randomNumber;
}
}
jsFiddle.
Usage
var myRandom = getRandomNumberOnce(0, 50);
myRandom(); // 4 (guaranteed to be random) :P http://xkcd.com/221/
I understand you're asking about removing elements but I'm going to go ahead and suggest an alternative way. This way does the same thing you want (getting a random element only once) but doesn't involve removing elements - well, it doesn't have to.
Essentially, randomise the order of your array rather than looking for random elements. Then work your way through the array for the random element you need. Your first random element will be at the 0 index, second random element will be at 1 index, etc.
var array = [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14];
array.sort(function(){ return (Math.round(Math.random())-0.5); });
for(var i=0;i<array.length;i++){
$("#list").append('<li>Random element ' + i + ': ' +array[i]);
}
Example: http://jsfiddle.net/jonathon/Re4HK/
Another question talks about randomising a JavaScript array but I just used a basic random sort as it works to illustrate the idea. You might want to look at that if you're concerned about how random your array is.
Sometimes you want a random item and don't
care if it is repeated once in a while.
Other times you want to quit when the aray is empty,
and see no repeats.
Array.prototype.getRandom= function(cut){
var i= Math.floor(Math.random()*this.length);
return cut? this.splice(i, 1): this[i];
}
I have an array var words = []//lots of different words in it. I have a Math.floor(Math.random()*words.length) that chooses a random word from the array. This is run in a loop that runs for a random number of times (between 2 and 200 times). I would like to make sure that the random numbers do not get chosen more than once during the time that that loop runs. How would you suggest doing this?
There's multiple ways of doing this.
You can shuffle the entire collection, and just grab items from one end. This will ensure you won't encounter any one item more than once (or rather, more than the number of times it occured in the original input array) during one whole iteration.
This, however, requires you to either modify in-place the original collection, or to create a copy of it.
If you only intend to grab a few items, there might be a different way.
You can use a hash table or other type of dictionary, and just do a check if the item you picked at random in the original collection already exists in the dictionary. If it doesn't, add it to the dictionary and use it. If it already exists in the dictionary, pick again.
This approach uses storage proportional to the number of items you need to pick.
Also note that this second approach is a bit bad performance-wise when you get to the few last items in the list, as you can risk hunting for the items you still haven't picked for quite a number of iterations, so this is only a viable solution if the items you need to randomly pick are far fewer than the number of items in the collection.
There are several different approaches, which are more or less effective depending on how much data you have, and how many items you want to pick:
Remove items from the array once they have been picked.
Shuffle the array and get the first items.
Keep a list of picked items and compare against new picks.
Loop through the items and pick values randomly based on the probability to be picked.
I'd shuffle the array as follows and then iterate over the shuffled array. There's no expensive array splice calls and the shuffling routine consists of swapping two values in the array n times where n is the length of the array, so it should scale well:
function shuffle(arr) {
var shuffled = arr.slice(0), i = arr.length, temp, index;
while (i--) {
index = Math.floor(i * Math.random());
temp = shuffled[index];
shuffled[index] = shuffled[i];
shuffled[i] = temp;
}
return shuffled;
}
console.log(shuffle(["one", "two", "three", "four"]));
this is how you can do it without shuffling the whole array
a = "abcdefghijklmnopq".split("");
c = 0;
r = [];
do {
var len = a.length - c;
var rnd = Math.floor(Math.random() * len);
r.push(a[rnd]);
a[rnd] = a[len - 1];
} while(++c < 5);
console.log(r);
the idea is to pick from 0..(length - step) elements and then shift the picked element towards the end.
I'd try using a map (Object literal) instead of an Array with keys being indexes:
var words = [ /* ... */ ] , map = { } , length = words.length ;
for(var n = 0 ; n < length ; n++) map[n] = words[n] ;
then make a function to pick a random entry based on the length, delete the entry (hence the index) and adjust the length:
function pickRandomEntry() {
var random = Math.floor( Math.random() * length ) ;
var entry = map[random] ;
delete map[random] ;
length-- ;
return entry ;
}
with this approach, you have to check for an undefined return value (since random might return the same number) and run the function again until it returns an actual value; or, make an array of picked indexes to filter the random numbers (which will however slow performance in case of long iteration cycles).
HTH
There are several solutions to this.
What you could do is use .splice() on your array to remove the item which is hit by words.
Then you can iterate over your array until it's empty. If you need to keep the array pristine you can create a copy of it first and iterate over the copy.
var words = ['apple', 'banana', 'cocoa', 'dade', 'elephant'];
while (words.length > 0) {
var i = Math.floor(Math.random()*words.length);
var word = words.splice(i, 1)[0];
}
Or something to that effect.
Here is a way with prime numbers and modulo that seems to do the trick without moving the original array or adding a hash:
<html>
<head>
</head>
<body>
shuffle
<div id="res"></div>
<script>
function shuffle(words){
var l = words.length,i = 1,primes = [43,47,53,59,61,67,71,73,79],//add more if needed
prime = primes[parseInt(Math.random()*primes.length, 10)],
temp = [];
do{
temp.push((i * prime) % l);
}while(++i <= l);
console.log(temp.join(','));
console.log(temp.sort().join(','));
}
</script>
</body>
</html>