如何在项目列表枚举上执行百分比?



我正在寻找一些关于如何为我的游戏做百分比事情的提示,我想要 1-98 范围内的所有花朵和 99-100 的白色/黑色花朵,以使其更加稀有,感谢您的帮助:)

public enum FlowerSuit {
WHITE_FLOWERS("white", ":white:", "470419377456414720", 1),
YELLOW_FLOWERS("yellow", ":yellow:", "470419561267855360", 1 ),
RED_FLOWERS("red", ":red:", "470419583250202644", 1),
RAINBOW_FLOWERS("rainbow", ":rainbow:", "470419602841665536", 1),       
PASTEL_FLOWERS("pastel", ":pastel:", "470419629450199040", 1),
ORANGE_FLOWERS("orange", ":orange:", "470419647900942366", 1),
BLUE_FLOWERS("blue", ":blue:", "470419688753594368", 1),
BLACK_FLOWERS("black", ":black:", "470419706751352842", 1);
private final String displayName;
private final String emoticon;
private int value;
private final String id;

FlowerSuit(String displayName, String emoticon, String id, int value ) {
this.displayName = displayName;
this.emoticon = emoticon;
this.value = value;
this.id = id;
}
public String getDisplayName() {
return displayName;
}
public String getEmoticon() {
return emoticon;        
}
public String getId() {
return id;
}
public int getValue() {
// TODO Auto-generated method stub
return value;
}

}

这就是我的做法,但对于初学者来说,通过使用 Java 8 流等,它可能可以改进。

public enum FlowerSuit {
WHITE_FLOWERS("white", ":white:", "470419377456414720", 1,3),
YELLOW_FLOWERS("yellow", ":yellow:", "470419561267855360", 1,2),
RED_FLOWERS("red", ":red:", "470419583250202644", 1,2),
RAINBOW_FLOWERS("rainbow", ":rainbow:", "470419602841665536", 1,2),       
PASTEL_FLOWERS("pastel", ":pastel:", "470419629450199040", 1,2),
ORANGE_FLOWERS("orange", ":orange:", "470419647900942366", 1,2),
BLUE_FLOWERS("blue", ":blue:", "470419688753594368", 1,2),
BLACK_FLOWERS("black", ":black:", "470419706751352842", 1,1);
private static Random random = new Random();
private final String displayName;
private final String emoticon;
private int value;
private final String id;
private final int freq;
private FlowerSuit(String displayName, String emoticon, String id, int value, int freq ) {
this.displayName = displayName;
this.emoticon = emoticon;
this.value = value;        
this.id = id;
this.freq = freq;
}
public String getDisplayName() {return displayName;}
public String getEmoticon() {return emoticon;}
public String getId() {return id;}
public int getValue() {return value;}
/**
* Choose a flower
* white has a 3 in 16 (about a 5:1) chance of being picked
* Black has a 1 in 16 chance, everything else 2/16
* @return
*/
public static FlowerSuit pick() {
//first sum all the chances (currently it's 16)
int sum = 0;
for (FlowerSuit f:FlowerSuit.values()) sum+= f.freq;
//now choose a random number
int r = FlowerSuit.random.nextInt(sum) + 1;
//now find out which flower to pick
sum = 0;
for (FlowerSuit f:FlowerSuit.values()) {
sum += f.freq;
if (r<=sum) return f;           
}
//code will never get here
return FlowerSuit.WHITE_FLOWERS;
}

public static void main(final String[] args) throws Exception {
//Test it
Map<FlowerSuit,Integer>count = new HashMap<FlowerSuit,Integer>();       
for (int a=0;a<1000000;a++) {
FlowerSuit f = FlowerSuit.pick();
Integer i = (count.get(f)!=null)?count.get(f):new Integer(0);
i = new Integer(i+1);
count.put(f,i);
}
int sum = 0;
for (Map.Entry<FlowerSuit,Integer>e:count.entrySet()) sum+=e.getValue();
float f = Float.valueOf(sum);
for (Map.Entry<FlowerSuit,Integer>e:count.entrySet()) {
System.out.println(e.getKey() + " was chosen " + ((e.getValue() / f) * 100f) + "% of the time");
}       
}    

}

BLUE_FLOWERS被选中的概率为12.4986%

PASTEL_FLOWERS有12.4707%的时间被选中

WHITE_FLOWERS被选中的概率为18.7365%

BLACK_FLOWERS 在 6.2632003% 的时间内被选中

ORANGE_FLOWERS有12.4986%的时间被选中

RED_FLOWERS 在 12.5241995% 的时间内被选中

YELLOW_FLOWERS 在 12.501401% 的时间内被选中

RAINBOW_FLOWERS有12.5068%的时间被选中

您可以使用TreeMap将 0 到 99 的所有整数映射到特定FlowerSuit。 利用floorEntry方法为每个数字选择一个FlowerSuit。 它可能看起来像这样。

public class FlowerChooser {
private static final NavigableMap<Integer, FlowerSuit> FLOWER_SUITS;
private static final Random RANDOMS = new Random();
public FlowerChooser() {
FLOWER_SUITS = new TreeMap<>();
FLOWER_SUITS.put(0, FlowerSuit.RED_FLOWERS);
FLOWER_SUITS.put(14, FlowerSuit.ORANGE_FLOWERS);
FLOWER_SUITS.put(28, FlowerSuit.YELLOW_FLOWERS);
FLOWER_SUITS.put(42, FlowerSuit.GREEN_FLOWERS);
FLOWER_SUITS.put(56, FlowerSuit.BLUE_FLOWERS);
FLOWER_SUITS.put(70, FlowerSuit.INDIGO_FLOWERS);
FLOWER_SUITS.put(84, FlowerSuit.VIOLET_FLOWERS);
FLOWER_SUITS.put(98, FlowerSuit.WHITE_FLOWERS);
FLOWER_SUITS.put(99, FlowerSuit.BLACK_FLOWERS);
}
public FlowerSuit randomFlowerSuit() {
int index = RANDOMS.nextInt(100);
return FLOWER_SUITS.floorEntry(index).getValue();
}
}

