zoukankan      html  css  js  c++  java
  • 根据权重随机选取指定条数记录的简单算法实现(C#)【含源代码】

    一.应用场景:

        有时我们需要从一些列数据中根据权重随机选取指定条数记录出来,这里需要权重、随机,我们根据权重越大的,出现概率越大。例如广告系统:可根据客户支付金额大小来调控客户们的广告出现概率,客户支付金额越大,其广告出现频率越频繁,例如:加入有10条广告,然后每条广告都有一个权重,我们每次要根据权重选取5条广告出来进行显示。有了需求,我们就进行解决,本文章就是利用一种简单的算法来实现根据权重来随机选取。

    二.简单算法的实现:

       根据我们需求,上网找了不少资料,都没有找到一种比较适合的方案,就自己想了一个简单的实现方案,试了一下,效果还挺不错的,现在和大家分享分享,有不合理或者错误之处,还望各位高手纠正。废话少说,其实算法很简单,如下:

    1. 每个广告都有一个权重,用W表示。我们假设最小的权重为1,数字越大的权重越高。
    2. 计算出所有广告的权重总和,用SUM表示。
    3. 遍历每个广告,将它的权重W与从0到(SUM-1)的一个随机数(即权重总和SUM以内的随机数)相加,得到新的权重排序值,用S表示。
    4. 根据广告权重排序值S从大到小进行排序。
    5. 根据排序结果选取最前面的几条记录就是我们所需要的结果。

    其简单原理如下:

    1.假如我们有A、B、C、D四个广告,其权重分别为1、2、3、4,则其总权重SUM=1+2+3+4=10。

    2.根据其权重值与一个小于SUM的随机值(由于SUM=10,则随机值范围是0~9)相加,得到一个权重排序值,如下:

    广告 权重值 最小权重排序值 最大权重排序值
    A 1 1+0=1 1+9=10
    B 2 2+0=2 2+9=11
    C 3 3+0=3 3+9=12
    D 4 4+0=4 4+9=13


    3.由此可以得出A、B、C、D的范围,由上面可以看出A是1~10之间的数,而D的范围是4~13,由此可以看出,D得出随机数大的概率比较大。所以我们只要根据其出现概率再排一下顺序,再选取排在权重排序值比较大的前几项即可,这就可以得到我们所需要的根据权重选取概率的广告。

    三.C#代码算法的实现:

    1.首先我们先声明一个权重基础类RandomObject,以后只要继承该类即可。其代码如下:

    /// <summary>
    /// 权重对象
    /// </summary>
    public class RandomObject
    {
    /// <summary>
    /// 权重
    /// </summary>
    public int Weight { set; get; }
    }

    这个类我们只定义一个Weight属性,其它属性可由继承它的类根据具体业务来实现具体属性。

    2.创建一个辅助类RandomHelper,新增一个实现根据权重随机选取条数的的函数,其代码如下:

        /// <summary>
    /// 算法:
    /// 1.每个广告项权重+1命名为w,防止为0情况。
    /// 2.计算出总权重n。
    /// 3.每个广告项权重w加上从0到(n-1)的一个随机数(即总权重以内的随机数),得到新的权重排序值s。
    /// 4.根据得到新的权重排序值s进行排序,取前面s最大几个。
    /// </summary>
    /// <param name="list">原始列表</param>
    /// <param name="count">随机抽取条数</param>
    /// <returns></returns>
    public static List<T> GetRandomList<T>(List<T> list, int count) where T: RandomObject
    {
    if (list == null || list.Count <= count || count <= 0)
    {
    return list;
    }

    //计算权重总和
    int totalWeights = 0;
    for (int i = 0; i < list.Count; i++)
    {
    totalWeights += list[i].Weight + 1; //权重+1,防止为0情况。
    }

    //随机赋值权重
    Random ran = new Random(GetRandomSeed()); //GetRandomSeed()随机种子,防止快速频繁调用导致随机一样的问题
    List<KeyValuePair<int, int>> wlist = new List<KeyValuePair<int, int>>(); //第一个int为list下标索引、第一个int为权重排序值
    for (int i = 0; i < list.Count; i++)
    {
    int w = (list[i].Weight + 1) + ran.Next(0, totalWeights); // (权重+1) + 从0到(总权重-1)的随机数
    wlist.Add(new KeyValuePair<int, int>(i, w));
    }

    //排序
    wlist.Sort(
    delegate(KeyValuePair<int, int> kvp1, KeyValuePair<int, int> kvp2)
    {
    return kvp2.Value - kvp1.Value;
    });

    //根据实际情况取排在最前面的几个
    List<T> newList = new List<T>();
    for (int i = 0; i < count; i++)
    {
    T entiy = list[wlist[i].Key];
    newList.Add(entiy);
    }

    //随机法则
    return newList;
    }


    为了适合各个场景,我们定义T是一个泛型类,该类务必继承RandomObject类,实现自己类信息。该方法有相关的注释,这里就不再详细重复描述。

    特别注意一下GetRandomSeed()这个函数,这个是为了防止在短时间内频繁创建和调用Random时候,会出现重复的随机数(也就是随机不在是随机的问题),其函数代码如下:

        /// <summary>
    /// 随机种子值
    /// </summary>
    /// <returns></returns>
    private static int GetRandomSeed()
    {
    byte[] bytes = new byte[4];
    System.Security.Cryptography.RNGCryptoServiceProvider rng = new System.Security.Cryptography.RNGCryptoServiceProvider();
    rng.GetBytes(bytes);
    return BitConverter.ToInt32(bytes, 0);
    }

    3.调用方法:

    调用方法很简单,只需要声明一个自己的实体,集成Object,然后根据实际情况直接调用RandomHelper.GetRandomList()方法即可。

    如下:

    //1.声明一个继承RandomObject的实体类,如:*
    public class AdvertEntity : RandomObject
    {
    /// <summary>
    /// 名称
    /// </summary>
    public string Name { set; get; }
    /// <summary>
    /// 描述
    /// </summary>
    public string Description { set; get; }
    //...其它相关的字段/属性
    }

    //2.初始化调用数据,如:
    //初始化模拟权重数据
    List<Advert> list = new List<Advert>();
    list.Add(new Advert { Name = "A", Weight = 1 });
    list.Add(new Advert { Name = "B", Weight = 2 });
    list.Add(new Advert { Name = "C", Weight = 2 });
    list.Add(new Advert { Name = "D", Weight = 3 });
    list.Add(new Advert { Name = "E", Weight = 4 });
    list.Add(new Advert { Name = "F", Weight = 5 });
    list.Add(new Advert { Name = "G", Weight = 60 });
    //根据权重随机取指定记录条数
    List<Advert> newList = RandomHelper.GetRandomList<Advert>(list, 2); //这个就是用法,newList就是随机取出的记录(第二个参数就是随机取多少条)

     

    四.测试运行结果如下:


     

    五.附源代码下载:

     点击下载源代码

    作者:刘付灵(Foolin)
    出处:http://www.cnblogs.com/foolin/
    关于作者:专注于.Net、Windows Phone 7和移动互联网开发。
    本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,如有问题,可以通过Foolin(AT)126.com  联系我,非常感谢。

    广而告知:http://www.liufuling.cn

  • 相关阅读:
    [CF1439B] Graph Subset Problem
    [CF1439C] Greedy Shopping
    [CF1119F] Niyaz and Small Degrees
    [ARC101C] Ribbons On the Tree
    [CF1446C] Xor Tree
    11月24日 模拟赛 题解
    UOJ346
    [CF1229C] Konrad and Company Evaluation
    [CF1326F] Wise Men (Hard Version)
    学军联赛模拟 第二十七测 题解
  • 原文地址:https://www.cnblogs.com/foolin/p/2412632.html
Copyright © 2011-2022 走看看