Making Math.random() more random? - javascript

I am slowly working my way through JavaScript and have run into an issue with Math.random().
I read on MDN that Math.random() is always seeded with the current Date - and I "believe" what is happening is that my two Math.random() calls are happening so quickly that they are returning basically the same value. Here is the code, this is the beginnings of a card game.
var cards = new Array(null);
cards[0] = new Array("Ace of Spades","ace","A","spades","black");
cards[1] = new Array("Two of Spades","two","2","spades","black");
cards[2] = new Array("Three of Spades","three","3","spades","black");
// you get the idea ... full code on the JSFiddle below
cards[51] = new Array("King of Hearts","king","K","hearts","red");
// initialize variables
var cardsInHand = 5;
// variables to make the card drawing decrement work without touching original array
var restOfDeck = new Array(null);
restOfDeck = cards;
// initialize hands as a doubly-nested Arrays
var player1Hand = new Array(null);
var player2Hand = new Array(null);
// function to randomly "draw" the cards, decrementing the deck each time
for (var k = 0; k < cardsInHand; k++) {
player1Hand[k] = restOfDeck[(Math.floor(Math.random() * restOfDeck.length))];
restOfDeck.splice(restOfDeck.indexOf(player1Hand[k]), 1);
console.log((k + 1) + ' cards to Player 1');
player2Hand[k] = restOfDeck[(Math.floor(Math.random() * restOfDeck.length))];
restOfDeck.splice(restOfDeck.indexOf(player2Hand[k]), 1);
console.log((k + 1) + ' cards to Player 2');
}
// display hand
document.write('Player 1\'s Hand\: <br />');
for (var i = 0; i < cardsInHand; i++) {
document.write(player1Hand[i][0] + "<br />");
}
document.write('<br /><br />Player 2\'s Hand\: <br />');
for (var j = 0; j < cardsInHand; j++) {
document.write(player2Hand[j][0] + "<br />");
}
What is happening that among the 10 cards "dealt", there are always two cards "in order" (Two of Hearts, then Three of Hearts). Always. I believe it has something to do with the two Math.random() calls in the draw/decrement function, but I am not sure how to rework. Thanks in advance!
JSFiddle here: http://jsfiddle.net/bjVCL/

