控制随机抽中几率 [ 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<charint> dict = new Dictionary<charint>(26);

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

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

            
//拷贝抽取权值最大的前Count项
            foreach (KeyValuePair<charint> 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<charint>> SortByValue(Dictionary<charint> dict)
    {
        List
<KeyValuePair<charint>> list = new List<KeyValuePair<charint>>();

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

            list.Sort(
              
delegate(KeyValuePair<charint> kvp1, KeyValuePair<charint> 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<charint> result = new Dictionary<charint>();
            
//随机数生成器
            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<charint> 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<charint> 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.     最重要的一点就是注意随机性,这个算法如果不是建立在随机的机制上是毫无价值的!!

     

结束

     当你失去发明机会的时候,可以考虑更好的应用它!! 

 

posted @ 2009-02-13 05:42  农民伯伯  阅读(17537)  评论(12编辑  收藏  举报