8

我正在用 Java 开发一款太空战斗游戏,作为学习该语言的持续努力的一部分。在一场战斗中,我有k艘船向他们的n个邪恶敌人的舰队开火。取决于他们的敌人被多少次射击击中(每艘船发射一发子弹击中一个敌人),有些会受到伤害,有些会被摧毁。我想弄清楚有多少敌人被击中一次,多少个敌人被击中两次等等,所以最后我有一个看起来像这样的桌子,发射了 100 枪:

Number of hits | Number of occurences | Total shots
----------------------------------------------------
       1       |        30            |      30
       2       |        12            |      24
       3       |         4            |      12
       4       |         7            |      28
       5       |         1            |       5

显然,我可以通过将每次射击随机放置在一个敌人身上,然后计算每次射击最后的次数来对少量射击和敌人进行暴力破解。然而,如果我有 300 万无畏的英雄向 1000 万敌人开火,这种方法将非常不切实际。

理想情况下,我想要的是一种方法来生成有多少敌人可能被恰好一定数量的射击击中的分布。然后我可以使用随机数生成器在该分布上选择一个点,然后重复这个过程,每次增加命中数,直到几乎所有镜头都被计算在内。是否有一般的统计分布/方法来估计大约有多少敌人被多少枪击中?

我一直在尝试从生日问题中找出一些东西,以弄清楚有多少人分享了多少个生日的概率,但没有取得任何重大进展。

我将在 Java 中实现它。

编辑:我发现了一个可能更容易解决的简化:n 个敌人根本没有被击中的概率分布是什么?即,零未命中、一未命中、二未命中等的概率是多少?

这是一个类似的问题,(好吧,同样的问题,但经过了简化),但似乎它可能更容易解决,并且可以让我在几次迭代中生成完整的分布。

4

4 回答 4

2

您应该看一下多项分布,将其限制在所有p i都等于1/k的情况下(注意维基百科文章交换了您的kn的含义)。


以前的回答尝试

也许像下面这样的方法将是富有成效的:

  1. 特定船被特定射击击中的概率是1/n
  2. 一艘给定的船在 k 次射击后恰好被击中一次的概率:h 1 = 1/n (1-1/n) k-1
  3. 同上,但正好是两次:h 2 = (1/n) 2 (1-1/n) k-2,依此类推;
  4. 预期的船只数量恰好命中一次:nh 1等等。
于 2013-05-08T16:01:09.753 回答
2

我假设每次射击都有概率 h 击中任何坏船。如果 h = 0,则所有镜头都将丢失。如果 h = 1,所有的镜头都会击中某物

现在,假设你射了 b 颗子弹。被击中的船只的期望值只是 Hs = h * b,但这些并不是唯一被击中的船只。

所以我们有一个 Hs 长的船列表。给定 N 艘敌舰,任何特定敌舰被击中的几率为 1/N。因此,在前 k 个时隙中但不在其他时隙中的机会是

(1/N)^k * (1-1/N)^(Hs-k)  

请注意,这是 Marko Topolnik 的回答。问题是这是一艘特定的船,它位于第一个 k 个插槽中,而不是位于 k 个插槽的任何组合中。我们必须通过考虑 Hs 总时隙中 k 个时隙的组合数来修改它:

(Hs choose k) * (1/N)^k * (1-1/N)^(Hs-k)

现在我们有机会在 k 个槽位中找到一艘特定的船。好吧,现在我们需要考虑整个 N 艘船的舰队:

(Hs choose k) * (1/N)^k * (1-1/N)^(Hs-k) * N

这个表达式表示在一个 N 大小的舰队中被击中 k 次的预期船只数量,该舰队被均匀分布的 Hs 击中。

数值健全性检查:

假设两颗子弹击中(Hs=2),我们有两艘敌舰(N=2)。为每艘船分配一个二进制 ID,让我们列举可能的命中列表。

