算法抽奖算法

Posted lyloou

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了算法抽奖算法相关的知识,希望对你有一定的参考价值。

抽奖算法理论

在一组奖品中,每个奖品有自己的概率,总概率为 1.0,也就是说在库存充足的情况下,必然能抽中其中的一个。

通过「谢谢参与」来作为无奖的奖品(也是一种奖品)。

需要注意的是:如果一组中所有的奖品,总概率之和不为 1.0,那么数值代表的概率就不是真实概率了,需要用所占比例来作为新的概率:新概率值=奖品概率/总概率

举个例子:只有 A 和 B 两个奖品,A 概率是 0.1,B 概率是 0.3,那么总概率就是 0.4,A 的真实概率就是0.1/0.4=0.25,B 的真实概率是0.3/0.4=0.75,真实的总概率依然为1

所以如果想要配置的奖品概率正好是抽奖时的概率值,那么就需要为这一组奖品列表的总概率配置成1.0

实现

首先定义一个有概率的奖品基类,所有继承这个基类的子类,都可以用调用 LotteryTool.draw 算法(draw 中的参数类型使用了java泛型)

import lombok.Getter;
import lombok.Setter;

/**
 * 奖品基类
 *
 */
@Setter
@Getter
public class BaseAward 
    /**
     * 抽中这个奖品的概率
     */
    private Double probability;


接着是具体的奖品类

import lombok.Getter;
import lombok.Setter;
import lombok.ToString;

/**
 * 奖品实现类
 */
@Setter
@Getter
@ToString
public class Award extends BaseAward 
    private Integer id;
    private String name;
    private Double price;
    private Integer stock;

    public Award(Integer id, String name, Double price, Double probability, Integer stock) 
        super();
        this.id = id;
        this.name = name;
        this.price = price;
        this.stock = stock;
        setProbability(probability);
    

看看抽奖的工具类

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.RandomUtil;

import java.util.List;

/**
 * 抽奖工具类
 */
public class LotteryTool 

    /**
     * 抽奖方法
     *
     * @param awardList 奖品列表,这些是备选的奖品(一定有库存的)
     * @param <T>       具体奖品类型
     * @return 返回一个抽中的奖品
     */
    public static <T extends BaseAward> T draw(List<T> awardList) 
        if (CollUtil.isEmpty(awardList)) 
            return null;
        

        // 获取总概率,当奖品总概率正好为1时,奖品的 probability 就是真实的概率,否则会按新的比例作为概率
        double sumProbability = awardList.stream()
                .map(BaseAward::getProbability)
                .reduce(0.0, Double::sum);

        // 一共会尝试 awardList.size() 次,确保能返回一个奖品
        for (T t : awardList) 

            // 使用随机值,左闭右开(包含0,不包含1)
            if (t.getProbability() > RandomUtil.randomDouble(0.0, 1.0) * sumProbability) 
                return t;
            
            sumProbability = sumProbability - t.getProbability();
        

        // 其它情况,会到这里(理论上,一定到不了这里的。)
        return null;
    


最后来个测试


import cn.hutool.core.util.StrUtil;

import java.util.ArrayList;
import java.util.List;
/**
* 测试
*/
public class Main 
    public static void main(String[] args) 
        // 奖品列表,库存一共36
        final List<Award> awardList = new ArrayList<>(4);
        awardList.add(new Award(1, "苹果手机", 7000.0, 0.05, 1));
        awardList.add(new Award(2, "5元金币", 5.0, 0.1, 5));
        awardList.add(new Award(3, "15元天堂雨伞", 15.0, 0.25, 10));
        awardList.add(new Award(4, "谢谢参与", 0.0, 0.6, 20));

        System.out.println("开始抽奖:");
        // 抽奖50次
        for (int i = 0; i < 50; i++) 
            String msg;
            final Award draw = LotteryTool.draw(awardList);
            if (draw == null) 
                msg = "奖品抽完了,下次早点来吧~";
             else 
                msg = StrUtil.format("抽到了价值「」的奖品「」", draw.getPrice(), draw.getName());

                // 抽到奖品了,需要减库存,库存不足了,要从列表中剔除
                draw.setStock(draw.getStock() - 1);
                if (draw.getStock() <= 0) 
                    awardList.remove(draw);
                
            

            System.out.println(StrUtil.format("第次抽奖,结果为:", i+1, msg));
        
        System.out.println("抽奖结束.");
    

