本文主要是介绍瓜分游戏币,红包拆分正态分布,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
背景
需要做一个给每个人参加活动的人瓜分固定奖池的游戏币,有平均瓜分和随机分2种分配方案。
平均瓜分
平均分很简单,就是总money/总person取整数就行,无论整除不整除,不整除有结余也无所谓。
随机瓜分
需求详情
根据一个时间段的参与人数设置一个总游戏币数,所有人进行瓜分,每个人最少获得一个币,同时可以无限次重新随机计算,计算出来的列表进行分页展示,而且支持对单个人获得币进行修改,然后统一生成奖励。
思路
- 可以多次进行重新随机计算,所以不能直接将计算的结果持久化,防止大量操作删表新增操作,所以先保存数据到redis中做缓存
- 在缓存中需要做分页查询,类似查询榜单,list,zset类型可实现分页查询,不需要每次查询都将所有数据拉出来进行分页筛选,zset占用空间大,而且也不需要格外的value做实时排列。所以暂时选用list做存储类型
- 缓存中需要保存用户与随机得到的游戏币的对应,选用person+分隔符+币 的String拼接实现
- 需要针对一个用户获得币进行修改,list类型可根据下标索引进行修改,可支持
- 计算出来后,每个用户对应一个随机游戏币,需要按币的大小进行排序,存入redis,重写list,String排序实现
代码实现
涉及的redis操作(简化)
jedis.lpush(key,value); //存放key,value value是可变类型,LPUSH mylist a b c
jedis.lrange(key, startNumber, endNumber);//查询
jedis.lset(key,index,value); //根据index进行修改
jedis.lindex(key, index); //根据当前index获取list中的值
随机算法工具类
- 模拟微信红包算法(简单实现)
-
每个人最少得到1游戏币,所以先给每个人分配一个游戏币,保证在随机下每个人所得不为0
-
随机每个人所得范围在(0-money / people * 2)
-
同时利用Random产生的随机数固定种子下产生的随机序列数相同来排查记录。
-
当前利用微信抢红包不需要一次性将整个分布计算下来,可以在每个人领红包的时候再进行计算
-
利用excel工具直观查看随机数分布是否满足预想
-
废话不多说,贴代码:
-
@Testpublic void randTestTest(){randTest(1000,100);}public static void randTest(int sum,int count){List<Integer> list = new ArrayList<>();sum = sum-count*1;Random random = new Random();long factor = System.currentTimeMillis();System.out.println("固定种子是:"+factor);random.setSeed(factor);//当random设置相同的种子时,随机序列数相同for(int i = 0;i<count;i++){int rand = rand(sum, count-i, list,random);sum = rand;}int summoneny = 0;int min =list.get(0);int max =-1;for(Integer entry :list){summoneny = summoneny+entry.intValue();min = Math.min(min,entry.intValue());max = Math.max(max,entry.intValue());}System.out.println(list.toString());System.out.println(min);System.out.println(max);}/**** @param money 剩余的钱* @param people 剩余的人数* @param l 红包列表* @return*/public static int rand(int money, int people, List<Integer> l ,Random random) {if (people == 1) {int red = money;l.add(red+1);return 0;}int min = 0;int max = money / people * 2;int red = random.nextInt(max);red = red <= min ? min : red;l.add(red+1);int remain = money-red;return remain;}
- 以上思想是每个人口袋能得到多少游戏币,随机在奖池中取钱。现我们转变一下思想,利用游戏币找口袋,游戏币随机落入口袋。
-
分布图展示,看起来后者分布更加均匀
-
利用钱找人,有可能一个人没有被找过一次,当然可以先给每个人先分一个游戏币,然后剩下的币找人,上代码
-
@Testpublic void rand1Test(){Random random = new Random();long factor = System.currentTimeMillis();System.out.println("固定种子是:"+factor);random.setSeed(factor);//当random设置相同的种子时,随机序列数相同int[] ints = rand1(1000, 100, random);int a = 0;int min =ints[0];int max =-1;for (int integer : ints) {a+=integer;min = Math.min(min,integer);max = Math.max(max,integer);}System.out.println(Arrays.toString(ints));System.out.println(a);System.out.println(min);System.out.println(max);}/**** @param money 总钱* @param count 剩余的人数* @return*/public int[] rand1(int money, int count ,Random random) {int[] nums = new int[count];for(int i= 0;i<money;i++){int person = random.nextInt(count);nums[person] +=1;}return nums;}
重写排序工具类
public static void sortListString(List list){System.out.println("排序前:"+list.toString());Collections.sort(list, new Comparator<String>() {@Overridepublic int compare(String o1, String o2) {return Integer.valueOf(o1.substring(o1.indexOf(":")+1,o1.length())).compareTo(Integer.valueOf((o2.substring(o2.indexOf(":")+1,o2.length()))));}});System.out.println("排序后:"+list.toString());}
总结
- 整个过程也看过网上很多介绍正态分布的算法,还有网上各位大神的分析与实现,以上是一个简单的实现同时也很好的契合要求,故实现如此,作此记录。
- 在redis的操作文档中只发现如下的存储方式,RPUSH mylist a b c,所以选择String作为载体进行存储,当然可以选择list中保存可序列化对象,将对象序列化后再保存到redis中,可以添加更多的属性。由于时间问题并未依此方案实现。
这篇关于瓜分游戏币,红包拆分正态分布的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!