抽奖案例库

概要

一个基于Java编程语言实现的抽奖案例小样,提供抽奖实现思路。

整体架构流程

  1. 抽奖活动管理
  2. 奖品设置
  3. 中奖规则

名词解释

  • 权重
  • 库存
  • 奖池

技术细节

案例1:权重为浮点型,计算是否中奖通过[0,1) * totalWeigth与奖品权重值进行比较。

import java.util.*;
import java.util.concurrent.CountDownLatch;

public class LotterySystem {
    private static List<Prize> prizes = new ArrayList<>();

    public static void main(String[] args) {
        // 管理员添加奖品(无需总和=1)
        addPrize(1, "iPhone", 100.0, 2, "奖品");
        addPrize(2, "AirPods", 100.0, 1, "奖品");
        addPrize(3, "谢谢参与", 10.0, -1, "谢谢参与");
        addPrize(4, "谢谢参与", 9800.0, -1, "谢谢参与");
        addPrize(5, "谢谢参与", 8800.0, -1, "谢谢参与");
        addPrize(6, "谢谢参与", 8090.0, -1, "谢谢参与");
        addPrize(7, "谢谢参与", 8600.0, -1, "谢谢参与");
        addPrize(8, "谢谢参与", 880.0, -1, "谢谢参与");
        // 查看当前权重分布
        prizes.forEach(prize -> System.out.printf(
                "开始: %s: 原始权重=%.1f, 实际概率=%.10f%%%n",
                prize.getName(), prize.getRawWeight(), prize.getNormalizedWeight() * 100
        ));

        // 抽奖
//        for (int i = 0; i < 20000; i++) {
//            Prize winner = drawWinner();
//            if (winner.id == 1 || winner.id == 2) {
//                System.out.println("第"+ i + "次中奖奖品: " + "id:"+ winner.id + ":" +winner.getName());
//            }
//        }

        int count = 1000;
        CountDownLatch countDownLatch = new CountDownLatch(count);
        // 模拟并发抽奖
        for (int i = 0; i < count; i++) {
            new Thread(() -> {
                Prize winner = drawWinner();
                if (winner.id == 1 || winner.id == 2) {
                    System.out.println("第"+ Thread.currentThread().getName() + "次中奖奖品: " + "id:"+ winner.id + ":" +winner.getName());
                }
                countDownLatch.countDown();
            }).start();
        }

        while (countDownLatch.getCount() >= 1) {}
        prizes.forEach(prize -> System.out.printf(
                "结束: %s: 原始权重=%.1f, 实际概率=%.10f%%%n",
                prize.getName(), prize.getRawWeight(), prize.getNormalizedWeight() * 100
        ));

    }

    public static void addPrize(int id, String name, double rawWeight, int stock, String type) {
        prizes.add(new Prize(id, name, rawWeight, stock, type));
    }

    public static Prize drawWinner() {
        double totalWeight = prizes.stream().mapToDouble(Prize::getRawWeight).sum();
        int sum = prizes.stream().filter(prize -> !"谢谢参与".equals(prize.type)).mapToInt(Prize::getstock).sum();
        if (sum <= 0) {
            Optional<Prize> optionalPrize = prizes.stream().filter(prize -> "谢谢参与".equals(prize.type)).findAny();
            if (optionalPrize.isPresent()) {
                return optionalPrize.get();
            }
            throw new RuntimeException("当前活动太火爆,奖品已被抽完啦");
        }
        double random = Math.random() * totalWeight;
        double cumulativeWeight = 0.0;

        for (Prize prize : prizes) {
            cumulativeWeight += prize.getRawWeight();
            if (random < cumulativeWeight) {
                if (prize.stock != -1 && prize.stock <= 0) {
                    prize.rawWeight = 0;
                    return drawWinner();
                }else if (prize.stock > 0){
                    synchronized (Prize.class) {
                        if (prize.stock <= 0) {
                            prize.rawWeight = 0;
                            return drawWinner();
                        }
                        prize.stock -= 1;
                        if (prize.stock <= 0) {
                            prize.rawWeight = 0;
                        }
                    }
                }
                return prize;
            }
        }
        throw new IllegalStateException("抽奖逻辑异常");
    }

    static class Prize {
        private int id;
        private String name;
        private double rawWeight;
        private int stock;
        private String type;

        public Prize(int id, String name, double rawWeight, int stock, String type) {
            this.id = id;
            this.name = name;
            this.rawWeight = rawWeight;
            this.stock = stock;
            this.type = type;
        }
        public String getName() { return name; }
        public double getRawWeight() { return rawWeight; }
        public int getstock() { return stock; }
        public double getNormalizedWeight() {
            double totalWeight = prizes.stream().mapToDouble(Prize::getRawWeight).sum();
            return rawWeight / totalWeight;
        }
    }
}

案例2:权重为整型,计算是否中奖通过totalWeigth基数来获取中奖数字与奖品权重值进行比较。

