支付宝扫福活动背后的一个算法问题

栏目: 编程工具 · 发布时间: 5年前

内容简介:支付宝扫福背后的可能涉及的一个问题,就是五福已经花花卡怎么得到。这里面涉及到很多的游戏规则,当然问题的本质就是从一个数组中按照一定的比例获取一项元素。随机获取一个指定数组的元素和按照一定比例获取随机项这种功能在一定的在日常开发中肯定很常见。前者相对实现起来比较简单,但是后者网上有千奇百怪的而实现方式,而且其代码俩都相对较大,领人阅读的时候不能很愉快。当然每个人都有每个人的实现方式,凡是自己实现确实是不错的选择,至少是对自己的一种锻炼。今天我就给出我的一种实现方式相对其他方式极大的简化了原理和实现方式。另外其

支付宝扫福背后的可能涉及的一个问题,就是五福已经花花卡怎么得到。这里面涉及到很多的游戏规则,当然问题的本质就是从一个数组中按照一定的比例获取一项元素。

随机获取一个指定数组的元素和按照一定比例获取随机项这种功能在一定的在日常开发中肯定很常见。前者相对实现起来比较简单,但是后者网上有千奇百怪的而实现方式,而且其代码俩都相对较大,领人阅读的时候不能很愉快。当然每个人都有每个人的实现方式,凡是自己实现确实是不错的选择,至少是对自己的一种锻炼。今天我就给出我的一种实现方式相对其他方式极大的简化了原理和实现方式。另外其实按照现有的常见系统这里面处理涉看似简单的游戏规则外,还有很多的AI和大数据成品,影响着随机的结果。具体代码见github

此处有彩蛋

元宵节前留言解密内容有机会获得现金红包

AUAAKCjTM0M7G1L+WSB80R5Iiv6zLcnXz8OESQJGbzktlFj0M5n0p6Q4gOO6r6Pr5AGWIYbMwm//IMqQ/KTLEFl8EQ

实现方式

 1    /**
 2     * 生成制定范围内的随机数
 3     *
 4     * @param scopeMin
 5     * @param scoeMax
 6     * @return
 7     */
 8    public static int integer(int scopeMin, int scoeMax) {
 9        Random random = new Random();
10        return (random.nextInt(scoeMax) % (scoeMax - scopeMin + 1) + scopeMin);
11    }
12    /**
13     * 从指定的数组中随机数组中的某个元素
14     */
15    public static <T> T randomItem(T[] param) {
16        int index = integer(0, param.length);
17        return param[index];
18    }

测试

 1    @Test
 2    public void testRandomItem(){
 3        for (int j = 0; j < 10; j++) {
 4            Map<Integer,Integer> map = new HashMap();
 5            for (int i = 0; i < 1000000; i++) {
 6                Integer integer = RandomUtil.randomItem(new Integer[]{10, 30, 50});
 7                if(map.containsKey(integer)){
 8                    map.put(integer,map.get(integer)+1);
 9                }else{
10                    map.put(integer,1);
11                }
12            }
13            int count = map.get(10)+map.get(30)+map.get(50);
14            String str = "10/30/50 ="+map.get(10)+":"+map.get(30)+":"+map.get(50)
15                    +"("+count+")";
16            System.out.println(str);
17        }
18    }

测试结果

此处我随机了1000000次,下面是一些测试结果,从结果上来看相对还是比较靠谱

 110/30/50 =333651:332493:333856(1000000)
 210/30/50 =333098:333090:333812(1000000)
 310/30/50 =333791:332406:333803(1000000)
 410/30/50 =333052:333188:333760(1000000)
 510/30/50 =333131:333132:333737(1000000)
 610/30/50 =333920:333441:332639(1000000)
 710/30/50 =332876:333978:333146(1000000)
 810/30/50 =333948:332682:333370(1000000)
 910/30/50 =333180:333794:333026(1000000)
1010/30/50 =333030:333798:333172(1000000)

按照比例随机项

相对网络上的其他方式,此方法的实现原理很简单就随机一个数字,通过这个数字反映出其位置,然后通过位置即可保证随机比例,测试的时候发现随机的次数越多比例越接近设定的值。

