18

I'm currently making a Conway's Game of Life reproduction in JavaScript and I've noticed that the function Math.random() is always returning a certain pattern. Here's a sample of a randomized result in a 100x100 grid:

enter image description here

Does anyone knows how to get better randomized numbers?

ApplyRandom: function() {

    var $this = Evolution;

    var total = $this.Settings.grid_x * $this.Settings.grid_y;
    var range = parseInt(total * ($this.Settings.randomPercentage / 100));

    for(var i = 0; i < total; i++) {
      $this.Infos.grid[i] = false;
    }

    for(var i = 0; i < range; i++) {
      var random = Math.floor((Math.random() * total) + 1);
      $this.Infos.grid[random] = true;
    }

    $this.PrintGrid();
  },

[UPDATE]

I've created a jsFiddle here: http://jsfiddle.net/5Xrs7/1/

[UPDATE]

It seems that Math.random() was OK after all (thanks raina77ow). Sorry folks! :(. If you are interested by the result, here's an updated version of the game: http://jsfiddle.net/sAKFQ/

(But I think there's some bugs left...)

Chololoco
  • 3,140
  • 1
  • 21
  • 24
  • When you need better random numbers , using the 'time' as a variable or a multiplier is always a good idea. – Harsha Venkataramu Jun 02 '13 at 16:12
  • How about you multiply two or three randomized numbers? Even more randomness, no? Instead of Math.floor(Math.random()*numResults+minResult), something like Math.floor(Math.random()*numResults/someNumber + minResult/someNumber)*Math.floor(Math.random()*numResults/someNumber + minResult/someNumber) – Ariane Jun 02 '13 at 16:12
  • 4
    Could you show us some code @Danny? – Dogbert Jun 02 '13 at 16:13
  • random.org has a service, if that could be an option – Drahcir Jun 02 '13 at 16:13
  • 3
    Related: http://stackoverflow.com/q/8330102/1569 – Factor Mystic Jun 02 '13 at 16:13
  • 4
    Upvoted @Dogbert, looks like an XY problem indeed. Math.random() IS pretty random by itself. Its result might be predicted (as shown in the paper [here](http://landing2.trusteer.com/sites/default/files/Temporary_User_Tracking_in_Major_Browsers.pdf)), but it definitely not follows such a clear pattern as shown here. Something else is wrong. – raina77ow Jun 02 '13 at 16:16
  • @Danny, could you create a reduced test case on http://jsfiddle.net? Can't really understand what variables you're plotting with that code. – Dogbert Jun 02 '13 at 16:34
  • 3
    Actually, I took the liberty of creating such a [fiddle](http://jsfiddle.net/n8hZb/). Math.random(), as expected, gives quite a chaotic pattern. I even didn't fix off-by one error of the original code, using it as is. – raina77ow Jun 02 '13 at 16:37
  • The point is, we either should look at PrintGrid(), or check the params used by `ApplyRandom`. – raina77ow Jun 02 '13 at 16:38
  • 3
    @Ariane: I just want to point out that multiplying random numbers just changes the probability distribution of the results; they'd very likely be *less* random. Consider probabilities of choosing from `{1,2}`. You'd get 2 just as often as you get the combined results of 1 or 4. – JayC Jun 02 '13 at 16:51
  • @JayC Indeed. Maths are far behind me. – Ariane Jun 02 '13 at 17:05
  • 4
    I know this has been solved now and turned out not to be a problem with `Math.Random()` after all, but for the benefit of anyone else coming here with similar issues it's worth repeating the adage: Any time you think you've found a bug in the libraries you're using, the odds are very very high that it's actually a bug in your own code, not in the core libraries. Jeff Atwood said it much better than I could, so I'll just link to his blog here: [The 1st Rule Of Programming: It's Always Your Fault](http://www.codinghorror.com/blog/2008/03/the-first-rule-of-programming-its-always-your-fault.html) – Spudley Jun 02 '13 at 17:32
  • @Spudley I'd gave you +100 if I could for this comment. ) – raina77ow Jun 02 '13 at 17:46
  • 2
    [this is the best random number generator that I know of: `function getRandomNumber() { return 4; /* chosen by fair dice roll. guaranteed to be random */ }`](http://xkcd.com/221/) – zzzzBov Jun 02 '13 at 18:22
  • @Spudley I know it's been almost 4 years now, but I still remember this comment from time to time. ;) – Chololoco Mar 30 '17 at 12:28

7 Answers7

12

This line in your code...

var position = (y * 10) + x;

... is what's causing this 'non-randomness'. It really should be...

var position = (y * $this.Settings.grid_x) + x;

I suppose 10 was the original size of this grid, that's why it's here. But that's clearly wrong: you should choose your position based on the current size of the grid.


As a sidenote, no offence, but I still consider the algorithm given in @JayC answer to be superior to yours. And it's quite easy to implement, just change two loops in ApplyRandom function to a single one:

var bias = $this.Settings.randomPercentage / 100;
for (var i = 0; i < total; i++) {
  $this.Infos.grid[i] = Math.random() < bias;
}

With this change, you will no longer suffer from the side effect of reusing the same numbers in var random = Math.floor((Math.random() * total) + 1); line, which lowered the actual cell fillrate in your original code.

raina77ow
  • 103,633
  • 15
  • 192
  • 229
  • 6
    As a sidenote, I can't believe how many people here seemed to think that the pattern shown in the question can indeed be result of `Math.random()` non-randomness. – raina77ow Jun 02 '13 at 16:49
  • No, it's not about you. ) Sometimes when I work with extended codebase and something goes wrong, I begin to doubt the fundamentals too, so I understand your feelings completely. ) – raina77ow Jun 02 '13 at 16:54
  • 2
    The multiplier for `y` should be `grid_x`, the width of a line of the grid. Currently they are equal so there's no difference, but if the size is ever made non-square something will fail. – Joni Jun 02 '13 at 16:55
  • Also, you are calling `PrintGrid` at the end of `ApplyRandom`, *and* from the code in `Init`. As `PrintGrid` modifies the grid, what you are displaying as the initial step, is actually the state after a first iteration. – rsanchez Jun 02 '13 at 16:59
  • 2
    @DannyCoulombe For better program structure, you should move the code to advance the game from `PrintGrid` to a new function. – rsanchez Jun 02 '13 at 17:01
3

Math.random is a pseudo random method, that's why you're getting those results. A by pass i often use is to catch the mouse cursor position in order to add some salt to the Math.random results :

Math.random=(function(rand) {
  var salt=0;
  document.addEventListener('mousemove',function(event) {
    salt=event.pageX*event.pageY;
    });
return function() { return (rand()+(1/(1+salt)))%1; };
})(Math.random);

It's not completly random, but a bit more ;)

nfroidure
  • 1,531
  • 12
  • 20
2

A better solution is probably not to randomly pick points and paint them black, but to go through each and every point, decide what the odds are that it should be filled, and then fill accordingly. (That is, if you want it on average %20 percent chance of it being filled, generate your random number r and fill when r < 0.2 I've seen a Life simulator in WebGL and that's kinda what it does to initialize...IIRC.

Edit: Here's another reason to consider alternate methods of painting. While randomly selecting pixels might end up in less work and less invocation of your random number generator, which might be a good thing, depending upon what you want. As it is, you seem to have selected a way that, at most some percentage of your pixels will be filled. IF you had kept track of the pixels being filled, and chose to fill another pixel if one was already filled, essentially all your doing is shuffling an exact percentage of black pixels among your white pixels. Do it my way, and the percentage of pixels selected will follow a binomial distribution. Sometimes the percentage filled will be a little more, sometimes a little less. The set of all shufflings is a strict subset of the possibilities generated this kind of picking (which, also strictly speaking, contains all possibilities for painting the board, just with astronomically low odds of getting most of them). Simply put, randomly choosing for every pixel would allow more variance.

Then again, I could modify the shuffle algorithm to pick a percentage of pixels based upon numbers generated from a binomial probability distribution function with a defined expected/mean value instead of the expected/mean value itself, and I honestly don't know that it'd be any different--at least theoretically--than running the odds for every pixel with the expected/mean value itself. There's a lot that could be done.

JayC
  • 7,053
  • 2
  • 25
  • 41
  • Yes, that's much better solution. Still would be interesting to see what's wrong in the original code. ) – raina77ow Jun 02 '13 at 16:41
1

console.log(window.crypto.getRandomValues(new Uint8Array(32))); //return 32 random bytes

This return a random bytes with crypto-strength: https://developer.mozilla.org/en/docs/Web/API/Crypto/getRandomValues

0

You can try

For a discussion of strength of Math.random(), see this question.

Community
  • 1
  • 1
Mifeet
  • 12,949
  • 5
  • 60
  • 108
0

The implementation of Math.random probably is based on a linear congruential generator, one weakness of which is that a random number depends on the earlier value, producing predictable patterns like this, depending on the choice of the constants in the algorithm. A famous example of the effect of poor choice of constants can be seen in RANDU.

The Mersenne Twister random number generator does not have this weakness. You can find an implementation of MT in JavaScript for example here: https://gist.github.com/banksean/300494


Update: Seeing your code, you have a problem in the code that renders the grid. This line:

var position = (y * 10) + x;

Should be:

var position = (y * grid_x) + x;

With this fix there is no discernible pattern.

Joni
  • 108,737
  • 14
  • 143
  • 193
0

You can using the part of sha256 hash from timestamp including nanoseconds:

console.log(window.performance.now()); //return nanoseconds inside

This can be encoded as string, then you can get hash, using this: http://geraintluff.github.io/sha256/

salt = parseInt(sha256(previous_salt_string).substring(0, 12), 16);
//48 bits number < 2^53-1

then, using function from @nfroidure, write gen_salt function before, use sha256 hash there, and write gen_salt call to eventListener. You can use sha256(previous_salt) + mouse coordinate, as string to get randomized hash.