蒙特卡罗模拟

时间:2015-04-13 00:24:32

标签: java simulation probability montecarlo

我是Java编程课程的学生。我的问题涉及对蒙特卡罗模拟的解释。我应该发现从三分钱和三分钱的钱包中挑出四分之三或三分钱的概率。一旦硬币被挑选,它就不会被替换。概率应为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;
    }
}

2 个答案:

答案 0 :(得分:4)

您获得01的原因是您只能从钱包中绘制(或选择)硬币一次,但然后你 test 绘制numTrials * numTrials次。你有两个循环(索引ij)迭代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. 如果您将这段代码分解为单独的方法,那么您可以更轻松地发现问题。
  4. 我要去地址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's answer建议使用Collections.shuffle(),然后获取收藏中的前3个硬币(例如ArrayList)。这是一个很好的建议,但我猜你还没有被介绍给Collections,而且可能不会被允许&#39;使用它们。如果你是 - 确实使用它们。如果不是(我假设),您可能想要考虑更好的方法从r长度数组中随机绘制n个项目而无需替换。

    一种(被广泛接受的)方式是Fisher-Yates shuffle及其衍生物。实际上,它涉及从数组的 未打开的 子集中随机选取。

    在Java中 - 一个工作示例可以如下 - 它通过将拾取的硬币移动到&#34; end&#34;钱包和只从第一个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
    5. 您的蒙特卡洛计划正在尝试估算该概率。给定足够的估计值(即numTrials足够高),你会期望它<0.1>收敛。它不会总是给出与0.1相等甚至开始的值。通过足够数量的试验,它可能会提供从0.090.1开始的内容。但是,如果numTrials == 1,它会提供01,因为它会绘制一次,并且绘制将匹配与否。如果numTrials == 2,结果只能是00.51,依此类推。

      进行蒙特卡罗模拟以估算概率的一个教训是获得足够高的样本数以获得良好的估计。这反过来取决于您想要的准确性 - 您可以使用您的代码在其工作后进行调查。

答案 1 :(得分:0)

您需要将生成的循环向下移动到numTrials循环中。您编写它的方式是进行单次绘制,然后检查一个结果numTrials次。

我没有仔细检查你的绘图的逻辑,但那是因为我推荐了一种不同的(更简单的)方法。在你的宿舍和便士上使用Collections.shuffle(),并在每次洗牌后检查前三个元素。

如果操作正确,答案应为2 * (3/6) * (2/5) * (1/4),即0.1。