实现

 1     /**
 2     * 从指定的数组中按照指定比例返回指定的随机元素
 3     *
 4     * @param param 随机数组
 5     * @param percentum 比例
 6     * @param <T>
 7     * @return
 8     */
 9    public static <T> T randomItem(T[] param, double[] percentum) {
10        int length = percentum.length;
11        Integer[] ints = ArrayUtil.doubleBitCount(percentum);
12        int max = Collections.max(Arrays.asList(ints));
13        int[] arr = new int[length];
14        int sum = 0;
15        Map map = new HashMap(length);
16        StringBuffer buffer = new StringBuffer();
17        for (int i = 0; i < max; i++) {
18            buffer.append("0");
19        }
20        int multiple = Integer.parseInt("1" + buffer.toString());
21        for (int i = 0; i < length; i++) {
22            int temp = (int) (percentum[i] * multiple);
23            arr[i] = temp;
24            if (i == 0) {
25                map.put(i, new int[]{1, temp});
26            } else {
27                map.put(i, new int[]{sum, sum + temp});
28            }
29            sum += temp;
30        }
31        int indexSum = integer(1, sum);
32        int index = -1;
33        for (int i = 0; i < length; i++) {
34            int[] scope = (int[]) map.get(i);
35            if (indexSum == 1) {
36                index = 0;
37                break;
38            }
39            if (indexSum > scope[0] && indexSum <= scope[1]) {
40                index = i;
41                break;
42            }
43        }
44        if (index == -1) {
45            throw new RuntimeException("随机失败");
46        } else {
47            return param[index];
48        }
49    }

测试

 1        @Test
 2    public void testRandomItemRatio(){
 3
 4        for (int j = 0; j < 10; j++) {
 5            Map<Integer,Integer> map = new HashMap();
 6            for (int i = 0; i < 1000000; i++) {
 7                double[] percentum = new double[]{0.6,0.3,0.1};
 8                Integer integer = RandomUtil.randomItem(new Integer[]{10, 30, 50}, percentum);
 9                if(map.containsKey(integer)){
10                    map.put(integer,map.get(integer)+1);
11                }else{
12                    map.put(integer,1);
13                }
14            }
15            int count = map.get(10)+map.get(30)+map.get(50);
16            String str = "10/30/50 ="+map.get(10)+":"+map.get(30)+":"+map.get(50)
17                    +"("+count+") ->"+
18                    StringHelper.formatNumber(new BigDecimal(map.get(10)/(float)count),"#.0000")+":"+
19                    StringHelper.formatNumber(new BigDecimal(map.get(30)/(float)count),"#.0000")+":"+
20                    StringHelper.formatNumber(new BigDecimal(map.get(50)/(float)count),"#.0000");
21            System.out.println(str);
22        }
23
24        //
25        System.out.println("测试万分之一的概率");
26        for (int j = 0; j < 10; j++) {
27            Map<Integer,Integer> map2 = new HashMap();
28            for (int i = 0; i < 1000000; i++) {
29                double[] percentum = new double[]{0.6,0.4999,0.0001};
30                Integer integer = RandomUtil.randomItem(new Integer[]{10, 30, 50}, percentum);
31                if(map2.containsKey(integer)){
32                    map2.put(integer,map2.get(integer)+1);
33                }else{
34                    map2.put(integer,1);
35                }
36            }
37            int count = map2.get(10)+map2.get(30)+map2.get(50);
38            String str = "10/30/50 ="+map2.get(10)+":"+map2.get(30)+":"+map2.get(50)
39                    +"("+count+") ->"+
40                    StringHelper.formatNumber(new BigDecimal(map2.get(10)/(float)count),"#.00000")+":"+
41                    StringHelper.formatNumber(new BigDecimal(map2.get(30)/(float)count),"#.00000")+":"+
42                    StringHelper.formatNumber(new BigDecimal(map2.get(50)/(float)count),"#.00000");
43            System.out.println(str);
44        }
45        System.out.println("测试十万分之一的概率");
46        for (int j = 0; j < 10; j++) {
47            Map<Integer,Integer> map3 = new HashMap();
48            for (int i = 0; i < 1000000; i++) {
49                double[] percentum = new double[]{0.6,0.49999,0.00001};
50                Integer integer = RandomUtil.randomItem(new Integer[]{10, 30, 50}, percentum);
51                if(map3.containsKey(integer)){
52                    map3.put(integer,map3.get(integer)+1);
53                }else{
54                    map3.put(integer,1);
55                }
56            }
57            int count = map3.get(10)+map3.get(30)+map3.get(50);
58            String str = "10/30/50 ="+map3.get(10)+":"+map3.get(30)+":"+map3.get(50)
59                    +"("+count+") ->"+
60                    StringHelper.formatNumber(new BigDecimal(map3.get(10)/(float)count),"#.000000")+":"+
61                    StringHelper.formatNumber(new BigDecimal(map3.get(30)/(float)count),"#.000000")+":"+
62                    StringHelper.formatNumber(new BigDecimal(map3.get(50)/(float)count),"#.000000");
63            System.out.println(str);
64        }
65    }