import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.Random;
import java.util.concurrent.CountDownLatch;

public class LotterySystem2 {
    private static List<Prize> prizes = new ArrayList<>();
    private static Random random = new Random();
    public static void main(String[] args) {
        // 管理员添加奖品(无需总和=1)
        addPrize(1, "iPhone", 100, 2, "奖品");
        addPrize(2, "AirPods", 100, 1, "奖品");
        addPrize(3, "谢谢参与", 10, -1, "谢谢参与");
        addPrize(4, "谢谢参与", 9800, -1, "谢谢参与");
        addPrize(5, "谢谢参与", 8800, -1, "谢谢参与");
        addPrize(6, "谢谢参与", 8090, -1, "谢谢参与");
        addPrize(7, "谢谢参与", 8600, -1, "谢谢参与");
        addPrize(8, "谢谢参与", 880, -1, "谢谢参与");

        // 查看当前权重分布
        prizes.forEach(prize -> System.out.printf(
                "开始: %s: 原始权重=%d, 实际概率=%.10f%%%n",
                prize.getName(), prize.getRawWeight(), prize.getNormalizedWeight() * 100
        ));

        // 模拟抽奖
//        for (int i = 0; i < 20000; i++) {
//            Prize winner = drawWinner();
//            if (winner.id == 1 || winner.id == 2) {
//                System.out.println("第"+ i + "次中奖奖品: " + "id:"+ winner.id + ":" +winner.getName());
//            }
//        }

        int count = 1000;
        CountDownLatch countDownLatch = new CountDownLatch(count);
        // 模拟并发抽奖
        for (int i = 0; i < count; i++) {
            new Thread(() -> {
                Prize winner = drawWinner();
                if (winner.id == 1 || winner.id == 2) {
                    System.out.println("第"+ Thread.currentThread().getName() + "次中奖奖品: " + "id:"+ winner.id + ":" +winner.getName());
                }
                countDownLatch.countDown();
            }).start();
        }

        while (countDownLatch.getCount() >= 1) {}
        // 查看当前权重分布
        prizes.forEach(prize -> System.out.printf(
                "结束: %s: 原始权重=%d, 实际概率=%.10f%%%n",
                prize.getName(), prize.getRawWeight(), prize.getNormalizedWeight() * 100
        ));
    }

    public static void addPrize(int id, String name, int rawWeight, int stock, String type) {
        prizes.add(new Prize(id, name, rawWeight, stock, type));
    }

    /**
     * 抽奖
     *
     * @return 奖品对象
     */
    public static Prize drawWinner() {
        int totalWeight = prizes.stream().mapToInt(Prize::getRawWeight).sum();
        int sum = prizes.stream().filter(prize -> !"谢谢参与".equals(prize.type)).mapToInt(Prize::getstock).sum();
        if (sum <= 0) {
            Optional<Prize> optionalPrize = prizes.stream().filter(prize -> "谢谢参与".equals(prize.type)).findAny();
            if (optionalPrize.isPresent()) {
                return optionalPrize.get();
            }
            throw new RuntimeException("当前活动太火爆,奖品已被抽完啦");
        }
        int number = random.nextInt(totalWeight);
        int cumulativeWeight = 0;

        for (Prize prize : prizes) {
            cumulativeWeight += prize.getRawWeight();
            if (number < cumulativeWeight) {
                if (prize.stock != -1 && prize.stock <= 0) {
                    prize.rawWeight = 0;
                    return drawWinner();
                }else if (prize.stock > 0){
                    synchronized (Prize.class) {
                        if (prize.stock <= 0) {
                            prize.rawWeight = 0;
                            return drawWinner();
                        }
                        prize.stock -= 1;
                        if (prize.stock <= 0) {
                            prize.rawWeight = 0;
                        }
                    }
                }
                return prize;
            }
        }
        throw new RuntimeException("抽奖逻辑异常");
    }

    /**
     * 奖品类
     *
     */
    static class Prize {
        private int id;
        private String name;
        private int rawWeight;
        private int stock;
        private String type;

        public Prize(int id, String name, int rawWeight, int stock, String type) {
            this.id = id;
            this.name = name;
            this.rawWeight = rawWeight;
            this.stock = stock;
            this.type = type;
        }

        public String getName() { return name; }
        public int getRawWeight() { return rawWeight; }
        public int getstock() { return stock; }

        public double getNormalizedWeight() {
            int totalWeight = prizes.stream().mapToInt(Prize::getRawWeight).sum();
            return (double) rawWeight / totalWeight;
        }
    }
}

小结

通过以上两个案例不难发现其实抽奖的业务并不复杂,当然这只是简单的抽奖活动,重要的理解就是奖池的设计、以及如何中奖,中奖后奖品的变化和奖池的变化。总结,以上只是提供实现的思路,具体用于生产还需要靠更多的业务场景和相关业务的影响。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

图个吉利儿

欢迎大家一起学习!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值