• Springboot 抽奖活动 算法


    开门见山,采用算法:Alias离散采样算法

    该算法逻辑不详述:可自行查询

    优点:查询的时间复杂度O1

    在项目中使用分为两步:

    1、构建(代码源自链接文章底部)

    public final class AliasMethod {
        /* The random number generator used to sample from the distribution. */
        private final Random random;
        /* The probability and alias tables. */
        private final int[] alias;
        private final double[] probability;
    
        /**
         * Constructs a new AliasMethod to sample from a discrete distribution and
         * hand back outcomes based on the probability distribution.
         * Given as input a list of probabilities corresponding to outcomes 0, 1,
         * ..., n - 1, this constructor creates the probability and alias tables
         * needed to efficiently sample from this distribution.
         *
         * @param probabilities The list of probabilities.
         */
        public AliasMethod(List<Double> probabilities) {
            this(probabilities, new Random());
        }
    
        /**
         * Constructs a new AliasMethod to sample from a discrete distribution and
         * hand back outcomes based on the probability distribution.
         * Given as input a list of probabilities corresponding to outcomes 0, 1,
         * ..., n - 1, along with the random number generator that should be used
         * as the underlying generator, this constructor creates the probability
         * and alias tables needed to efficiently sample from this distribution.
         *
         * @param probabilities The list of probabilities.
         * @param random        The random number generator
         */
        public AliasMethod(List<Double> probabilities, Random random) {
            /* Begin by doing basic structural checks on the inputs. */
            if (probabilities == null || random == null) throw new NullPointerException();
            if (probabilities.size() == 0)
                throw new IllegalArgumentException("Probability vector must be nonempty.");
            /* Allocate space for the probability and alias tables. */
            probability = new double[probabilities.size()];
            alias = new int[probabilities.size()];
            /* Store the underlying generator. */
            this.random = random;
            /* Compute the average probability and cache it for later use. */
            final double average = 1.0 / probabilities.size();
            /*
            Make a copy of the probabilities list, since we will be making
            changes to it.
            */
            probabilities = new ArrayList<>(probabilities);
            /* Create two stacks to act as worklists as we populate the tables. */
            Deque<Integer> small = new ArrayDeque<>();
            Deque<Integer> large = new ArrayDeque<>();
            /* Populate the stacks with the input probabilities. */
            for (int i = 0; i < probabilities.size(); ++i) {
                /*
                If the probability is below the average probability, then we add
                it to the small list; otherwise we add it to the large list.
                */
                if (probabilities.get(i) >= average) large.add(i);
                else small.add(i);
            }
            /*
            As a note: in the mathematical specification of the algorithm, we
            will always exhaust the small list before the big list.  However,
            due to floating point inaccuracies, this is not necessarily true.
            Consequently, this inner loop (which tries to pair small and large
            elements) will have to check that both lists aren't empty.
            */
            while (!small.isEmpty() && !large.isEmpty()) {
                /* Get the index of the small and the large probabilities. */
                int less = small.removeLast();
                int more = large.removeLast();
                /*
                These probabilities have not yet been scaled up to be such that
                1/n is given weight 1.0.  We do this here instead.
                */
                probability[less] = probabilities.get(less) * probabilities.size();
                alias[less] = more;
                /*
                Decrease the probability of the larger one by the appropriate
                amount.
                */
                probabilities.set(more, (probabilities.get(more) + probabilities.get(less)) - average);
                /*
                If the new probability is less than the average, add it into the
                small list; otherwise add it to the large list.
                */
                if (probabilities.get(more) >= 1.0 / probabilities.size()) large.add(more);
                else small.add(more);
            }
            /*
            At this point, everything is in one list, which means that the
            remaining probabilities should all be 1/n.  Based on this, set them
            appropriately.  Due to numerical issues, we can't be sure which
            stack will hold the entries, so we empty both.
            */
            while (!small.isEmpty()) probability[small.removeLast()] = 1.0;
            while (!large.isEmpty()) probability[large.removeLast()] = 1.0;
        }
    
        /**
         * Samples a value from the underlying distribution.
         *
         * @return A random value sampled from the underlying distribution.
         */
        public int next() {
            /* Generate a fair die roll to determine which column to inspect. */
            int column = random.nextInt(probability.length);
            /* Generate a biased coin toss to determine which option to pick. */
            boolean coinToss = random.nextDouble() < probability[column];
            /*
            Based on the outcome, return either the column or its alias.
            Log.i("1234","column="+column);
            Log.i("1234","coinToss="+coinToss);
            Log.i("1234","alias[column]="+coinToss);
            */
            return coinToss ? column : alias[column];
        }
    
    }

    项目启动时构建:

    @Configuration
    public class LotteryConfig {
    
        /**
         * 构建奖盘奖品
         * 注意:
         * 所有生效的物品概率和必须为:1
         */
        public static final ActivityLotteryGoodEnum[] LOTTERY_GOODS = new ActivityLotteryGoodEnum[]{
                ActivityLotteryGoodEnum.THANKS,
                ActivityLotteryGoodEnum.HONGBAO
                ...
        };
    
        /**
         * 构建抽奖算法 - 枚举中定义了抽奖概率,就不详述了,例如:0.01(百分之一)
         *
         * @return AliasMethod
         */
        @Bean
        public AliasMethod aliasMethod() {
            List<Double> alias = new LinkedList<>();
            for (ActivityLotteryGoodEnum good : LOTTERY_GOODS) {
                alias.add(good.getRate());
            }
            return new AliasMethod(alias);
        }
    
    }

    2、项目中使用

    引入:

    @Autowired
    private AliasMethod alias;

    使用:(就是抽了一个物品的枚举)

    private ActivityLotteryGoodEnum lottery() {
        return LotteryConfig.LOTTERY_GOODS[alias.next()];
    }
  • 相关阅读:
    第一次作业
    第07组 Alpha事后诸葛亮
    2019SDN第4次作业
    第07组 Alpha冲刺(4/4)
    第07组 Alpha冲刺(3/4)
    第07组 Alpha冲刺(2/4)
    2019 SDN上机第3次作业
    2019 SDN阅读作业
    第07组 Alpha冲刺(1/4)
    2019 SDN上机第2次作业
  • 原文地址:https://www.cnblogs.com/SamNicole1809/p/16080280.html
Copyright © 2020-2023  润新知