Нормално разпределени произволни цели числа?

Има ли хубав начин да получите произволно генерирани цели числа с нормално разпределение?

Първият метод, който ми идва наум:

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 Transform. Зависи от 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