C#随机数命中率远高于预期
我们有一个随机数发生器:C#随机数命中率远高于预期,c#,asp.net,random,C#,Asp.net,Random,我们有一个随机数发生器: Random rnd = new Random(); bool PiratePrincess = rnd.Next(1, 5000) == 1; 这在每个页面视图上都被调用。变量为True的概率应为1/5000。然而,在大约15000个页面浏览量中,这已经是真实的大约20次了 有人能解释一下为什么会出现这种情况,以及如何防止这种情况,使其大约是5000倍吗?这一点都不重要,因为它是真正随机的 编辑:这样做是否有效? Random rnd = new Random();
Random rnd = new Random();
bool PiratePrincess = rnd.Next(1, 5000) == 1;
这在每个页面视图上都被调用。变量为True
的概率应为1/5000。然而,在大约15000个页面浏览量中,这已经是真实的
大约20次了
有人能解释一下为什么会出现这种情况,以及如何防止这种情况,使其大约是5000倍吗?这一点都不重要,因为它是真正随机的
编辑:这样做是否有效?
Random rnd = new Random();
bool PiratePrincess = (rnd.Next() + ThisUser.UserID + ThisUser.LastVisit.Ticks + DateTime.Now.Ticks) % 5000 == 1;
这些页面浏览的速度有多快
new Random
将基于当前时间初始化,因此许多重叠请求将获得相同的随机种子。基于与当前时间散列的远程IP地址随机分配种子,以获得更多的唯一性
这就是说,一枚硬币可以抛20次,每次都有人头。这是合理的随机结果
编辑:这样就可以了
var r = new Random(
Convert.ToInt32(
(ThisUser.UserID ^ ThisUser.LastVisit.Ticks ^ DateTime.Now.Ticks) & 0xFFFFFFFF)
);
var isPiratePrincess = (r.Next (5000) == 42);
每次生成一个数字时,您都会使用一个新种子创建一个新的随机数生成器。得到的值的分布与种子的关系比与所用算法的分布特征的关系更大
我不确定实现更均匀分布的最佳方法是什么。如果您的通信量足够低,您可以在“检查可除性5000”页面上使用命中计数器,但如果您尝试扩展它,这种方法将很快遇到争用问题。随机数生成器使用当前时间作为随机数生成器的种子。因此,如果两个同时出现,理论上它们可能有相同的种子。如果您希望数字彼此唯一,您可能希望对页面的所有实例使用相同的随机数生成器。您只需要实例化一个
random
实例,因此:
public class Widget
{
private static rngInstance = new Random() ;
public bool IsPiratePrincess()
{
bool isTrue = rngInstance.Next(1, 5000) == 1 ;
return isTrue ;
}
}
伪随机数生成器实现了一个序列。每次实例化一个新实例时,它都会基于(除其他外)一天中的当前时间对自身进行播种,并开始一个新的系列
如果您在每次调用时实例化一个新的值,并且实例化足够频繁,您可能会在生成的伪随机值流中看到相似之处,因为初始种子值可能彼此接近
编辑以注意:由于System.Random
不是线程安全的,您可能需要以线程安全的方式包装它。这门课(从)就可以了。它使用每线程静态字段,并为每个线程实例化一个RNG实例:
public static class RandomGen2
{
private static Random _global = new Random();
[ThreadStatic]
private static Random _local;
public static int Next()
{
Random inst = _local;
if (inst == null)
{
int seed;
lock (_global) seed = _global.Next();
_local = inst = new Random(seed);
}
return inst.Next();
}
}
我对用另一个RNG的输出为每个RNG实例播种有点怀疑。我觉得这很容易使结果产生偏差。我认为使用默认构造函数可能更好
另一种方法是锁定对单个RNG实例的每个访问:
public static class RandomGen1
{
private static Random _inst = new Random();
public static int Next()
{
lock (_inst) return _inst.Next();
}
}
但这会带来一些性能问题(瓶颈,加上每次调用时的锁开销)。如果问题真的是种子(包括我在内的许多人都怀疑),那么您可以保留Random()的一个实例(在高容量情况下会导致并发问题,但对于每天约15000次的点击来说是可以的),或者你可以给种子引入更多的熵 如果这是一个应用程序,您不希望有决心的人破坏伪随机特性,那么您应该研究在服务器上生成良好种子的软件或硬件(扑克网站通常使用硬件熵生成器)
如果你想要一个好的分布,不要期望人们尝试破解你的解决方案,考虑只混合各种熵的来源(当前时间戳,用户的用户代理字符串的哈希,IPv4地址或IPv6地址的散列等)。
更新:你提到你也有用户ID。将熵与上面提到的一个或多个项目一起散列,特别是当前时间戳中的刻度。如果你完全避免
随机
,你可以得到正好1/5000
vals = Enumerable.Repeat(false, 4999).ToList();
vals.Add(true);
// this or an in-memory shuffle if that is a concern
isPiratePrincess = vals.OrderBy(v => Guid.NewGuid()).ToList();
// remove values as it's queried & reset when empty.
这里使用Random的问题是,您可能处于15K中有20个被合法命中的状态。在接下来的几千次迭代中,您将遇到一个冷条,使您回归到预期的平均值。我猜您的问题是,您正在为每个页面创建一个新的
随机
。您需要一个持久的Random
,您将在其上为每个页面调用.Next()
,否则您将根据创建可能没有良好随机特性的Random
的默认方法创建一个新的Random
。另一种可能性是,Random
根本没有那么好的随机特性(不是真正的随机),你可能必须想出一种新的方法来生成你的随机数。如果你在一个紧密的循环中调用它,你可能会在new Random()之后多次得到相同的结果将当前时间戳用作种子。如果在时间戳的刻度计数更改之前多次调用,每次都会得到相同的结果。假设这是一个不太可能出现的web场景,但请查找类似的问题。@mydogisbox:默认方法是使用当前的勾号计数。除非15000页的浏览量大约是每秒,否则这很可能不是问题所在。@EricJ。我不知道你在说什么。我的论点与你的论点基本相同吗?我知道这是一个合理的结果,但我认为最有可能的情况是RNG正在做一些我没有预料到的事情!您的更新应该改为:Random rnd=new Random(ThisUser.UserID^ ThisUser.LastVisit.Ticks^ DateTime.Now.Ticks);bool-piratePrenses=(rnd.Next(15000)==1)代码>这是一个结果,从字面上看,有一百万分之一的机会是“合法的”。如果你的代码中的错误的机会远远超过百万分之一,我不会说它是合法的,因为@Insta一直在溢出,只要添加它们就行了