前言
关于这个算法也许(肯定)已经被发明,但是我、我身边的朋友、我的老师在这之前是不知道也没能想出来的,如果你不知道的话,那么也包括你了: ) 在这个范围内被首次提出应该算是“发明”的!!增加、减少随机抽中几率——我的好朋友狄鹏在三年前想到的一个算法,我现在拿出来发扬光大。此算法可用于题库随机抽题、赌博机控制出彩率,甚至俄罗斯方块等游戏,有广泛的用途!也希望能帮得到你!
强调
在随机的基础上增控制抽中几率,注意随机性!!
正文
一、文字解说:
为待随机抽取的集合每个项加一个权值,这个权值就是随机几率,比如正常被抽正的几率为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
//========================================================================
//
// 作 者:小唐
// 邮 箱:[email protected]
// 博 客:http://over140.cnblogs.com/
// 时 间:2009-2-10
// 描 述:控制随机抽中几率。
//
//========================================================================
using System;
using System.Collections.Generic;
public class RandomController
{
#region Member Variables
//待随机抽取数据集合
public List
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
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
///
/// 构造函数
///
/// 随机抽取个数
public RandomController(ushort count)
{
if (count > 26)
throw new Exception("抽取个数不能超过数据集合大小!!");
_Count = count;
}
#endregion
#region Method
#region 普通随机抽取
///
/// 随机抽取
///
/// 随机数生成器
///
public char[] RandomExtract(Random rand)
{
List
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 受控随机抽取
///
/// 随机抽取
///
/// 随机数生成器
///
public char[] ControllerRandomExtract(Random rand)
{
List
if (rand != null)
{
//临时变量
Dictionary
//为每个项算一个随机数并乘以相应的权值
for (int i = datas.Count - 1; i >= 0; i--)
{
dict.Add(datas[i], rand.Next(100) * weights[i]);
}
//排序
List
//拷贝抽取权值最大的前Count项
foreach (KeyValuePair
{
result.Add(kvp.Key);
}
}
return result.ToArray();
}
#endregion
#region Tools
///
/// 排序集合
///
///
///
private List
{
List
if (dict != null)
{
list.AddRange(dict);
list.Sort(
delegate(KeyValuePair
{
return kvp2.Value - kvp1.Value;
});
}
return list;
}
#endregion
#endregion
#region Properties
private int _Count;
///
/// 随机抽取个数
///
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
//随机数生成器
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
{
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
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
{
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. 最重要的一点就是注意随机性,这个算法如果不是建立在随机的机制上是毫无价值的!!
结束
当你失去发明机会的时候,可以考虑更好的应用它!!
文章来自学IT网:http://www.xueit.com/html/2009-02/21_563_00.html