根据权重随机选取指定条数记录的简单算法实现(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就是随机取出的记录(第二个参数就是随机取多少条)

四.测试运行结果如下:

时间: 2024-10-12 17:34:26

根据权重随机选取指定条数记录的简单算法实现(C#)的相关文章

SQL存储过程中,传参获取指定条数的记录

假设传入参数 为 @TopCount 直接写 Select top @topcount * from table 是不行的. 可以考虑使用SET ROWCOUNT 示例如下 ALTER PROCEDURE [dbo].[P_CSM_SelectHandleResult] @TopCount int=50 AS BEGIN SET NOCOUNT ON; set rowcount @TopCount SELECT * from HandleResult order by HandleIndex d

MYSQL:随机抽取一条数据库记录

今天我们要实现从随机抽取一条数据库记录的功能,并且抽取出来的数据记录不能重复: 1.首先我们看文章表中的数据: 2.实现功能代码如下: 1 /** 2 * 获取随机的N篇文篇 3 * @param int $len 文章篇数 4 */ 5 public static function getRandom($len = 6) { 6 # 查询数据库,得到最小Id 7 # SELECT min(id) FROM mimi_aritcle 8 $min = Db::name(self::$tb)->f

带权随机数问题--根据权重随机选择一条路径

最近工作中遇到了一个根据权重随机选择一条路径的问题,一时没有啥好方案,参考借鉴了网上的经验,得出了如下解决方案: 思路:1.求权重的和,对(0,权重之歌和]区间进行划分,每个权重占用长度为权重的区间: 2.产生一个在(0,权重之和]区间的等概率随机数: 3.该随机数落在哪个区间,则该区间对应的权重的映射为本次产生的带权随机数. 1 import java.util.ArrayList; 2 import java.util.HashMap; 3 import java.util.List; 4

sharepoint列表如何进行随机取几条记录?

sharepoint列表如何进行随机取记录?由于itemid是不连续.可能存在删除添加等操作导致 我们可以采用随机取第几条记录.把记录集合取出来.产生随机第几条数.这里关键是如何产生不重复的随机数 方法如下: #region//获取随机数 /// <summary> /// 获取随机数 /// </summary> /// <param name="Number">随机个数</param> /// <param name="

根据权重挑选通道的简单算法

当存在一批通道,根据权重,选择哪个通道去使用的简单算法. 利用随机数,数据区间,来获取通道. 通道权重越大,单位时间内使用该通道的概率会大一些. 代码 1 //利用了一个权重区间的比例问题,抓取随机数的可能性,来体现权重思想 2 public static void main(String[] args) { 3 //定义三个通道的权重,按随机数选拔使用哪个通道. 4 //A 10 B 70 C 30 5 //从数据库查询出list集合 6 ChannelD A=new ChannelD("A&

mongodb随机查询一条记录的正确方法!

关于从mongodb库中随机取出一条记录的方法的博文很多,其中都提到了下面三种方法: 1.skip过随机数量的记录. DBCursor cursor = coll.find(query); int rint = random.nextInt(cursor.count()); cursor.skip(rint); DBObject word = null; if(cursor.hasNext()){ word = cursor.next(); cursor.close(); } 很多人说不推荐这种

从SQLSERVER/MYSQL数据库中随机取一条或者N条记录

原文:从SQLSERVER/MYSQL数据库中随机取一条或者N条记录 从SQLSERVER/MYSQL数据库中随机取一条或者N条记录 很多人都知道使用rand()函数但是怎麽使用可能不是每个人都知道 建立测试表 USE [sss] GO CREATE TABLE RANDTEST(ID INT DEFAULT RAND()*100,NAME NVARCHAR(200) DEFAULT 'nihao') GO CREATE INDEX IX_RANDTEST_ID ON RANDTEST(ID)

从mysql数据表中随机取出一条记录

核心查找数据表代码: select * from 表名 order by rand( ) limit 1; //此处的1就是取出数据的条数 但这样取数据网上有人说效率非常差的,那么要如何改进呢 搜索Google,网上基本上都是查询max(id) * rand()来随机获取数据. SELECT * FROM `table` AS t1 JOIN (SELECT ROUND(RAND() * (SELECT MAX(id) FROM `table`)) AS id) AS t2 WHERE t1.i

mysql_oracle_随机查询几条记录

数据库的随机查询SQL 1. Oracle,随机查询20条 select * from ( select  *  from 表名 order by dbms_random.value ) where rownum <= 20; 2.MS SQL Server,随机查询20条 select top 20  * from  表名order by newid() 3.My SQL:,随机查询20条 select  *  from  表名 order by rand() limit 20 随机查询指定人