我正在寻找有关如何为我的游戏执行百分比操作的提示,我希望所有花朵在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;
}
}
答案 0 :(得分:1)
这就是我要做的,但是对于初学者来说,可以通过使用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的时间
答案 1 :(得分:0)
您可以使用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。
答案 2 :(得分:0)
无论您采用哪种解决方案,都希望在枚举中包括频率测量。例如,您可以执行以下操作:
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中的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();
}
}
这是我最喜欢的方法,原因很简单,因为它为您完成了很多工作。像这样的许多问题已经解决了,那么为什么不使用始终存在的解决方案呢?但是,自己编写解决方案仍有一些价值。
总而言之,这些方法中的每一种都可以很好地用于您的规模,但是我建议使用第二种或第三种方法。