Моделирование Монте-Карло

Я учусь в классе программирования на Java. Моя проблема связана с интерпретацией моделирования Монте-Карло. Я должен найти вероятность того, что три четверти или три пенни будут выбраны из кошелька, в котором есть три четверти и 3 пенни. Как только монета взята, она не возвращается. Вероятность должна быть 0,1XXXXXXX. Я получаю 0 или 1 за свой ответ. Это то, что у меня есть до сих пор.

public class CoinPurse {
    public static void main(String[] args) {
        System.out.print("Probability of Drawing 3 coins of the Same Type - ");
        System.out.println(coinPurseSimulation(100));
    }

    /**
     Runs numTrials trials of a Monte Carlo simulation of drawing 
     3 coins out of a purse containing 3 pennies and 3 quarters. 
     Coins are not replaced once drawn.
     @param numTrials - the number of times the method will attempt to draw 3 coins
     @returns a double - the fraction of times 3 coins of the same type were drawn.
     */

    public static double coinPurseSimulation(int numTrials) {
        final int P = 1;
        final int Q = 2;
        int [] purse = {Q, Q, Q, P, P, P};
        int [] drawCoins = new int[3];
        for (int draw = 0; draw < 3; draw ++) {
            int index = (int)(Math.random() * purse.length);
            drawCoins[draw] = purse[index];
            int [] newPurse = new int[purse.length-1];
            int j = 0;
            for (int i =0; i < purse.length; i++) {
                if (i == index) {
                    continue;
                }
                newPurse[j] = purse[i];
                j++;
            }
            purse = newPurse;
        }
        double number = 0.0;
        double result = 0.0;
        for (int i = 0; i < numTrials; i++) {
            result++;
            for (int j = 0; j < numTrials;j++) {
                if(drawCoins[0] == drawCoins [1] && drawCoins[1] == drawCoins[2]) {
                    number++;
                }
            }
        }
        return number/result;
    }
}

person C516    schedule 13.04.2015    source источник


Ответы (2)


Причина, по которой вы получаете только 0 или 1, заключается в том, что вы только берете (или забираете) монеты из кошелька только один раз, но затем вы проверяете, что нарисовать numTrials * numTrials раз. У вас есть два цикла (с индексами i и j), повторяющих numTrials раз - ваша логика здесь немного запуталась.

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

public class CoinPurse
{
    public static void main(String[] args)
    {
        System.out.print("Probability of Drawing 3 coins of the Same Type - ");
        System.out.println(coinPurseSimulation(100));
    }

    /**
     * Runs numTrials trials of a Monte Carlo simulation of drawing 3 coins out
     * of a purse containing 3 pennies and 3 quarters. Coins are not replaced
     * once drawn.
     * 
     * @param numTrials
     *            - the number of times the method will attempt to draw 3 coins
     * @returns a double - the fraction of times 3 coins of the same type were
     *          drawn.
     */

    public static double coinPurseSimulation(int numTrials)
    {
        final int P = 1;
        final int Q = 2;

        double number = 0.0;
        double result = 0.0;

        // Changed your loop index to t to avoid conflict with i in your draw
        // loop
        for (int t = 0; t < numTrials; t++)
        {
            result++;

            // Moved your draw without replacement code here
            int[] purse =
            { Q, Q, Q, P, P, P };
            int[] drawCoins = new int[3];

            for (int draw = 0; draw < 3; draw++)
            {
                int index = (int) (Math.random() * purse.length);
                drawCoins[draw] = purse[index];
                int[] newPurse = new int[purse.length - 1];
                int j = 0;
                for (int i = 0; i < purse.length; i++)
                {
                    if (i == index)
                    {
                        continue;
                    }
                    newPurse[j] = purse[i];
                    j++;
                }
                purse = newPurse;
            }

            // Deleted the loop with index j - you don't need to test the same
            // combination numTrials times...
            if (drawCoins[0] == drawCoins[1] && drawCoins[1] == drawCoins[2])
            {
                number++;
            }
        }

        return number / result;
    }
}

Код сбора монет

