Нормально распределенные случайные числа?

Есть ли хороший способ получить случайно сгенерированные целые числа с нормальным распределением?

Первый способ, который приходит мне в голову:

int rndi = (int)Math.floor(random.nextGaussian()*std);

Есть ли способ лучше?


person liborw    schedule 19.07.2010    source источник
comment
wtf целые числа с нормальным распределением? Пожалуйста, объясните, что вы хотите сделать.   -  person Alexandre C.    schedule 19.07.2010
comment
Я хочу получить случайный элемент из массива, но некоторые элементы с большей вероятностью.   -  person liborw    schedule 19.07.2010
comment
Так что вам, вероятно, нужно биномиальное распределение.   -  person Alexandre C.    schedule 19.07.2010


Ответы (4)


Вы должны обновить вопрос, чтобы прояснить, каков именно ваш вариант использования.

Согласно вашему комментарию, вы вообще не должны использовать нормальное распределение. Вместо этого попробуйте один из многих дискретных дистрибутивов, поскольку вам нужны целые числа в конце. Их много, но я бы порекомендовал один — очень простой. Он использует стохастический вектор в качестве дискретного распределения вероятностей.

Вот пример реализации:

public class DiscreteRandom {

    private final double[] probDist;

    public DiscreteRandom(double... probs) {
        this.probDist = makeDistribution(probs);
    }

    private double[] makeDistribution(double[] probs) {
        double[] distribution = new double[probs.length];
        double sum = 0;
        for (int i = 0; i < probs.length; i++) {
            sum += probs[i];
            distribution[i] = sum;
        }
        return distribution;
    }

    public int nextInt() {
        double rand = Math.random();
        int i = 0;
        while (rand > probDist[i]) i++;
        return i;
    }

    /**
     * Simple test
     */
    public static void main(String[] args) {
        // We want 0 to come 3 times more often than 1.
        // The implementation requires normalized probability
        // distribution thus testProbs elements sum up to 1.0d.
        double[] testProbs = {0.75d, 0.25d};
        DiscreteRandom randGen = new DiscreteRandom(testProbs);

        // Loop 1000 times, we expect:
        // sum0 ~ 750
        // sum1 ~ 250
        int sum0 = 0, sum1 = 0, rand;
        for (int i = 0; i < 1000; i++) {
            rand = randGen.nextInt();
            if (rand == 0) sum0++;
            else           sum1++;
        }
        System.out.println("sum0 = " + sum0 + "sum1 = " + sum1);
    }
}
person Rekin    schedule 19.07.2010
comment
несколько комментариев: (1) используйте TreeMap, переходящий от кумулятивного распределения вероятностей к целым числам, а в nextInt() используйте TreeMap.floorEntry. (2) Ваш подход обрабатывает только N целых чисел от 0 до N-1. Что, если OP хочет 2N+1 целых чисел от -N до +N или N целых чисел от 1000 до 1000+N-1? - person Jason S; 19.07.2010
comment
(1) - отличная оптимизация, мне всегда было интересно, как это сделать правильно. Спасибо. И о (2) я думаю, что какой-то метод упаковки мог бы справиться с этим случаем без лишнего кодирования. - person Rekin; 19.07.2010

Строго говоря, у вас не может быть нормально распределенных целых чисел. Возможно, вам нужен вывод нормального распределения, отсортированного по сегментам. В этом случае вы, вероятно, захотите сместить и масштабировать нормальное распределение в соответствии с размером вашего массива. Если вы просто возьмете выборки из стандартного нормального распределения (среднее значение = 0 и масштаб = 1), вы получите выборки между -2 и 2 примерно в 99% случаев.

Предположим, вам нужны случайные выборки из массива размера N. Вы хотите, чтобы записи в середине выбирались чаще, чем выборки в конце, но вы хотите, чтобы выборки ближе к концам появлялись время от времени, скажем, в 1% случаев. . Затем вы можете вычислить что-то вроде N/2 + N*z/4, где z — ваша стандартная нормаль, а затем преобразовать эти числа в целое число. Если вы сделаете это, вы иногда будете получать индекс за пределами вашего массива. Просто проверьте это и получите новое значение, когда это произойдет.

person John D. Cook    schedule 19.07.2010

Это зависит от того, что вы пытаетесь сделать с этими случайными числами.

У java.util.Random есть некоторые недостатки. Как указано в JavaDoc, метод nextGaussian() использует преобразование Box Muller. Это зависит от Random.nextDouble(), который реализован с использованием линейного конгруэнтного генератора. И реализация не самая лучшая, как указано в предложении по исправлению:

Метод Sun использует 48-битное начальное число и (что касается нижнего бита) обращается только к 17 битам из них, что приводит к чрезвычайно серьезной неслучайности.

Поэтому, если вы заинтересованы в высоком статистическом качестве, вам действительно следует избегать реализации Sun. Взгляните на этот апплет "Не такой случайный", чтобы наглядно убедиться, насколько это плохо. .

Если вас беспокоит статистическое качество, лучшее, что вы можете сделать, это использовать какую-нибудь внешнюю библиотеку PRNG.

person Rekin    schedule 19.07.2010
comment
Патологическое поведение, отмеченное в цитируемой статье, является ограничением битов младшего порядка, создаваемых линейными конгруэнтными генераторами, когда m является степенью двойки; это не относится к реализации Sun. При правильном использовании java.util.Random обеспечивает приемлемые результаты для многих приложений. en.wikipedia.org/wiki/Linear_congruential_generator - person trashgod; 19.07.2010
comment
Конечно, это правильно. Для этого использования этого определенно достаточно. Основной вопрос (без последнего комментария) был расплывчатым, и я поторопился с ответом. - person Rekin; 19.07.2010

Вы можете предварительно вычислить список «случайных» целых чисел, а затем вручную настроить этот список, чтобы получить желаемое распределение.

Затем, когда вам нужен «случайный» номер, просто выберите следующий доступный номер из списка...

Таким образом, вы обеспечиваете распределение и, следовательно, вероятность выбора конкретного элемента. Для удовольствия вы можете просто «перепутать» свой список, когда вам нужно.

person NotMe    schedule 19.07.2010