java 权重随机数算法

来源:互联网 发布:王家卫句式知乎 编辑:程序博客网 时间:2024/05/19 17:03

大家好,我是入错行的bug猫。(http://blog.csdn.net/qq_41399429,谢绝转载)

如题,前段时间写一个抽奖程序,需要产生随机数A、B、C、D…,并且出现的概率是固定值,比喻特等奖,一等奖,二等奖,三等奖中奖概率分别是1:2:3:4

bug猫:产生随机数啊,那个简单,我知道!

脑袋一拍,有了:
随机生成含0,不含10的整数,按照理论,生成 0,1,2,3,4,5,6,7,8,9 概率完全相等,
生成的随机数为0(大于等于0,小于1),占整体的概率是1/10;
生成的随机数为1,2(大于等于1,小于3),占整体的概率是2/10;
生成的随机数为3,4,5(大于等于3,小于6),占整体的概率是3/10;
生成的随机数为6,7,8,9(大于等于6,小于10),占整体的概率是4/10;
于是四种可能的比,正好是 1:2:3:4

于是bug猫疯狂地敲键盘,代码撸出来了,代码就不贴了,没什么技术含量。

老板:不对啊,怎么少了参与奖?不行,参与奖要加,还要占整个概率的50%。

bug猫:……
好吧,不难。我把随机数范围设置为0-20,0到10保持不变,10-20范围属于参与奖。
加一个if else判定搞定!

老板:嗯——咱们还等加一个优惠奖,抽中这个优惠奖,可以直接有下次抽奖的资格,这个奖,就和特等奖、一等奖、二等奖、三等奖之和一样吧

bug猫:……
经过一系列计算,bug猫终于把各种概率计算出来后,把之前的各个临界值修改了一遍。加班搞完了!

老板:等等!貌似抽奖就有奖获奖,这不对啊!还有大把的不中奖呢?

bug猫:噫,这不是您故意设计成这样的吗?
老板:我要是都发给中奖的人,哪你还要年终奖吗?

bug猫:向年终奖低头!
于是bug猫熬夜搞完了!

像这种不断变更需求,程序猿能不各种苦逼加班吗?
bug猫认为,程序猿需要会“偷懒”,不会“偷懒”的程序猿最终会被各种需求淹没,甚至会被淘汰!
此偷懒非上班摸鱼打浑,而是提高自己效率,更短的时间内,干完自己手上的活,让自己闲下来。

回到正题,如果java中有权重随机数,那么bug猫也不会苦逼兮兮改代码了。
可惜java里没有现成的方法。
网上查资料,要么是千篇一律,不知道是谁转载谁的,要么就是复杂得一腿子,还有的根本没法扩展。
那么,自己撸吧!

解题思路:
按照一开始思路,是没有问题。
某个元素的概率变化了,或者删减了元素,影响到的只是随机数范围,和临界值。
我们看一下随机数范围是怎么来的:等于各个元素的比例之和。
如果概率是按百分比,那么随机数范围,也就是总体是100;
如果是类似A:B:C:D:E=1:2:3:4:6,那么随机数范围应该是(1+2+3+4+6)=16;
再看临界值:
16份数据中,A占1份,B占2份,C占3份,D占4份,E占6份

元素 最小 最大 A 0 = 0 1=0+1 B 1=A最大 3=A最大+2 C 3=B最大 6=B最大+3 D 6=C最大 10=C最大+4 E 10=D最大 16=D最大+6

规则很明显
数学表达式:E0最小=0,E0最大=E0最小+权值0;En最小=En-1最大,En最大=En最小+权值n
说人话就是:当前元素概率范围,最小值是上一个元素的最大值,最大值是当前元素的最小值,加上当前元素的权值;

java 代码:

int[] weight = new int[]{1,2,3,4,6};   //所有元素权重List<Integer> weightTmp = new ArrayList<Integer>(weight.length+1);weightTmp.add(0);//随机数范围从0开始计算Integer sum = 0;//随机数最大值,java随机数是不包含该值for( Integer d : weight ){   sum += d;   weightTmp.add(sum);//当前元素概率范围临界值}Random random = new Random();int rand = random.nextInt(sum);//产生0到sum,含0不含sum的随机数

随机数,和临界值都准备好,剩下的就是判断rand是落在哪个范围。
哈?用if else?
先等等,bug猫已经对if else产生不可抗力的恐惧了。

我们还是先观察一下,weightTmp是一个集合,里面的每个对象,正好是元素的临界值。第一个对象是0,最后一个对象是sum,而且weightTmp里面的对象,全部按从小到大排好,找到可以插入rand的位置,这个区间段对应的权重,就是本次随机到的元素:

int index = 0;for(int i = weightTmp.size()-1; i >0; i--){//从集合尾部向头部遍历。也就是从大到小遍历   if( rand >= weightTmp.get(i)){//当第一次出现随机数,大于临界值时      index = i;      break;   }}return index;

返回的index,就是随机到的weight下表。

整理一下代码:

/** * 权重随机数 * @param weight [15,568,4181,2] * @return 索引值 */public static int random(List<Integer> weight){    List<Integer> weightTmp = new ArrayList<Integer>(weight.size()+1);    weightTmp.add(0);    Integer sum = 0;    for( Integer d : weight ){        sum += d;        weightTmp.add(sum);    }    Random random = new Random();    int rand = random.nextInt(sum);    int index = 0;    for(int i = weightTmp.size()-1; i >0; i--){        if( rand >= weightTmp.get(i)){            index = i;            break;        }    }    return index;}//这只是整数权重的,如果是小数、或者百分比,可以先转化成整数,在执行这个方法

搞定!

临近下班的时候,老板带着零食饮料过来,bug喵呀,这个还要加一个奖,另外几个概率也改改。
bug猫撮着枸杞白开水,掐指一算各自的概率,随手在键盘敲了几下,测试通过。对老板说,已经改完了!
在老板诧异的阳光下,伴着下班闹钟,淡定地溜了。








~THE END~

于是第二天bug喵被老板塞了更多的任务了…… (╯‵□′)╯︵┻━┻

原创粉丝点击