只创建此类的一个对象,然后每当你想要一个FlowerSuit时,调用randomFlowerSuit方法。

randomFlowerSuit方法从 0 到 99 中选取一个随机数,然后在映射中查找适当的条目。floorEntry方法选择键小于或等于所选数字的条目。 这意味着从 0 到 13 的数字映射到红色,从 14 到 27 映射到橙色,依此类推。 映射到白色的唯一数字是 98,映射到黑色的唯一数字是 99。

无论您实现什么解决方案,您都希望在枚举中包含频率度量。 例如,您可以执行以下操作:

public enum FlowerSuit {
WHITE_FLOWERS("white", ":white:", "470419377456414720", 1, 1),
YELLOW_FLOWERS("yellow", ":yellow:", "470419561267855360", 1, 20),
// More declarations here
// Add this variable
private final int frequency;
// Do just as you did before in the constructor, but with the frequency
FlowerSuit(String displayName, String emoticon, String id, int value, int frequency){
this.frequency = frequency;
// More assignments here
} 
public int getFrequency(){
return frequency;
}
// More getters here
}

此添加至关重要,无论您使用哪种方法来加权花卉选择,您都会希望将此添加添加到您的 FlowerSuit 枚举中。

现在,我们可以探索几种不同的方法来执行此选择。

注1:我使用ThreadLocalRandom表示范围内的随机数,该范围内的随机数来自java.util.concurrent.ThreadLocalRandom

注2:对于其中的每一个,制作一个FlowerPicker实例,并使用pickFlower()方法采摘下一朵花。 这样可以避免一遍又一遍地运行昂贵的设置代码。

方法1:袋花

此方法可能是最容易实现的。 它需要创建一个枚举列表,其中每个枚举表示frequency次,然后从此列表中选择一个随机条目。 这类似于把一束花扔进袋子里,摇晃它,然后把手伸进去,抓住你触摸的第一朵花。 下面是实现:

public class FlowerPicker(){
private ArrayList<FlowerSuit> bag;
public FlowerPicker(){
// Get all possible FlowerSuits
FlowerSuit[] options = FlowerSuit.values();
// You can use an array here or an array list with a defined length if you know the total of the frequencies
bag = new ArrayList<FlowerSuit>();
// Add each flower from options frequency times
for (FlowerSuit flower : options)
for (int i=0; i<flower.getFrequency(); i++)
bag.add(flower);
}
public FlowerBag pickFlower(){
// Now, select a random flower from this list
int randomIndex = ThreadLocalRandom.current().nextInt(0, bag.size());
return bag.get(randomIndex);
}
}

这种方法的优点是足够简单,很容易理解。 但是,如果您的频率非常具体(例如,如果您希望彩虹花在 1,000,000,000,000 次中返回 499,999,999 次(,则可能会效率低下。 让我们继续下一个方法。

注1:你可以通过减少代表被选择频率的分数来做得更好,但我会把这个留给你。

注意2:您也可以通过在bag列表中存储标识号而不是FlowerSuit对象来使其稍微好一点。

方法2:可导航地图

这种方法有点困难。 它使用[NavigableMap][1],这是[TreeMap][2]的实现。 这种方法与"花袋"方法非常相似,但效率更高一些。 简而言之,它使用TreeMap为每个FlowerSuit提供一个数字范围,可以选择这些数字来返回该FlowerSuit。 下面是一个完整的示例:

public class FlowerPicker(){
private NavigableMap<Double, FlowerSuit> map;
public FlowerPicker(){
// Get all possible FlowerSuits
FlowerSuit[] options = FlowerSuit.values();
map = new TreeMap<Double, FlowerSuit>();
int runningTotal = 0;
// Add each flower with the proper range
for (FlowerSuit flower : options){
runningTotal += flower.getFrequency();
map.put(runningTotal, flower);
}
}
public FlowerBag pickFlower(){
// Now, select a random number and get the flower with this number in its range
int randomRange = ThreadLocalRandom.current().nextInt(0, bag.size());
return map.higherEntry(randomRange).getValue();
}
}

这是一种可靠的方法,它可以很好地扩展非常特定的频率。 如果你有一束不同类型的花,情况会稍微差一点,但这种方法在大尺度上仍然是一个不错的选择。不过还有另一种选择。

方法 3:枚举分布这种方法非常好,因为您几乎不需要做任何事情。 但是,它使用来自Apache Commons的[EnumeratedDistribution][3]。 枚举分布需要对象和权重对的列表。 无论如何,让我们跳进去:

public class FlowerPicker(){
private EnumeratedDistribution distribution;
public FlowerPicker(){
// Get all possible FlowerSuits
FlowerSuit[] options = FlowerSuit.values();
List<Pair<FlowerSuit, Double>> weights = new List<Pair<FlowerSuit, Double>>();
// Add each flower and weight to the list
for (FlowerSuit flower : options){
weights.add(new Pair(flower, flower.getFrequency()));
// Construct the actual distribution
distribution = new EnumeratedDistribution(weights);
}
public FlowerBag pickFlower(){
// Now, sample the distribution
return distribution.sample();
}
}

这是我最喜欢的方法,仅仅是因为很多都是为你完成的。 像这样的许多问题已经解决,那么为什么不使用始终存在的解决方案呢? 但是,自己编写解决方案有一些价值。

总之,这些方法中的每一种都可以在您的规模上使用,但我推荐第二种或第三种方法。

最新更新