【问题标题】:Math#random ain't so random?数学#random 不是那么随机吗?
【发布时间】:2012-04-01 09:21:02
【问题描述】:

我发现我的程序发生了一些奇怪的事情。

这个程序主要用于组件点击,因为它是一个测试随机性的概念。

如您所见,它打印正确,因为它应该有向中间点击的趋势,这非常完美。

问题是,它似乎有偏见。

import java.applet.Applet;
import java.awt.Point;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.awt.*;

public class Testing extends Applet {

/**
 * 
 */
private static final long serialVersionUID = -2441995094105327849L;

public void init() {
    setSize(WIDTH, HEIGHT);
    img = createImage(WIDTH, HEIGHT);
    g = img.getGraphics();
    paint(g);
    setVisible(true);
    g.setColor(new Color(0, 0, 0));
    g.fillRect(0, 0, WIDTH, HEIGHT);
    g.setColor(new Color(55, 55, 55, 55));
    main();
}

public final static int WIDTH = 400, HEIGHT = 400;
public static int[] widths = new int[WIDTH], heights = new int[HEIGHT];
public static ArrayList<String> set = new ArrayList<String>();
public Image img;
public Graphics g;

public void paint(Graphics g) {
    g.drawImage(img, 0, 0, null);
}

public void update(Graphics g) {
    paint(g);
}

public void main() {
    int count101 = 0;
    int count100 = 0;
    int count99 = 0;
    try {
        PrintWriter pw = new PrintWriter(new FileWriter(
                new File("Data.dat")));
        Point center = new Point(WIDTH / 2, HEIGHT / 2);

        int runs = 10000000;

        for (int i = 0; i < runs; i++) {
            int x = center.x
                    - (int) ((Math.random() - Math.random())
                            * Math.random() * center.x);
            int y = center.y
                    - (int) ((Math.random() - Math.random())
                            * Math.random() * center.y);
            widths[x]++;
            heights[y]++;
            repaint();
            g.fillRect(x, y, 1, 1);
            if((x & y) == 101){
                count101++;
            }
            if((x & y) == 100){
                count100++;
            }
            if((x & y) == 99){
                count99++;
            }
        }
        System.out.println(count101);
        System.out.println(count100);
        System.out.println(count99);
        repaint();
        pw.flush();
        pw.close();
    } catch (IOException e) {
        e.printStackTrace();
    }
}
}

这会不断打印有偏差的结果。

它打印以下内容:

3640
10918
3741

这是相当有偏差的,因为它在大多数情况下都遵循趋势,随着所有其他值的线性增加,但是一旦达到 100 标记,它就决定要投下炸弹并再捡起 6%然后每隔一个。

有人知道这其中的原因吗?

哦,顺便说一句,我确实有一个 txt 文件,其中包含以百分比等重复 10,000,000 次打印出来的每个结果,它很长,所以我不会发布,但我确实有信息。

【问题讨论】:

  • 你需要为随机函数播种。
  • 没有按预期打印?这对你来说是随机的......
  • 为什么要对x 和y 进行按位与运算?你期望它有什么意义?还是您认为if((x &amp; y) == 101) 与if ((x == 101) &amp;&amp; (y == 101)) 的含义相同?
  • 你的意思是if((x&amp;y)==101)还是if(x==101 &amp;&amp; y==101)?
  • 你问的是什么?为什么某些位模式的“计数”有偏差?简而言之,您的输入数据中存在偏差,因此该偏差很可能也出现在您的输出数据中。此外,对随机生成的数字执行操作并不能保证结果是随机分布的。实际上,有一种理论认为,您执行的转换越多,结果就越有可能呈正态分布(这可能正是您所看到的)。

标签: java math random


【解决方案1】:

不要使用Math.random(),而是使用java.util.Random。首先,用当前时间播种随机生成器:

Random randGenerator = new java.util.Random(System.currentTimeMillis());

然后随机生成一个接一个。

randGenerator.nextDouble();

【讨论】:

  • Math.random() 很好 - 如果您不提供种子,它会使用当前时间自行播种。
【解决方案2】:

这既不是 Java Math.rand() 也不是伪随机生成问题。这导致了奇怪的(但预期的)行为:

Math.random() - Math.random()

两个分布uniformly 的和(和减法)random variables 不会导致均匀分布的变量。据我记得他们的结果是triangular distribution:

请参阅:Distribution of mean of two standard uniform variables。

这就是您所看到的 - 具有三角形分布的二维随机变量的完美说明。此外,如果你不断添加均匀分布的随机变量,你最终会得到normal distribution。

要实现均匀分布,你所要做的就是替换笨拙的:

int x = center.x
                - (int) ((Math.random() - Math.random())
                        * Math.random() * center.x);

简单:

int x = (int) (Math.random() * center.x * 2);

您的代码(没有乘法)生成随机变量,其可能值从0 到center.x * 2 和expected value 在center.x。到目前为止,一切都很好。但分布是三角形的,这意味着probability density 在该范围内不相等。

两个随机变量的乘积(其中一个不再均匀分布)具有更复杂的分布,但肯定不是均匀的。

后面的代码sn-p生成一个简单的、均匀分布的变量,其概率密度函数在整个空间中相等。

旁注

在您的程序中,这是一个明显的数学错误,但伪随机生成器实际上可以在空间中生成“非随机”模式。看看Strange Attractors and TCP/IP Sequence Number Analysis,图片来自那篇文章:


(来源:coredump.cx)

【讨论】:

  • 感谢使用的建议:(int) (Math.random() * center.x * 2);但这不是我想要的。基本上,我希望它更专注于单击中心(这是用于游戏的宏工具,这是为了确保帐户看起来更“人性化”)。例如,当您单击一个按钮时,您将更频繁地向中间移动。您不会单击边缘上的最后一个像素。我拥有的图片是理想的结果,但没有偏向 100 分。正如你在这里看到的:pastebin.com/8wQuZFhU 我希望它更线性
  • @Legend:哦,我明白了!但这意味着我的答案非常全面,但题外话!看看这些文章:n-sphere 和 Box–Muller transform。
【解决方案3】:

在这种情况下,随机播种并没有多大作用,因为它已经使用 System.nanoTime() 来播种自己:

public Random() { this(++seedUniquifier + System.nanoTime()); }
private static volatile long seedUniquifier = 8682522807148012L;

Math.random() 方法反复使用相同的 Random,并使用默认构造函数来构建它。

问题是您正在对两个改变分布的随机数进行计算。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-05-01
    • 2012-04-24
    • 2023-03-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多