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

时间:2018-08-21 00:21:53

标签: java enums percentage

我正在寻找有关如何为我的游戏执行百分比操作的提示,我希望所有花朵在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;
    }

}

3 个答案:

答案 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();
    }
}

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

总而言之,这些方法中的每一种都可以很好地用于您的规模,但是我建议使用第二种或第三种方法。