zoukankan      html  css  js  c++  java
  • 加权随机算法

    加权随机算法一般应用在以下场景:有一个集合S,里面比如有A,B,C,D这四项。这时我们想随机从中抽取一项,但是抽取的概率不同,比如我们希望抽到A的概率是50%,抽到B和C的概率是20%,D的概率是10%。一般来说,我们可以给各项附一个权重,抽取的概率正比于这个权重。那么上述集合就成了:

    {A:5,B:2,C:2,D:1}

    方法一:

    扩展这个集合,使每一项出现的次数与其权重正相关。在上述例子这个集合扩展成:
    {A,A,A,A,A,B,B,C,C,D}
    然后就可以用均匀随机算法来从中选取。

    好处:选取的时间复杂度为O(1),算法简单。
    坏处:空间占用极大。另外如果权重数字位数较大,例如{A:49.1 B:50.9}的时候,就会产生巨大的空间浪费。

    方法二:

    计算权重总和sum,然后在1到sum之间随机选择一个数R,之后遍历整个集合,统计遍历的项的权重之和,如果大于等于R,就停止遍历,选择遇到的项。

    还是以上面的集合为例,sum等于10,如果随机到1-5,则会在遍历第一个数字的时候就退出遍历。符合所选取的概率。

    好处:没有额外的空间占用,算法也比较简单。
    坏处:选取的时候要遍历集合,时间复杂度是O(n)。

    方法三:

    可以对方法二进行优化,对项目集按照权重排序。这样遍历的时候,概率高的项可以很快遇到,减少遍历的项。
    比较{A:5,B:2,C:2,D:1}和{B:2,C:2,A:5,D:1}
    前者遍历步数的期望是5/10*1+2/10*2+2/10*3+1/10*4而后者是2/10*1+2/10*2+5/10*3+1/10*4。

    好处:提高了平均选取速度。
    坏处:需要进行排序,并且不易添加删除修改项。

    问题:

    例如我们要选从不同省份选取一个号码,每个省份的权重不一样,直接选随机数肯定是不行的了,就需要一个模型来解决这个问题。

    简化成下面的问题:

     字典的key代表是省份,value代表的是权重,我们现在需要一个函数,每次基于权重选择一个省份出来

    {"A":2, "B":2, "C":4, "D":10, "E": 20}


    解决:

    这是能想到和能看到的最多的版本,不知道还没有更高效好用的算法。

    1. #!/usr/bin/env python  
    2. # -*- coding: utf-8 -*-  
    3. #python2.7x  
    4. #random_weight.py   
    5. #author: orangleliu@gmail.com 2014-10-11  
    6.   
    7. ''''' 
    8. 每个元素都有权重,然后根据权重随机取值 
    9.  
    10. 输入 {"A":2, "B":2, "C":4, "D":10, "E": 20} 
    11. 输出一个值 
    12. '''  
    13. import random  
    14. import collections as coll  
    15.   
    16. data = {"A":2, "B":2, "C":4, "D":6, "E": 11}  
    17.   
    18. #第一种 根据元素权重值 "A"*2 ..等,把每个元素取权重个元素放到一个数组中,然后最数组下标取随机数得到权重  
    19. def list_method():  
    20.     all_data = []  
    21.     for v, w in data.items():  
    22.         temp = []  
    23.         for i in range(w):  
    24.             temp.append(v)  
    25.         all_data.extend(temp)  
    26.           
    27.     n = random.randint(0,len(all_data)-1)  
    28.     return all_data[n]  
    29.       
    30. #第二种 也是要计算出权重总和,取出一个随机数,遍历所有元素,把权重相加sum,当sum大于等于随机数字的时候停止,取出当前的元组  
    31. def iter_method():  
    32.     total = sum(data.values())  
    33.     rad = random.randint(1,total)  
    34.       
    35.     cur_total = 0  
    36.     res = ""  
    37.     for k, v in data.items():  
    38.         cur_total += v  
    39.         if rad<= cur_total:  
    40.             res = k   
    41.             break  
    42.     return res  
    43.       
    44.       
    45. def test(method):  
    46.     dict_num = coll.defaultdict(int)  
    47.     for i in range(100):  
    48.         dict_num[eval(method)] += 1  
    49.     for i,j in dict_num.items():  
    50.         print i, j      
    51.       
    52. if __name__ == "__main__":  
    53.     test("list_method()")  
    54.     print "-"*50  
    55.     test("iter_method()")  
    56.       

    一次执行的结果

    [javascript] view plain copy print?在CODE上查看代码片派生到我的代码片
    1. A 4  
    2. C 14  
    3. B 7  
    4. E 44  
    5. D 31  
    6. --------------------------------------------------  
    7. A 8  
    8. C 16  
    9. B 6  
    10. E 43  
    11. D 27  
  • 相关阅读:
    HDU 4611 Balls Rearrangement 数学
    Educational Codeforces Round 11 D. Number of Parallelograms 暴力
    Knockout.Js官网学习(简介)
    Entity Framework 关系约束配置
    Entity Framework Fluent API
    Entity Framework DataAnnotations
    Entity Framework 系统约定配置
    Entity Framework 自动生成CodeFirst代码
    Entity Framework CodeFirst数据迁移
    Entity Framework CodeFirst尝试
  • 原文地址:https://www.cnblogs.com/UnGeek/p/5917995.html
Copyright © 2011-2022 走看看