Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/asp.net/34.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/ember.js/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C#随机数命中率远高于预期_C#_Asp.net_Random - Fatal编程技术网

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一直在溢出,只要添加它们就行了