• 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()];
    }
  • 相关阅读:
    Android-adb相关
    我想和iOS大牛们交流的问题
    iOS应用 bug定位
    iOS 中Block的基础用法
    iOS9 耗电量惊人
    三人小团队git分支协作试水
    今天开通了博客
    解决XCode安装插件后插件不能使用的问题(转载)
    (转载)iOS- 指压即达,如何集成iOS9里的3D Touch
    人魔七七
  • 原文地址:https://www.cnblogs.com/SamNicole1809/p/16080280.html
Copyright © 2020-2023  润新知