00 (ship 0 hit twice)
01
10
11

一次击中的舰船数量为:

(2 choose 1) * (1/2)^1 * (1-1/2)^(2-1) * 2 = 1

两次被击中的舰船数量为:

(2 choose 2) * (1/2)^2 * (1-1/2)^(2-2) * 2 = 0.5

为了完成完整性检查,我们需要确保我们的总命中数等于 Hs。每艘被击中两次的船需要 2 发子弹,每艘被击中一次的船需要 1 发子弹:

1*1 + 0.5*2 = 2 == Hs  **TRUE**

Hs=3 和 N=2 的另一个快速示例:

(3 choose 1) * (1/2)^1 * (1-1/2)^(3-1) * 2
3 * 0.5 * 0.25 * 2 = 0.75

(3 choose 2) * (1/2)^2 * (1-1/2)^(3-2) * 2
3 * 0.5^2 * 0.5 * 2 = 0.75

(3 choose 3) * (1/2)^3 * (1-1/2)^(3-3) * 2
1 * 0.5^3 * 1 * 2 = 0.25

0.75 + 0.75*2 + 0.25*3 = 3 == Hs  **TRUE**
于 2013-05-09T04:27:11.403 回答
2

如果你有 S 艘船并向它们开 A 枪,每艘船的命中数将遵循二项分布,其中 p = 1/S 和 n = A:

http://en.wikipedia.org/wiki/Binomial_distribution

您可以查询此分布并询问:

  • 一艘船被击中0次的可能性有多大?
  • 一艘船被击中 1 次的可能性有多大?
  • 一艘船被击中 2 次的可能性有多大?
  • 一艘船被击中(最大生命值)或更多次的可能性有多大?(提示:只需从以下所有内容中减去 1.0)

并将这些乘以船舶数量 S,得到您期望被击中 0、1、2、3 等次的船舶数量。但是,由于这不是随机滚动的结果,因此每次战斗都会以完全相同的方式进行。

如果您的舰艇数量较少但射击次数较多,您可以在每艘舰艇上滚动一次二项式分布。或者,如果您的射击数量较少但船只数量较多,您可以随机放置每个射击。我还没有想到一种很酷的方法来获得大量镜头和大量镜头的随机分布(或其随机近似值),但是找到一个会很棒:)

于 2013-05-09T04:36:51.080 回答
1

想出了解决这个问题的方法,最后开始用Java编写它。这给出了一个精确的解决方案,用于计算在给定船只和射击的情况下m船只未被击中的概率。然而,它在计算上是相当昂贵的。首先,总结一下我所做的事情:kn

概率等于射击完全m未命中的船只的方式总数除以射击船只的方式总数。

P = m_misses / total

总计是k^n,因为每次射击都可以击中一k艘船。

要获得分子,请从 开始nCr(k,m)。这是选择m不被击中的船只的方式的数量。这乘以k-m没有错过任何击中船只的方式的数量就是总概率。

     nCr(k,m)*(k-m_noMiss)
 P = ---------------------
             k^n

现在计算分子中的第二项。这是所有镜头分布的总和,即某个镜头分布发生的方式有多少。例如,如果 2 艘船被 3 颗子弹击中,并且每艘船至少被击中一次,则可以通过以下方式击中它们:

100
010
001
110
101
011

炮弹分布等于 k ​​的长度 km 组成。在这种情况下,我们将有 [2,1] 和 [1,2],长度为 2 的 3 组合。

对于第一个构图,[2,1],我们可以通过从 3 次射击中选择 2 次击中第一艘船,然后从剩余 1 次中选择 1 次击中第二艘来计算生成它的方式的数量,即nCr(3,2) * nCr(1,1). 请注意,我们可以将其简化为3!/(2!*1!). 该模式适用于所有镜头模式,因此某个模式 ,p可以出现的方式数可以写为n!/prodSum(j=1,k-m,p_j!),其中符号表示从 1 到 的乘积和k-mj是一个索引,p_j表示 中的j第 项p

