Математика # случайно не так уж случайно? - PullRequest
5 голосов
/ 15 марта 2012

Я нахожу что-то странное, что происходит с моей программой.

Эта программа в основном используется для щелчка компонентов, так как это концепция для проверки случайности.

enter image description here

Как вы можете видеть, она печатается правильно, так какона должна иметь тенденцию щелкать по центру, что идеально.

Проблема в том, что она кажется предвзятой.

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% больше, чем все остальные.

Кто-нибудь знает какие-либо причины для этого?

О, кстати, у меня есть текстовый файл, содержащий каждый напечатанный результатповторение 10 000 000 раз с процентами и т. д., это довольно долго, поэтому я не буду публиковать, но у меня есть информация.

Ответы [ 3 ]

16 голосов
/ 15 марта 2012

Это не проблема Java Math.rand() или псевдослучайная генерация. Это вызывает странное (но ожидаемое) поведение:

Math.random() - Math.random()

Сумма (и вычитание) двух равномерно распределенных случайных величин не приводит к равномерно распределенной переменной. Насколько я помню, они приводят к треугольному распределению :

triangular 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 и с ожидаемым значением при center.x. Все идет нормально. Но распределение является трангулярным, что означает, что плотность вероятности не равна в этом диапазоне.

Умножение двух случайных величин (из которых одна больше не является равномерно распределенным) имеет еще более сложное распределение, но, безусловно, не равномерно.

Последний фрагмент кода генерирует простые, равномерно распределенные переменные с функцией плотности вероятности, равной во всем пространстве.

примечание

В вашей программе это очевидная математическая ошибка, но псевдослучайный генератор может фактически генерировать «неслучайные» шаблоны в пространстве. Взгляните на Странные аттракторы и анализ порядковых номеров TCP / IP , изображение из этой статьи:

3d-пространство http://lcamtuf.coredump.cx/oldtcp/tcpseq/data.jpg

2 голосов
/ 15 марта 2012

Вместо использования Math.random() используйте java.util.Random. Сначала запустите генератор случайных чисел с текущим временем:

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

затем генерируйте одно случайное число за другим.

randGenerator.nextDouble();
0 голосов
/ 16 марта 2012

Заполнение случайного числа в этом случае не имеет большого значения, поскольку оно уже использует System.nanoTime () для заполнения себя:

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

Метод Math.random () повторно использует один и тот же Random снова и снова и использует конструктор по умолчанию для его построения.

Проблема в том, что вы выполняете вычисления для двух случайных чисел, которые меняют распределение.

...