This is just simple probability at work. The only time you won't see adjacent pairs is if the card values are distributed such that ...
1st card - can be any value
2nd card - must be one of 11 values not adjacent to previous card (11:13 odds)
3rd card - must be one of 9 values not adjacent to previous two
4th card - must be one of 7 values not adjacent to previous three
5th card - must be one of 5 values not adjacent to previous four
Thus, the odds of getting a hand with no adjacent values is (13/13) * (11 * 13) * (9 * 13) * (7 / 13) * (5 / 13) = a paltry 12%
(Note: 'my stastics is a little rusty so I may not have this quite right, but it's close enough to give a sense of the effect you're seeing.)

Related

Generate random & unique 4 digit codes without brute force

I'm building an app and in one of my functions I need to generate random & unique 4 digit codes. Obviously there is a finite range from 0000 to 9999 but each day the entire list will be wiped and each day I will not need more than the available amount of codes which means it's possible to have unique codes for each day. Realistically I will probably only need a few hundred codes a day.
The way I've coded it for now is the simple brute force way which would be to generate a random 4 digit number, check if the number exists in an array and if it does, generate another number while if it doesn't, return the generated number.
Since it's 4 digits, the runtime isn't anything too crazy and I'm mostly generating a few hundred codes a day so there won't be some scenario where I've generated 9999 codes and I keep randomly generating numbers to find the last remaining one.
It would also be fine to have letters in there as well instead of just numbers if it would make the problem easier.
Other than my brute force method, what would be a more efficient way of doing this?
Thank you!
Since you have a constrained number of values that will easily fit in memory, the simplest way I know of is to create a list of the possible values and select one randomly, then remove it from the list so it can't be selected again. This will never have a collision with a previously used number:
function initValues(numValues) {
const values = new Array(numValues);
// fill the array with each value
for (let i = 0; i < values.length; i++) {
values[i] = i;
}
return values;
}
function getValue(array) {
if (!array.length) {
throw new Error("array is empty, no more random values");
}
const i = Math.floor(Math.random() * array.length);
const returnVal = array[i];
array.splice(i, 1);
return returnVal;
}
// sample code to use it
const rands = initValues(10000);
console.log(getValue(rands));
console.log(getValue(rands));
console.log(getValue(rands));
console.log(getValue(rands));
This works by doing the following:
Generate an array of all possible values.
When you need a value, select one from the array with a random index.
After selecting the value, remove it from the array.
Return the selected value.
Items are never repeated because they are removed from the array when used.
There are no collisions with used values because you're always just selecting a random value from the remaining unused values.
This relies on the fact that an array of integers is pretty well optimized in Javascript so doing a .splice() on a 10,000 element array is still pretty fast (as it can probably just be memmove instructions).
FYI, this could be made more memory efficient by using a typed array since your numbers can be represented in 16-bit values (instead of the default 64 bits for doubles). But, you'd have to implement your own version of .splice() and keep track of the length yourself since typed arrays don't have these capabilities built in.
For even larger problems like this where memory usage becomes a problem, I've used a BitArray to keep track of previous usage of values.
Here's a class implementation of the same functionality:
class Randoms {
constructor(numValues) {
this.values = new Array(numValues);
for (let i = 0; i < this.values.length; i++) {
this.values[i] = i;
}
}
getRandomValue() {
if (!this.values.length) {
throw new Error("no more random values");
}
const i = Math.floor(Math.random() * this.values.length);
const returnVal = this.values[i];
this.values.splice(i, 1);
return returnVal;
}
}
const rands = new Randoms(10000);
console.log(rands.getRandomValue());
console.log(rands.getRandomValue());
console.log(rands.getRandomValue());
console.log(rands.getRandomValue());
Knuth's multiplicative method looks to work pretty well: it'll map numbers 0 to 9999 to a random-looking other number 0 to 9999, with no overlap:
const hash = i => i*2654435761 % (10000);
const s = new Set();
for (let i = 0; i < 10000; i++) {
const n = hash(i);
if (s.has(n)) { console.log(i, n); break; }
s.add(n);
}
To implement it, simply keep track of an index that gets incremented each time a new one is generated:
const hash = i => i*2654435761 % (10000);
let i = 1;
console.log(
hash(i++),
hash(i++),
hash(i++),
hash(i++),
hash(i++),
);
These results aren't actually random, but they probably do the job well enough for most purposes.
Disclaimer:
This is copy-paste from my answer to another question here. The code was in turn ported from yet another question here.
Utilities:
function isPrime(n) {
if (n <= 1) return false;
if (n <= 3) return true;
if (n % 2 == 0 || n % 3 == 0) return false;
for (let i = 5; i * i <= n; i = i + 6) {
if (n % i == 0 || n % (i + 2) == 0) return false;
}
return true;
}
function findNextPrime(n) {
if (n <= 1) return 2;
let prime = n;
while (true) {
prime++;
if (isPrime(prime)) return prime;
}
}
function getIndexGeneratorParams(spaceSize) {
const N = spaceSize;
const Q = findNextPrime(Math.floor(2 * N / (1 + Math.sqrt(5))))
const firstIndex = Math.floor(Math.random() * spaceSize);
return [firstIndex, N, Q]
}
function getNextIndex(prevIndex, N, Q) {
return (prevIndex + Q) % N
}
Usage
// Each day you bootstrap to get a tuple of these parameters and persist them throughout the day.
const [firstIndex, N, Q] = getIndexGeneratorParams(10000)
// need to keep track of previous index generated.
// it’s a seed to generate next one.
let prevIndex = firstIndex
// calling this function gives you the unique code
function getHashCode() {
prevIndex = getNextIndex(prevIndex, N, Q)
return prevIndex.toString().padStart(4, "0")
}
console.log(getHashCode());
Explanation
For simplicity let’s say you want generate non-repeat numbers from 0 to 35 in random order. We get pseudo-randomness by polling a "full cycle iterator"†. The idea is simple:
have the indexes 0..35 layout in a circle, denote upperbound as N=36
decide a step size, denoted as Q (Q=23 in this case) given by this formula‡
Q = findNextPrime(Math.floor(2 * N / (1 + Math.sqrt(5))))
randomly decide a starting point, e.g. number 5
start generating seemingly random nextIndex from prevIndex, by
nextIndex = (prevIndex + Q) % N
So if we put 5 in we get (5 + 23) % 36 == 28. Put 28 in we get (28 + 23) % 36 == 15.
This process will go through every number in circle (jump back and forth among points on the circle), it will pick each number only once, without repeating. When we get back to our starting point 5, we know we've reach the end.
†: I'm not sure about this term, just quoting from this answer
‡: This formula only gives a nice step size that will make things look more "random", the only requirement for Q is it must be coprime to N
This problem is so small I think a simple solution is best. Build an ordered array of the 10k possible values & permute it at the start of each day. Give the k'th value to the k'th request that day.
It avoids the possible problem with your solution of having multiple collisions.

Applying Fibonacci, working with large numbers

I am trying to successfully complete this challenge on the Rosalind page. The challenge is:
Given: Positive integers n≤40 and k≤5.
Return: The total number of rabbit pairs that will be present after n months if we begin with 1 pair and in each generation, every pair of reproduction-age rabbits produces a litter of k rabbit pairs (instead of only 1 pair).
The exercise gives a text file of two numbers, the n and k mentioned above.
My code, which attempts to implement Fibonacci, works as expected for lower numbers of months. However, the result begins to become extremely large for higher numbers of months, and in each case I am given, my answer is Infinity.
Is my formula applied incorrectly? Or is Javascript a bad choice of language to use for such an exercise?
My code:
function fibonacciRabbits(months, pairs){
var months = months;
var numberOfPairs = pairs;
var result = 0;
// Declare parent & child arrays with constants
var parentArr = [1, numberOfPairs + 1]
var childArr = [numberOfPairs, numberOfPairs]
var total = []
// Loop from the point after constants set above
for(var i = 2; i < months - 2 ; i++){
parentArr.push(parentArr[i-1] + childArr[i-1])
childArr.push(parentArr[i-1] * childArr[i-2])
total.push(parentArr[i-1] + childArr[i-1])
}
result = childArr[childArr.length - 1] + parentArr[parentArr.length - 1]
console.log(months + ' months and ' + numberOfPairs + ' pairs:\n')
console.log('parentArr:\n', parentArr)
console.log('childArr:\n', childArr)
console.log('total\n', total)
console.log('result:', result)
console.log('\n\n\n')
}
fibonacciRabbits(5, 3)
fibonacciRabbits(11, 3)
fibonacciRabbits(21, 3)
fibonacciRabbits(31, 2)
And here is a REPL
Here is a more brief solution that doesn't produce such large numbers, and handles the maximum case without hitting infinity in Javascript. I think your solution was getting too big too fast.
function fibonacciRabbits(months, reproAmount){
var existingAdults = 0;
var adultPairs = 0;
var childPairs = 1;
for(var i = 2; i <= months; i++){
adultPairs = childPairs; //children mature
childPairs = (existingAdults * reproAmount); //last month's adults reproduce
existingAdults += adultPairs; //new adults added to the reproduction pool
}
console.log(existingAdults + childPairs);
}
To make sure you are on the right track, test your function with:
fibonacciRabbits(1, 1);
fibonacciRabbits(2, 1);
Which from the website says: f(1)=f(2)=1. So these should both produce "1" no matter what. Your code produces "3" for both of these.

Randomly fill fixed number of cells in a two dimensional grid [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 7 years ago.
Improve this question
I'm trying to make JavaScript Minesweeper and I've got some problems in pushBombs function, I guess. I'm trying to put bombs in array, sometimes it puts exact number that is said but sometimes it puts less number.
My questions:
When condition is true it puts bomb in array but when it's false it passes this itaration or looks for another index in array that is true?
Math.random() returns different values in condition and in code block.
It puts bomb where it already was but then why?
var grid = {
_grid: [],
createGrid: function (c) {
for (var i = 0; i < c; i++) {
this._grid.push([]);
for (var a = 0; a < c; a++) {
this._grid[i][a] = 'Empty';
}
}
},
pushBombs: function (b) {
for (var i = 0; i < b; i++) {
if (this._grid[Math.round(Math.random() * (this._grid.length - 1))][Math.round(Math.random() * (this._grid.length - 1))] == 'Empty') {
this._grid[Math.round(Math.random() * (this._grid.length - 1))][Math.round(Math.random() * (this._grid.length - 1))] = 'Bomb';
}
}
},
Everytime you call Math.random() you are going to get a different value. Assuming that during your code, you want to be looking at the same slot in both the check and in the assignment, you would want to only call that once for each axis of your grid. Below, I'm calling them xx and yy.
pushBombs: function (b) {
for (var i = 0; i < b; i++) {
var xx = Math.round(Math.random() * (this._grid.length - 1));
var yy = Math.round(Math.random() * (this._grid.length - 1));
if (this._grid[xx][yy] == 'Empty') {
this._grid[xx][yy] = 'Bomb';
}
}
}
EDIT
Something you might want to think about is that, this code assumes that both dimensions of your grid are equal in length. Based on the createGrid() method it looks like that is the case. Thinking ahead you may choose to have a rectangular grid, in which case, this pushBombs() method would break. One option is to store the X and Y lengths in so that the xx and yy can correctly randomize their values. Alternatively, I might suggest changing the code above slightly to use:
//randmomize the length of the first dimension
var xx = Math.round(Math.random() * (this._grid.length - 1));
//randomize the second dimension. Notice, this is looking at the length
//of the first item in the grid (which is your second dimension).
var yy = Math.round(Math.random() * (this._grid[0].length - 1));
EDIT 2
If you want to ensure that you always get the exact number of bombs, you should use a while loop as #PierreDuc suggested:
while(b > 0) {
var xx = Math.round(Math.random() * (this._grid.length - 1));
var yy = Math.round(Math.random() * (this._grid[0].length - 1));
if (this._grid[xx][yy] == 'Empty') {
this._grid[xx][yy] = 'Bomb';
b--;
}
}
Note though that if the value of b is greater than the number of Empty slots, this will turn into an infinite loop.

Javascript: Some Variables Defined, Some Undefined

I am writing a basic casino javascript game that will randomly pick 3 numbers 1-10. If each number is 7, it will display an alert box saying 'You Win!'. In the function below:
function StartSpin(){
var one;
var two;
var three;
var cone;
var ctwo;
var cthree;
var one = Math.floor((Math.random() * 10) + 1);
var two = Math.floor((Math.random() * 10) + 1);
var three = Math.floor((Math.random() * 10) + 1);
if(one == 1){var cone = "Ace";}
if(two == 1){var ctwo = "Ace";}
if(three == 1){var cthree = "Ace";}
document.getElementsByClassName("Spinner")[0].innerHTML= cone
document.getElementsByClassName("Spinner")[1].innerHTML= ctwo
document.getElementsByClassName("Spinner")[2].innerHTML= cthree
}
On the actual page before clicking the button to start randomizing it says:
--|--|--. When clicking it, it sets the --'s to the randomized number. Every number/-- set says undefined except sometimes one will say 'Ace' meaning it was 1. So it might say: undefined|Ace|undefined, or undefined|undefined|undefined, etc.
Here is the HTML:
<div id="GameOne">
<h1>~ Game One ~</h1>
<h2>Try to get three 7's!</h2>
<span class="so Spinner">--</span> |
<span class="st Spinner">--</span> |
<span class="sth Spinner">--</span>
<br/>
<button id="SpinButton" onclick="StartSpin()">Spin!</button>
</div>
EDIT: I re-defined variables to see if that would help the undefined problem(In the javascript code fyi)
The short answer is you are only giving your variables values other than undefined if you randomly get the number 1. Otherwise they stay undefined - which is the default value of variables in JavaScript.
Here's some seriously cleaned up logic:
http://jsbin.com/milibusaxe/1/edit?html,js,output
function roll() {
var n = Math.floor((Math.random() * 10) + 1);
return (n === 1 ? 'Ace!' : n);
}
function StartSpin(){
var slots = document.getElementsByClassName("Spinner");
for (var i = 0, e = slots.length; i < e; i++) {
slots[i].innerHTML = roll();
}
}
document.getElementById('SpinButton').addEventListener('click', StartSpin);
As a side note, three sevens or three ones? Might want to make up your mind on that one.
They are being set to undefined because you are only setting the variables (cone, ctwo, cthree) when a 1 is randomly selected. I assume if an ace isn't drawn you want the number to be displayed?
function StartSpin() {
for (var i = 0; i < 3; i++) {
var num = Math.floor((Math.random() * 10) + 1);
if (num == 1) {
num = 'Ace';
}
document.getElementsByClassName("Spinner")[i].innerHTML = num;
}
}
You define the cone, ctwo and ctreeonly if one, two or three (respectively) equals to 1. Otherwise, variables are not initiated and that's why they are undefined.
See undefined
You can try this:
https://jsfiddle.net/0jaxL1hb/1/
Looks like you are having some trouble with how variables work. cone, ctwo, & cthree are undefined in most cases unless you get a 1. Also you only need to declare var in front of a variable when you create it. Later references just use the variable name:
var i = 1;
var j = i + 5;
console.log("i is", i, "and j is", j); // will print `i is 1 and j is 5`
A declared variable without a set value will be undefined
var k;
console.log(k); // will print `undefined`
In you code you are trying to transform 1 into the string "Ace", but you end up throwing out the values in one, two, and three in ALL other cases. This should work instead:
function StartSpin() {
// Function to make a number between 1 and 10, if 1 return "Ace" instead
function randomNumberOrAce() {
var number = Math.floor((Math.random() * 10) + 1);
// Check here if it's a `1`, and return "Ace instead", otherwise return the previously stored value
if (number === 1) {
return "Ace";
} else {
return number;
}
}
// Fill in the first three elements of ".Spinner" with three random numbers
document.getElementsByClassName("Spinner")[0].innerHTML = randomNumberOrAce();
document.getElementsByClassName("Spinner")[1].innerHTML = randomNumberOrAce();
document.getElementsByClassName("Spinner")[2].innerHTML = randomNumberOrAce();
}
<div id="GameOne">
<h1>~ Game One ~</h1>
<h2>Try to get three 7's!</h2>
<span class="so Spinner">--</span> |
<span class="st Spinner">--</span> |
<span class="sth Spinner">--</span>
<br/>
<button id="SpinButton" onclick="StartSpin()">Spin!</button>
</div>

How to resort a Javascript array when only the first value has changed

I've got a little app that recalculates the apportionment of seats in Congress in each state as the user changes the population hypothetically by moving counties between states. There are functionally infinite combinations, so I need to compute this on the fly.
The method is fairly straightforward: You give each state 1 seat, then assign the remaining 385 iteratively by weighting them according to population / ((seats * (seats + 1)) and assigning the seat to the top priority state.
I've got this working fine the obvious way:
function apportion(states) {
var totalReps = 435;
// assign one seat to each state
states.forEach(function(state) {
state.totalReps = 1;
totalReps -= 1;
state.priority = state.data.population / Math.sqrt(2); //Calculate default quota
});
// sort function
var topPriority = function(a, b) {
return b.priority - a.priority;
};
// assign the remaining 385
for (totalReps; totalReps > 0; totalReps -= 1) {
states.sort(topPriority);
states[0].totalReps += 1;
// recalculate the priority for this state
states[0].priority = states[0].data.population / Math.sqrt(states[0].totalReps * (states[0].totalReps + 1));
}
return states;
}
However, it drags a little when called several times a second. I'm wondering whether there's a better way to place the state that received the seat back in the sorted array other than by resorting the whole array. I don't know a ton about the Javascript sort() function and whether it will already do this with maximal efficiency without being told that all but the first element in the array is already sorted. Is there a more efficient way that I can implement by hand?
jsFiddle here: http://jsfiddle.net/raphaeljs/zoyLb9g6/1/
Using a strategy of avoiding sorts, the following keeps an array of priorities that is aligned with the states object and uses Math.max to find the highest priority value, then indexOf to find its position in the array, then updates the states object and priorities array.
As with all performance optimisations, it has very different results in different browsers (see http://jsperf.com/calc-reps), but is at least no slower (Chrome) and up to 4 times faster (Firefox).
function apportion1(states) {
var totalReps = 435;
var sqrt2 = Math.sqrt(2);
var priorities = [];
var max, idx, state, n;
// assign one seat to each state
states.forEach(function(state) {
state.totalReps = 1;
state.priority = state.data.population / sqrt2; //Calculate default quota
priorities.push(state.priority);
});
totalReps -= states.length;
while (totalReps--) {
max = Math.max.apply(Math, priorities);
idx = priorities.indexOf(max);
state = states[idx];
n = ++state.totalReps;
state.priority = state.data.population / Math.sqrt(n * ++n);
priorities[idx] = state.priority;
}
return states;
}
For testing I used an assumed states object with only 5 states, but real population data. Hopefully, with the full 50 states the benefit will be larger.
Another strategy is to sort on population since that's how the priorities are distributed, assign at least one rep to each state and calculate the priority, then run from 0 adding reps and recalculating priorities. There will be a threshold below which a state should not get any more reps.
Over to you. ;-)
Edit
Here's a really simple method that apportions based on population. If may allocation one too many or one too few. In the first case, find the state with the lowest priority and at least 2 reps (and recalc priority if you want) and take a rep away. In the second, find the state with the highest priority and add one rep (and recalc priority if required).
function simple(states) {
var totalPop = 0;
var totalReps = 435
states.forEach(function(state){totalPop += state.data.population});
var popperrep = totalPop/totalReps;
states.forEach(function(state){
state.totalReps = Math.round(state.data.population / popperrep);
state.priority = state.data.population / Math.sqrt(state.totalReps * (state.totalReps + 1));
});
return states;
}
Untested, but I'll bet it's very much faster than the others. ;-)
I've updated the test example for the simple function to adjust if the distribution results in an incorrect total number of reps. Tested across a variety of scenarios, it gives identical results to the original code even though it uses a very different algorithm. It's several hundred times faster than the original with the full 50 states.
Here's the final version of the simple function:
function simple(states) {
var count = 0;
var state, diff;
var totalPop = states.reduce(function(prev, curr){return prev + curr.data.population},0);
var totalReps = 435
var popperrep = totalPop/totalReps;
states.forEach(function(state){
state.totalReps = Math.round(state.data.population / popperrep) || 1;
state.priority = state.data.population / Math.sqrt(state.totalReps * (state.totalReps + 1));
count += state.totalReps;
});
// If too many reps distributed, trim from lowest priority with 2 or more
// If not enough reps distributed, add to highest priority
while ((diff = count - totalReps)) {
state = states[getPriority(diff < 0)];
state.totalReps += diff > 0? -1 : 1;
count += diff > 0? -1 : 1;
state.priority = state.data.population / Math.sqrt(state.totalReps * (state.totalReps + 1));
// console.log('Adjusted ' + state.data.name + ' ' + diff);
}
return states;
// Get lowest priority state with 2 or more reps,
// or highest priority state if high is true
function getPriority(high) {
var idx, p = high? 0 : +Infinity;
states.forEach(function(state, i){
if (( high && state.priority > p) || (!high && state.totalReps > 1 && state.priority < p)) {
p = state.priority;
idx = i;
}
});
return idx;
}
}

Categories

Resources