如果我们定义P为 的所有长度k-m组合的集合,则船舶未被击中n的概率为:m

     nCr(k,m)*sum(p is an element of P, n!/prodSum(j=1,k-m,p_j!))
 P = --------------------------------------------------------------
                                 k^n

符号有点草率,因为没有办法将数学符号方程放入 SO,但这就是它的要点。

话虽如此,这种方法效率极低,但我似乎找不到更好的方法。如果有人可以简化这一点,请务必发布您的方法!我很好奇它是如何做到的。

以及用于执行此操作的 java 代码:

import java.util.ArrayList;
import java.util.Arrays;
import org.apache.commons.math3.util.ArithmeticUtils;

class Prob{
    public boolean listsEqual(Integer[] integers, Integer[] rootComp){
        if(integers.length != rootComp.length){
            return false;
        }
        for (int i = 0; i < integers.length; i++){
            if(integers[i] != rootComp[i]){return false;};
        }       
        return true;
    }

    public Integer[] firstComp(int base, int length){
        Integer[] comp = new Integer[length];
        Arrays.fill(comp, 1);
        comp[0] = base - length + 1;
        return comp;        
    }

    public Integer[][] enumerateComps(int base, int length){
        //Provides all compositions of base of size length

        if(length > base){return null;};
        Integer[] rootComp = firstComp(base, length);
        ArrayList<Integer[]> compsArray = new ArrayList<Integer[]>();

        do {
            compsArray.add(rootComp);
            rootComp = makeNextComp(rootComp);
        } while(!listsEqual(compsArray.get(compsArray.size() - 1), rootComp));

        Integer[][] newArray = new Integer[compsArray.size()][length];

        int i = 0;
        for (Integer[] comp : compsArray){
            newArray[i] = comp;
            i++;
        }

        return newArray;
    }

    public double getProb(int k, int n, int m){
        //k = # of bins
        //n = number of objects
        //m = number of empty bins

        //First generate list of length k-m compositions of n

        if((n < (k-m)) || (m >= k)){
            return 0;
        }

        int[] comp = new int[n-1];

        Arrays.fill(comp, 1);

        comp[0] = n - (k-m) + 1;

        //Comp is now the first 
        Integer[][] L = enumerateComps(n, k-m);

        double num = 0;
        double den = Math.pow(k, n);
        double prodSum;
        int remainder;

        for(Integer[] thisComp : L){
            remainder = n;
            prodSum = 1;
            for(Integer thisVal : thisComp){
                prodSum = prodSum * ArithmeticUtils.binomialCoefficient(remainder, thisVal);

                remainder -= thisVal;
            }

            num += prodSum;
        }

        return num * ArithmeticUtils.binomialCoefficient(k, m) / den;
    }

    public Integer[] makeNextComp(Integer[] rootComp){
        Integer[] comp = rootComp.clone();

        int i = comp.length - 1;
        int lastVal = comp[i];
        i--;

        for(; i >=0 ; i--){
            if (comp[i] != 1){
                //Subtract 1 from comp[i]
                comp[i] -= 1;
                i++;
                comp[i] = lastVal + 1;
                i++;
                for(;i < comp.length; i++){
                    comp[i] = 1;
                };
                return comp;                
            }
        }
        return comp;
    }
}


public class numbersTest {
    public static void main(String[] args){
        //System.out.println(ArithmeticUtils.binomialCoefficient(100,50));
        Prob getProbs = new Prob();

        Integer k = 10; //ships
        Integer n = 10; //shots
        Integer m = 4; //unscathed

        double myProb = getProbs.getProb(k,n,m);

        System.out.printf("Probability of %s ships,  %s hits, and %s unscathed: %s",k,n,m,myProb);
    }
}
于 2013-05-21T22:26:12.280 回答