我的概率/机会系统有什么错误?

时间:2020-02-03 22:17:33

标签: java

我目前正在使用机会系统来计算物品。

LootData是一种对象,它存储要提供的值(称为项目)和获得此“项目”的机会。

一个项目将包含一个ItemStack(一个项目)以及一个double(一个机会)。

我的数学或代码似乎已关闭,因为某些机会较少的“项目”被更频繁地给出。

...

2 个答案:

答案 0 :(得分:0)

首先,我不同意一些评论-getChance()返回一个数字,对于您使用的算法,它是否为intdouble都无关紧要,也不是什么范围,等等。列表中项目的顺序也不会影响结果。

我包装了您提供的带有测试循环的代码(getChance()返回双精度值),但结果是我看不到低机会项始终返回不适当的金额。

List<LootData> items = Arrays.asList(new LootData(0.1), new LootData(0.1), new LootData(0.4), new LootData(0.3),
    new LootData(0.1)

);
int numberOfItems = 3;

int NUM_TESTS = 100;

// Calculate total chance
double totalChance = 0;
for (LootData item : items)
  totalChance += item.getChance();

for (int testNumber = 0; testNumber < NUM_TESTS; testNumber++) {
  for (int i = 0; i < numberOfItems; i++) {
    // Select a 'random chance' from 0 to total chance
    double randomChance = Math.random() * totalChance;

    // Check which item the 'random chance' landed on
    for (LootData item : items) {
      randomChance -= item.getChance();

      if (randomChance < 0) {
        System.out.println("Selected item " + item + " for " + testNumber + ", " + i);
        item.incrementCount();
        break;
      }
    }
  }
}

for (LootData item : items) {
  System.out.println(item.getChance() + " count=" + item.getCount() + "  expected="
      + Math.round(NUM_TESTS * numberOfItems * item.getChance() / totalChance));
}

典型结果:

0.1 count=33  expected=30
0.1 count=24  expected=30
0.4 count=118  expected=120
0.3 count=88  expected=90
0.1 count=37  expected=30

没有什么超出预期的可变性-例如,后续运行的最后一项返回的值小于30。

编辑添加:

再想一想,尽管您上面的算法很好,但是问题可能出在您的期望和/或如何构建结果列表上。

我假设您正在构建包含唯一项目的结果列表-也就是说,一旦将项目添加到ItemStack,就不会再添加它。因此,考虑到这一点,让我们举一个极端的例子-4个非常低机会的项目和一个大机会的项目(所以总共5个),返回numberOfItems = 3的ItemStack。

大概率项目将首先被选中的可能性。一旦发生这种情况,该项目的零概率就会再次被选中。这意味着将为剩余的2个时隙选择低概率项目。 在多次运行中,那些低机会项目将被多次选择以填补剩余的空缺,从而使您的预期概率产生偏差。

另一个要考虑的因素是如何确保唯一性-如果randomChance恰好指向已被选中的大概率项目该怎么办? 您是否简单地进入下一个项目?如果是这样,则与其他任何低机会项目相比,该项目固有地变得更有可能被选择-再次使概率倾斜。您需要做的是为每个位置计算totalChance-例如,更改此位置:

double totalChance = 0;
for (LootData item: items)
    totalChance += item.getChance();

for (int i = 0; i < numberOfItems; i++) {
    // Select a 'random chance' from 0 to total chance
    double randomChance = Math.random() * totalChance;

    // Check which item the 'random chance' landed on
    for (LootData item: items) {
        randomChance -= item.getChance();

        if (randomChance < 0) {

对此:

for (int i = 0; i < numberOfItems; i++) {
    double totalChance = 0;
    for (LootData item: items) {
        if (itemsToGive.doesNotInclude(item)) {
            totalChance += item.getChance();
        }
    }

    // Select a 'random chance' from 0 to total chance
    double randomChance = Math.random() * totalChance;

    // Check which item the 'random chance' landed on
    for (LootData item: items) {
        if (itemsToGive.doesNotInclude(item)) {
            randomChance -= item.getChance();

            if (randomChance < 0) {

答案 1 :(得分:0)

代码看起来不错,结果似乎合理。

也许是转换问题?在不同的数字类型之间切换可能会在意想不到的地方截断值。

以下是示例输出:

Item [  0 ] Freq [   0 ] Odds [ 0.012 ] Proportion [ 0.000 ]
Item [  1 ] Freq [   2 ] Odds [ 0.012 ] Proportion [ 0.020 ]
Item [  2 ] Freq [  14 ] Odds [ 0.122 ] Proportion [ 0.140 ]
Item [  3 ] Freq [  12 ] Odds [ 0.122 ] Proportion [ 0.120 ]
Item [  4 ] Freq [  36 ] Odds [ 0.366 ] Proportion [ 0.360 ]
Item [  5 ] Freq [  36 ] Odds [ 0.366 ] Proportion [ 0.360 ]

这是我写的算法和测试代码:

public class RandomTest {

    public static final int[] lootChance = new int[] { 1, 1, 10, 10, 30, 30 };
    public static final int totalLootChance;
    static {
        int total = 0;
        for ( int chance : lootChance ) {
            total += chance;
        }
        totalLootChance = total;
    }

    public static int lookupItem(double selection) {
        for ( int lootNo = 0; lootNo < lootChance.length; lootNo++ ) {
            selection -= lootChance[lootNo];
            if ( selection < 0 ) {
                return lootNo;
            }
        }
        throw new IllegalArgumentException("Selection [ " + selection + " ] out of range [ " + totalLootChance + " ]");
    }

    public static final int lootRuns = 100;

    public static void main(String[] args) {
        System.out.printf("Items [ %2d ] Weighed Total [ %3d ]\n", lootChance.length, totalLootChance);
        for ( int itemNo = 0; itemNo < lootChance.length; itemNo++) {
            System.out.printf("  [ %2d ] [ %3d ]\n", itemNo, lootChance[itemNo]);
        }

        int[] distribution = new int[ lootChance.length ];

        for ( int runNo = 0; runNo < lootRuns; runNo++ ) {
            double selection = Math.random() * totalLootChance;
            int itemSelection = lookupItem(selection);
            distribution[itemSelection]++;

            System.out.printf(
                "Run [ %3s ] Roll [ %04.2f ] Item [ %2d ]\n",
                runNo, selection, itemSelection);
        }

        for ( int lootNo = 0; lootNo < lootChance.length; lootNo++ ) {
            System.out.printf(
                "Item [ %2d ] Freq [ %3d ] Odds [ %02.3f ] Proportion [ %02.3f ]\n",
                lootNo,
                distribution[lootNo],
                (((double) lootChance[lootNo]) / totalLootChance),
                ((double) distribution[lootNo]) / lootRuns);
        }
    }
}