测试结果

 110/30/50 =600354:299397:100249(1000000) ->0.6004:0.2994:0.1002
 210/30/50 =600025:299830:100145(1000000) ->0.6000:0.2998:0.1001
 310/30/50 =600126:299818:100056(1000000) ->0.6001:0.2998:0.1001
 410/30/50 =600687:299354:99959(1000000) ->0.6007:0.2994:0.1000
 510/30/50 =600916:299568:99516(1000000) ->0.6009:0.2996:0.0995
 610/30/50 =599388:300589:100023(1000000) ->0.5994:0.3006:0.1000
 710/30/50 =599777:299881:100342(1000000) ->0.5998:0.2999:0.1003
 810/30/50 =599734:300583:99683(1000000) ->0.5997:0.3006:0.0997
 910/30/50 =599214:300557:100229(1000000) ->0.5992:0.3006:0.1002
1010/30/50 =600500:299443:100057(1000000) ->0.6005:0.2994:0.1001
11测试万分之一的概率
1210/30/50 =545096:454801:103(1000000) ->0.54510:0.45480:0.00010
1310/30/50 =545798:454116:86(1000000) ->0.54580:0.45412:0.00009
1410/30/50 =545413:454495:92(1000000) ->0.54541:0.45450:0.00009
1510/30/50 =545842:454068:90(1000000) ->0.54584:0.45407:0.00009
1610/30/50 =544981:454932:87(1000000) ->0.54498:0.45493:0.00009
1710/30/50 =545930:453973:97(1000000) ->0.54593:0.45397:0.00010
1810/30/50 =545555:454358:87(1000000) ->0.54555:0.45436:0.00009
1910/30/50 =546784:453122:94(1000000) ->0.54678:0.45312:0.00009
2010/30/50 =545536:454370:94(1000000) ->0.54554:0.45437:0.00009
2110/30/50 =545724:454168:108(1000000) ->0.54572:0.45417:0.00011
22测试十万分之一的概率
2310/30/50 =545508:454484:8(1000000) ->0.545508:0.454484:0.000008
2410/30/50 =545711:454281:8(1000000) ->0.545711:0.454281:0.000008
2510/30/50 =545483:454506:11(1000000) ->0.545483:0.454506:0.000011
2610/30/50 =545286:454706:8(1000000) ->0.545286:0.454706:0.000008
2710/30/50 =545381:454612:7(1000000) ->0.545381:0.454612:0.000007
2810/30/50 =545623:454368:9(1000000) ->0.545623:0.454368:0.000009
2910/30/50 =546073:453914:13(1000000) ->0.546073:0.453914:0.000013
3010/30/50 =545580:454403:17(1000000) ->0.545580:0.454403:0.000017
3110/30/50 =545413:454582:5(1000000) ->0.545413:0.454582:0.000005
3210/30/50 =545211:454781:8(1000000) ->0.545211:0.454781:0.000008

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

人类2.0

人类2.0

皮埃罗∙斯加鲁菲(Piero Scaruffi) / 闫景立、牛金霞 / 中信出版集团股份有限公司 / 2017-2-1 / CNY 68.00

《人类2.0:在硅谷探索科技未来》从在众多新技术中选择了他认为最有潜力塑造科技乃至人类未来的新技术进行详述,其中涉及大数据、物联网、人工智能、纳米科技、虚拟现实、生物技术、社交媒体、区块链、太空探索和3D打印。皮埃罗用一名硅谷工程师的严谨和一名历史文化学者的哲学视角,不仅在书中勾勒出这些新技术的未来演变方向和面貌,还对它们对社会和人性的影响进行了深入思考。 为了补充和佐证其观点,《人类2.0......一起来看看 《人类2.0》 这本书的介绍吧!

JS 压缩/解压工具
JS 压缩/解压工具

在线压缩/解压 JS 代码

Base64 编码/解码
Base64 编码/解码

Base64 编码/解码

HEX HSV 转换工具
HEX HSV 转换工具

HEX HSV 互换工具