博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
控制随机抽中几率 [ C# | Random ]
阅读量:7088 次
发布时间:2019-06-28

本文共 5416 字,大约阅读时间需要 18 分钟。

一、文字解说:

          为待随机抽取的集合每个项加一个权值,这个权值就是随机几率,比如正常被抽正的几率为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.     最重要的一点就是注意随机性,这个算法如果不是建立在随机的机制上是毫无价值的!!

本文转自博客园农民伯伯的博客,原文链接:,如需转载请自行联系原博主。

你可能感兴趣的文章
Exchange Server 2013系列十二:邮箱的基本管理
查看>>
[C#进阶系列]专题二:你知道Dictionary查找速度为什么快吗?
查看>>
并发连接数、请求数、并发用户数
查看>>
SDA报告给各国网络空间安全防卫水平进行评级
查看>>
去小机化思维(二)--【软件和信息服务】2015.03
查看>>
【翻译】Sencha Cmd中脚本压缩方法之比较
查看>>
最新.NET 5.0 C#6 MVC6 WCF5 NoSQL Azure开发120课视频
查看>>
爱因斯坦计划最新进展(201710)
查看>>
传统HA系统的终结者-【软件和信息服务】2013.11
查看>>
Spread for Windows Forms快速入门(15)---使用 Spread 设计器
查看>>
自动抓屏工具 -- psr
查看>>
高端手机市场,拼配置还是拼安全
查看>>
评蔡文胜区块链10问,数字货币不等于区块链,套路韭菜不要太明显
查看>>
数据丢失零容忍
查看>>
php分词
查看>>
经典图书评注:Accustoming Yourself to C++
查看>>
转载:RamDisk~充分利用你的大内存,详细教程(11月7 新功能更新)
查看>>
用C#代码实现类似QQ窗体的“上、左、右”停靠功能
查看>>
jqPlot
查看>>
将Access换成sql要改些什么?注意哪些问题?(汇总)
查看>>