我目前正在使用机会系统来计算物品。
LootData是一种对象,它存储要提供的值(称为项目)和获得此“项目”的机会。
一个项目将包含一个ItemStack(一个项目)以及一个double(一个机会)。
我的数学或代码似乎已关闭,因为某些机会较少的“项目”被更频繁地给出。
...
答案 0 :(得分:0)
首先,我不同意一些评论-getChance()
返回一个数字,对于您使用的算法,它是否为int
,double
都无关紧要,也不是什么范围,等等。列表中项目的顺序也不会影响结果。
我包装了您提供的带有测试循环的代码(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);
}
}
}