18

我目前正在用 JavaScript 重现康威的生命游戏,我注意到函数 Math.random() 总是返回某种模式。以下是 100x100 网格中的随机结果示例:

在此处输入图像描述

有谁知道如何获得更好的随机数?

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();
  },

[更新]

我在这里创建了一个 jsFiddle:http: //jsfiddle.net/5Xrs7/1/

[更新]

看来 Math.random() 毕竟没问题(感谢raina77ow)。对不起各位!:(。如果您对结果感兴趣,这里是游戏的更新版本:http: //jsfiddle.net/sAKFQ/

(但我认为还有一些错误......)

4

7 回答 7

11

您的代码中的这一行...

var position = (y * 10) + x;

...是导致这种“非随机性”的原因。真的应该...

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

我想10这是这个网格的原始大小,这就是它在这里的原因。但这显然是错误的:您应该根据网格的当前大小来选择您的位置。


作为旁注,没有冒犯,但我仍然认为@JayC 答案中给出的算法优于你的算法。而且它很容易实现,只需将ApplyRandom函数中的两个循环更改为一个:

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

通过此更改,您将不再遭受var random = Math.floor((Math.random() * total) + 1);在行中重复使用相同数字的副作用,这会降低原始代码中的实际单元格填充率。

于 2013-06-02T16:47:00.227 回答
3

Math.random 是一种伪随机方法,这就是你得到这些结果的原因。我经常使用的绕过方法是捕捉鼠标光标位置,以便在 Math.random 结果中添加一些盐:

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);

它不是完全随机的,而是更多;)

于 2013-06-02T16:27:04.060 回答
2

更好的解决方案可能不是随机选择点并将它们涂成黑色,而是遍历每个点,确定应该填充的几率是多少,然后进行相应的填充。(也就是说,如果你希望它平均有 20% 的几率被填充,那么r当我在 WebGL 中看到一个 Life 模拟器时生成你的随机数并填充,r < 0.2 这就是它初始化的作用......IIRC。

编辑:这是考虑替代绘画方法的另一个原因。虽然随机选择像素最终可能会减少工作量并减少对随机数生成器的调用,这可能是一件好事,具体取决于您想要什么。事实上,您似乎选择了一种方式,最多可以填充一定百分比的像素。如果您一直跟踪正在填充的像素,并且如果已经填充了另一个像素,则选择填充另一个像素,那么您所做的基本上就是改组一个确切的百分比白色像素中的黑色像素。按照我的方式去做,所选像素的百分比将遵循二项分布。有时填充的百分比会多一点,有时会少一点。所有洗牌的集合是产生这种选择的可能性的严格子集(严格来说,它包含绘制棋盘的所有可能性,只是获得大多数可能性的可能性极低)。简而言之,为每个像素随机选择将允许更多的变化。

再说一次,我可以修改 shuffle 算法以根据从具有定义的预期/平均值而不是预期/平均值本身的二项式概率分布函数生成的数字来选择像素百分比,老实说,我不知道它与使用预期/平均值本身对每个像素运行赔率有任何不同——至少在理论上是这样。有很多事情可以做。

于 2013-06-02T16:40:41.203 回答
0

你可以试试

有关强度的讨论Math.random(),请参阅此问题

于 2013-06-02T16:16:19.723 回答
0

可能的实现Math.random基于线性同余生成器,其中一个弱点是随机数取决于较早的值,从而产生这样的可预测模式,具体取决于算法中常数的选择。在RANDU中可以看到一个关于常数选择不当的著名例子。

Mersenne Twister随机数生成器没有这个弱点。例如,您可以在 JavaScript 中找到 MT 的实现:https ://gist.github.com/banksean/300494


更新:查看您的代码,您在呈现网格的代码中存在问题。这一行:

var position = (y * 10) + x;

应该:

var position = (y * grid_x) + x;

有了这个修复,就没有明显的模式了。

于 2013-06-02T16:41:18.857 回答
0

您可以使用时间戳中的部分 sha256 哈希,包括纳秒:

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

这可以编码为字符串,然后你可以得到哈希,使用这个: http: //geraintluff.github.io/sha256/

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

然后,使用来自@nfroidure 的函数,在之前编写 gen_salt 函数,在那里使用 sha256 哈希,并将 gen_salt 调用写入 eventListener。您可以使用 sha256(previous_salt) + 鼠标坐标作为字符串来获取随机散列。

于 2018-07-24T21:52:00.463 回答
0

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

这将返回一个具有加密强度的随机字节:https ://developer.mozilla.org/en/docs/Web/API/Crypto/getRandomValues

于 2021-07-13T15:27:20.713 回答