/*output~
开始抽奖:
第1次抽奖,结果为:抽到了价值「0.0」的奖品「谢谢参与」
第2次抽奖,结果为:抽到了价值「5.0」的奖品「5元金币」
第3次抽奖,结果为:抽到了价值「0.0」的奖品「谢谢参与」
第4次抽奖,结果为:抽到了价值「0.0」的奖品「谢谢参与」
第5次抽奖,结果为:抽到了价值「0.0」的奖品「谢谢参与」
第6次抽奖,结果为:抽到了价值「0.0」的奖品「谢谢参与」
第7次抽奖,结果为:抽到了价值「15.0」的奖品「15元天堂雨伞」
第8次抽奖,结果为:抽到了价值「0.0」的奖品「谢谢参与」
第9次抽奖,结果为:抽到了价值「15.0」的奖品「15元天堂雨伞」
第10次抽奖,结果为:抽到了价值「5.0」的奖品「5元金币」
第11次抽奖,结果为:抽到了价值「0.0」的奖品「谢谢参与」
第12次抽奖,结果为:抽到了价值「0.0」的奖品「谢谢参与」
第13次抽奖,结果为:抽到了价值「0.0」的奖品「谢谢参与」
第14次抽奖,结果为:抽到了价值「15.0」的奖品「15元天堂雨伞」
第15次抽奖,结果为:抽到了价值「0.0」的奖品「谢谢参与」
第16次抽奖,结果为:抽到了价值「0.0」的奖品「谢谢参与」
第17次抽奖,结果为:抽到了价值「0.0」的奖品「谢谢参与」
第18次抽奖,结果为:抽到了价值「0.0」的奖品「谢谢参与」
第19次抽奖,结果为:抽到了价值「0.0」的奖品「谢谢参与」
第20次抽奖,结果为:抽到了价值「0.0」的奖品「谢谢参与」
第21次抽奖,结果为:抽到了价值「0.0」的奖品「谢谢参与」
第22次抽奖,结果为:抽到了价值「0.0」的奖品「谢谢参与」
第23次抽奖,结果为:抽到了价值「0.0」的奖品「谢谢参与」
第24次抽奖,结果为:抽到了价值「15.0」的奖品「15元天堂雨伞」
第25次抽奖,结果为:抽到了价值「15.0」的奖品「15元天堂雨伞」
第26次抽奖,结果为:抽到了价值「0.0」的奖品「谢谢参与」
第27次抽奖,结果为:抽到了价值「0.0」的奖品「谢谢参与」
第28次抽奖,结果为:抽到了价值「15.0」的奖品「15元天堂雨伞」
第29次抽奖,结果为:抽到了价值「15.0」的奖品「15元天堂雨伞」
第30次抽奖,结果为:抽到了价值「5.0」的奖品「5元金币」
第31次抽奖,结果为:抽到了价值「15.0」的奖品「15元天堂雨伞」
第32次抽奖,结果为:抽到了价值「15.0」的奖品「15元天堂雨伞」
第33次抽奖,结果为:抽到了价值「5.0」的奖品「5元金币」
第34次抽奖,结果为:抽到了价值「15.0」的奖品「15元天堂雨伞」
第35次抽奖,结果为:抽到了价值「5.0」的奖品「5元金币」
第36次抽奖,结果为:抽到了价值「7000.0」的奖品「苹果手机」
第37次抽奖,结果为:奖品抽完了,下次早点来吧~
第38次抽奖,结果为:奖品抽完了,下次早点来吧~
第39次抽奖,结果为:奖品抽完了,下次早点来吧~
第40次抽奖,结果为:奖品抽完了,下次早点来吧~
第41次抽奖,结果为:奖品抽完了,下次早点来吧~
第42次抽奖,结果为:奖品抽完了,下次早点来吧~
第43次抽奖,结果为:奖品抽完了,下次早点来吧~
第44次抽奖,结果为:奖品抽完了,下次早点来吧~
第45次抽奖,结果为:奖品抽完了,下次早点来吧~
第46次抽奖,结果为:奖品抽完了,下次早点来吧~
第47次抽奖,结果为:奖品抽完了,下次早点来吧~
第48次抽奖,结果为:奖品抽完了,下次早点来吧~
第49次抽奖,结果为:奖品抽完了,下次早点来吧~
第50次抽奖,结果为:奖品抽完了,下次早点来吧~
抽奖结束.

Process finished with exit code 0
 */

参考资料

阅读原文

以上是关于算法抽奖算法的主要内容,如果未能解决你的问题,请参考以下文章

第28题手写抽奖算法

PHP实现大转盘抽奖算法实例

“50000 EOS 砸向你”抽奖活动的哈希算法解读

关于随机红包抽奖算法

福大软工1816 · 团队现场编程实战(抽奖系统)

福大软工1816 · 团队现场编程实战(抽奖系统)