控制随机抽中几率 [ C# | Random ]

简介:

一、文字解说:

          为待随机抽取的集合每个项加一个权值,这个权值就是随机几率,比如正常被抽正的几率为1,那么将希望被抽中几率更大的项的权值设置为3或5,然后随机抽取集合中的项,并将随机数乘以每个项对应的权值,然后排序!!提取前N个项即可!大家可以发现权值更高被乘之后有更高几率排在前面而被抽中,如果将权值设为0将永远也不会被抽中!

二、应用场景:

          1.     随机抽题:如果题A去年考过了,那么我希望今年出现的几率更小或者不出现,那么我将题A的权值设置为0,这道题将在以后的考试随机抽题中永远不会被随机抽中;而另外题B是本院今年模拟考试中的一道题目,我将这道题权值增加到5,根据算法,那么这道题目在下次随机抽题抽中率将比普通题目提高数倍!

          2.     赌博机:大家知道游戏厅里面的赌博机是可以调的,被人调了之后出彩率明显提高或者降低,我觉得本算法适合解释。假设赌博机有24个赌项可供选择,分别是A-Z各个字母,按正常几率的话每个项的权值都是1,调机师可以通过动态改变权值来达到提高或降低中奖率。假如你投三个币,分别选了A、B、C,赌博机根据调机师的设置动态改变了A、B、C的权值,让灯转3-4圈后更大的几率停留在这三个选择中奖金较少的一个。

          3.     俄罗斯方块:大家在打QQ俄罗斯方块对打的时候,有时候明显感觉堆得越高,出的东西反而不顺意,我觉得本算法也可以达到这个效果。计算机能算得出下一个最优方案是出条还是出角最好,所以可以通过调整权值来打破平均出现的几率来达到这个目的!

          ......

三、代码实现(C#实现):

      RandomController.cs

// ========================================================================
//                                                                                                                    
//  作 者:小唐
//  邮 箱:over140@gmail.com
//  博 客: http://over140.cnblogs.com/
//  时 间:2009-2-10
//  描 述:控制随机抽中几率。
//                                                                                                                      
// ========================================================================

using  System;
using  System.Collections.Generic;

public   class  RandomController
{

    
#region  Member Variables

    
// 待随机抽取数据集合
     public  List < char >  datas  =   new  List < char > (
        
new   char []{
            
' A ' , ' B ' , ' C ' , ' D ' , ' E ' , ' F ' ,
            
' G ' , ' H ' , ' I ' , ' J ' , ' K ' , ' L ' ,
            
' M ' , ' N ' , ' O ' , ' P ' , ' Q ' , ' R ' ,
            
' S ' , ' T ' , ' U ' , ' V ' , ' W ' , ' X ' ,
            
' Y ' , ' Z '
    });

    
// 权值
     public  List < ushort >  weights  =   new  List < ushort > (
        
new   ushort []{
            
1 , 2 , 3 , 4 , 5 , 6 ,
            
7 , 8 , 9 , 0 , 1 , 1 ,
            
1 , 1 , 1 , 1 , 1 , 1 ,
            
1 , 1 , 1 , 1 , 1 , 1 ,
            
1 , 1
    });

    
#endregion

    
#region  Contructors

    
///   <summary>
    
///  构造函数
    
///   </summary>
    
///   <param name="count"> 随机抽取个数 </param>
     public  RandomController( ushort  count)
    {
        
if  (count  >   26 )
            
throw   new  Exception( " 抽取个数不能超过数据集合大小!! " );

        _Count 
=  count;
    }

    
#endregion

    
#region  Method

    
#region  普通随机抽取

    
///   <summary>
    
///  随机抽取
    
///   </summary>
    
///   <param name="rand"> 随机数生成器 </param>
    
///   <returns></returns>
     public   char [] RandomExtract(Random rand)
    {
        List
< char >  result  =   new  List < char > ();
        
if  (rand  !=   null )
        {
            
for  ( int  i  =  Count; i  >   0 ; )
            {
                
char  item  =  datas[rand.Next( 25 )];
                
if  (result.Contains(item))
                    
continue ;
                
else
                {
                    result.Add(item);
                    i
-- ;
                }
            }
        }
        
return  result.ToArray();
    }


    
#endregion

    
#region  受控随机抽取

    
///   <summary>
    
///  随机抽取
    
///   </summary>
    
///   <param name="rand"> 随机数生成器 </param>
    
///   <returns></returns>
     public   char [] ControllerRandomExtract(Random rand)
    {
        List
< char >  result  =   new  List < char > ();
        
if  (rand  !=   null )
        {
            
// 临时变量
            Dictionary < char int >  dict  =   new  Dictionary < char int > ( 26 );

            
// 为每个项算一个随机数并乘以相应的权值
             for  ( int  i  =  datas.Count  -   1 ; i  >=   0 ; i -- )
            {
                dict.Add(datas[i], rand.Next(
100 *  weights[i]);
            }

            
// 排序
            List < KeyValuePair < char int >>  listDict  =  SortByValue(dict);

            
// 拷贝抽取权值最大的前Count项
             foreach  (KeyValuePair < char int >  kvp  in  listDict.GetRange( 0 , Count))
            {
                result.Add(kvp.Key);
            }
        }
        
return  result.ToArray();
    }


    
#endregion

    
#region  Tools

    
///   <summary>
    
///  排序集合
    
///   </summary>
    
///   <param name="dict"></param>
    
///   <returns></returns>
     private  List < KeyValuePair < char int >>  SortByValue(Dictionary < char int >  dict)
    {
        List
< KeyValuePair < char int >>  list  =   new  List < KeyValuePair < char int >> ();

        
if  (dict  !=   null )
        {
            list.AddRange(dict);

            list.Sort(
              
delegate (KeyValuePair < char int >  kvp1, KeyValuePair < char int >  kvp2)
              {
                  
return  kvp2.Value  -  kvp1.Value;
              });
        }
        
return  list;
    }


    
#endregion

    
#endregion

    
#region  Properties

    
private   int  _Count;
    
///   <summary>
    
///  随机抽取个数
    
///   </summary>
     public   int  Count
    {
        
get
        {
            
return  _Count;
        }
        
set
        {
            _Count 
=  value;
        }
    }

    
#endregion
}

----------------------------------------------------------------------------------------

     调用测试代码:

         static   void  Main( string [] args)
        {
            
// 从集合中随机抽取个数
             const   ushort  COUNT  =   6 ;
            
// 循环次数
             const   int  FOR_COUNT  =   1000 ; // 10000

            
#region  1000、10000次随机抽取,每次抽取6个

            RandomController rc 
=   new  RandomController(COUNT);
            
// 累积器
            Dictionary < char int >  result  =   new  Dictionary < char int > ();
            
// 随机数生成器
            Random rand  =   new  Random();
            
// 循环生成随机数
             for  ( int  i  =   0 ; i  <  FOR_COUNT; i ++ )
            {
                
char [] rands  =  rc.RandomExtract(rand);
                
for  ( int  j  =   0 ; j  <  COUNT; j ++ )
                {
                    
char  item  =  rands[j];
                    
if  (result.ContainsKey(item))
                        result[item] 
+=   1 ;
                    
else
                        result.Add(item, 
1 );
                }
                Thread.Sleep(
5 );
            }

            Console.WriteLine(
" \t\t出现次数\t占总共出现次数百分比 " );

            
foreach  (KeyValuePair < char int >  item  in  result)
            {
                Console.WriteLine(item.Key 
+   " \t\t "   +  item.Value.ToString()  +   " \t\t "   +  (( double )item.Value  /  ( double )(FOR_COUNT  *  COUNT)).ToString( " 0.00% " ));
            }
        }

     普通随机抽取分别进行1000次和10000次测试显示:

          1000次

          

          10000次

          

     控制随机几率随机抽取分别进行1000次和10000次代码修改:

          1.     将rc.RandomExtract(rand)改为rc.ControllerRandomExtract(rand)

          2.     注释掉上面输出部分代码,加上以下代码:

            Dictionary < char , ushort >  items  =   new  Dictionary < char , ushort > ();
            
for  ( int  i  =   0 ,j  =  rc.datas.Count; i  <  j; i ++ )
            {
                items.Add(rc.datas[i],rc.weights[i]);
            }

            Console.WriteLine(
" \t\t出现次数\t占总共出现次数百分比\t权值 " );
            
            
foreach  (KeyValuePair < char int >  item  in  result)
            {
                Console.WriteLine(item.Key 
+   " \t\t "   +  item.Value.ToString()  +   " \t\t "   +  (( double )item.Value  /  ( double )(FOR_COUNT  *  COUNT)).ToString( " 0.00% " +   " \t\t\t "   +  items[item.Key]);
            }

          测试结果:

          1000次

          

          10000次

          

小结

     从上面统计结果可以看出,普通随机数分布比较均匀,随机抽中的几率相对持平;但是经过控制随机抽中几率,权值高的明显抽中几率要高,另外需要注意的是这里只输出了25个字母,也就是还有一个字母没有被抽中过,因为按算法他是始终不会出现的,除非一次抽26个!!

     需要注意的是:

          1.     合理的调配权值和随机数生成的大小也很有关系,大家可以看到权值5的和权值1的出现几率相差不是5倍,而是30-50倍。

          2.     如果数据源随机的数据大,比如上千上万条,按现在的程序是不可行的,可以先随机抽取比所需抽取个数多2-5倍的数据,然后直接按权值排序然后抽取前N位来达到目的。

          3.     最重要的一点就是注意随机性,这个算法如果不是建立在随机的机制上是毫无价值的!!

本文转自博客园农民伯伯的博客,原文链接:控制随机抽中几率 [ C# | Random ],如需转载请自行联系原博主。

目录
相关文章
|
6月前
|
算法 C#
54.c#:random类
54.c#:random类
128 1
|
6月前
|
开发框架 前端开发 .NET
C#编程与Web开发
【4月更文挑战第21天】本文探讨了C#在Web开发中的应用,包括使用ASP.NET框架、MVC模式、Web API和Entity Framework。C#作为.NET框架的主要语言,结合这些工具,能创建动态、高效的Web应用。实际案例涉及企业级应用、电子商务和社交媒体平台。尽管面临竞争和挑战,但C#在Web开发领域的前景将持续拓展。
189 3
|
6月前
|
SQL 开发框架 安全
C#编程与多线程处理
【4月更文挑战第21天】探索C#多线程处理,提升程序性能与响应性。了解C#中的Thread、Task类及Async/Await关键字,掌握线程同步与安全,实践并发计算、网络服务及UI优化。跟随未来发展趋势,利用C#打造高效应用。
196 3
|
8天前
|
C# 开发者
C# 一分钟浅谈:Code Contracts 与契约编程
【10月更文挑战第26天】本文介绍了 C# 中的 Code Contracts,这是一个强大的工具,用于通过契约编程增强代码的健壮性和可维护性。文章从基本概念入手,详细讲解了前置条件、后置条件和对象不变量的使用方法,并通过具体代码示例进行了说明。同时,文章还探讨了常见的问题和易错点,如忘记启用静态检查、过度依赖契约和性能影响,并提供了相应的解决建议。希望读者能通过本文更好地理解和应用 Code Contracts。
20 3
|
29天前
|
安全 C# 数据安全/隐私保护
实现C#编程文件夹加锁保护
【10月更文挑战第16天】本文介绍了两种用 C# 实现文件夹保护的方法:一是通过设置文件系统权限,阻止普通用户访问;二是使用加密技术,对文件夹中的文件进行加密,防止未授权访问。提供了示例代码和使用方法,适用于不同安全需求的场景。
|
2月前
|
API C#
C# 一分钟浅谈:文件系统编程
在软件开发中,文件系统操作至关重要。本文将带你快速掌握C#中文件系统编程的基础知识,涵盖基本概念、常见问题及解决方法。文章详细介绍了`System.IO`命名空间下的关键类库,并通过示例代码展示了路径处理、异常处理、并发访问等技巧,还提供了异步API和流压缩等高级技巧,帮助你写出更健壮的代码。
39 2
|
2月前
|
安全 程序员 编译器
C#一分钟浅谈:泛型编程基础
在现代软件开发中,泛型编程是一项关键技能,它使开发者能够编写类型安全且可重用的代码。C# 自 2.0 版本起支持泛型编程,本文将从基础概念入手,逐步深入探讨 C# 中的泛型,并通过具体实例帮助理解常见问题及其解决方法。泛型通过类型参数替代具体类型,提高了代码复用性和类型安全性,减少了运行时性能开销。文章详细介绍了如何定义泛型类和方法,并讨论了常见的易错点及解决方案,帮助读者更好地掌握这一技术。
71 11