У меня есть несколько комментариев по поводу вашей маршрутизации для розыгрыша монет:

  1. Работает правильно
  2. Это довольно громоздко
  3. Вам было бы легче обнаружить проблему, если бы вы разбили этот фрагмент кода на отдельный метод.

Я собираюсь обратиться к 3, а затем к 2.

Разбейте код рисования на метод

private static int[] pickCoins(int[] purse, int numPicks)
{
    //A little error check
    if (numPicks > purse.length)
    {
        System.err.println("Can't pick " + numPicks + 
                           " coins from a purse with only " + purse.length + " coins!");
    }

    int[] samples = new int[numPicks];

    // Your sampling code here

    return samples;
}

Теперь вы можете просто позвонить из своего второго цикла, т.е.

drawCoins = pickCoins(purse, 3);

Алгоритм выборки

Ответ @ pjs рекомендует использовать Collections.shuffle(), а затем взять первые 3 монеты в вашей коллекции (например, ArrayList). Это хорошее предложение, но я предполагаю, что вы еще не знакомы с Collections, и, возможно, вам не разрешено их использовать. Если да - воспользуйтесь ими. Если нет (как я предполагаю), вы можете подумать о лучших способах случайного извлечения n элементов из массива длины r без замены.

Один (хорошо принятый) способ - это перемешивание Фишера-Йейтса и его производные. По сути, он включает случайный выбор из неотобранного подмножества массива.

В Java - рабочий пример может быть следующим - он работает, перемещая выбранные монеты в «конец» кошелька и собирая только первые maxInd неотобранные монеты.

    private static int[] pickCoins(int[] purse, int numCoins)
    {
        int[] samples = new int[numCoins];
        int maxInd = purse.length - 1;

        for (int i = 0; i < numCoins; i++)
        {
            int index = (int) (Math.random() * maxInd);
            int draw = purse[index];
            samples[i] = draw;
            // swap the already drawn sample with the one at maxInd and decrement maxInd
            purse[index] = purse[maxInd];
            purse[maxInd] = draw;
            maxInd -= 1;
        }

        return samples;
    }

Ожидаемые результаты

Вы говорите, что ваш ожидаемый результат 0.1XXXXXXX. Когда вы изучаете моделирование Монте-Карло, вам, возможно, придется подумать об этом немного больше. Ожидаемый результат зависит от того, сколько испытаний вы сделаете.

Во-первых, в этом простом примере вы можете рассмотреть аналитический (или в некотором смысле точный) результат. Рассмотрим процедуру:

  1. Вы берете свою первую монету - неважно, какая это
  2. Какая бы монета ни была, в сумке осталось 2 одинаковых - вероятность выбрать одну из них 2 / 5.
  3. Если вы выбрали одну из совпадающих монет на шаге 2, теперь в сумке осталась 1 соответствующая монета. Вероятность выбора этого 1 / 4
  4. Таким образом, вероятность получить 3 совпадающих монеты (любого достоинства) составляет 2 / 5 * 1 / 4 == 2 / 20 == 0.1.

Ваша программа Монте-Карло пытается оценить эту вероятность. Можно ожидать, что он сходится на 0,1 при достаточных оценках (то есть при достаточно высоком numTrials). Он не всегда дает значение, равное или даже начинающееся с 0.1. При достаточном количестве испытаний это может дать что-то начиная с 0.09 или 0.1. Однако, если numTrials == 1, он выдаст либо 0, либо 1, потому что он будет рисовать один раз, и ничья либо совпадет, либо нет. Если numTrials == 2, результаты могут быть только 0, 0.5 или 1 и так далее.

Один из уроков моделирования методом Монте-Карло для оценки вероятностей - иметь достаточно большое количество выборок, чтобы получить хорошую оценку. Это, в свою очередь, зависит от требуемой точности - вы можете использовать свой код, чтобы исследовать это, когда он заработает.

person J Richard Snape    schedule 13.04.2015

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

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

Если все сделано правильно, ответ должен быть 2 * (3/6) * (2/5) * (1/4), что составляет 0,1.

person pjs    schedule 13.04.2015