随机数生成器仅生成一个随机数

Iva*_*nov 741 c# random

我有以下功能:

//Function to get random number
public static int RandomNumber(int min, int max)
{
    Random random = new Random();
    return random.Next(min, max);
}
Run Code Online (Sandbox Code Playgroud)

我怎么称呼它:

byte[] mac = new byte[6];
for (int x = 0; x < 6; ++x)
    mac[x] = (byte)(Misc.RandomNumber((int)0xFFFF, (int)0xFFFFFF) % 256);
Run Code Online (Sandbox Code Playgroud)

如果我在运行时使用调试器执行该循环,则会得到不同的值(这就是我想要的).但是,如果我在该代码下面放置一个断点两行,则"mac"数组的所有成员都具有相同的值.

为什么会这样?

Mar*_*ell 1011

每次new Random()使用时钟初始化它.这意味着在紧密循环中,您可以多次获得相同的值.您应该保留一个Random实例并继续Next同一个实例上使用.

//Function to get a random number 
private static readonly Random random = new Random(); 
private static readonly object syncLock = new object(); 
public static int RandomNumber(int min, int max)
{
    lock(syncLock) { // synchronize
        return random.Next(min, max);
    }
}
Run Code Online (Sandbox Code Playgroud)

编辑(见评论):我们为什么需要lock这里?

基本上,Next是要改变Random实例的内部状态.如果我们同时从多个线程那样做,你可以说"我们只是让结果更加随机",但我们实际上做的是可能破坏内部实现,我们也可以开始得到相同的数字从不同的线程,这可能是一个问题 - 也可能不是.然而,保证内部发生的事情是更大的问题; 因为Random不是让线程安全的任何保证.因此有两种有效的方法:

  • 同步,以便我们不会从不同的线程同时访问它
  • Random每个线程使用不同的实例

两者都可以; 但mutexing一个单一的在同一时间从多个来电实例找麻烦.

lock实现这些方法的第一(和更简单); 然而,另一种方法可能是:

private static readonly ThreadLocal<Random> appRandom
     = new ThreadLocal<Random>(() => new Random());
Run Code Online (Sandbox Code Playgroud)

这是每个线程,所以你不需要同步.

  • 作为一般规则,所有静态方法都应该是线程安全的,因为很难保证多个线程不会同时调用它.通常不需要***实例*(即非静态)方法是线程安全的. (17认同)
  • @Dan如果对象永远不会公开曝光:你可以.(非常理论上的)风险是一些其他线程以你没想到的方式锁定它. (5认同)
  • @Florin - 两者之间没有"基于堆栈"的区别.静态字段与"外部状态"一样多,并且**绝对**将在呼叫者之间共享.对于实例,不同的线程很可能具有不同的实例(常见模式).使用静态,*保证*它们共享(不包括[ThreadStatic]). (4认同)
  • @gdoron 你有错误吗?“锁”应该防止线程在这里相互绊倒...... (2认同)
  • @gdoron 你的意思是:为什么这里需要`lock`? (2认同)
  • 应添加来自 [MSDN](http://msdn.microsoft.com/en-us/library/system.random.aspx) 的这一行 _"此类型的任何公共静态(在 Visual Basic 中共享)成员都是线程安全的. 不保证任何实例成员都是线程安全的。”_ **谢谢!** (2认同)
  • @gdoron 另请参阅“社区内容”中的“随机不是线程安全的”——弄乱内部状态非常糟糕 (2认同)
  • 使用`ThreadLocal`方法的@MarcGravell如果这两个线程同时访问`appRandom`,那么两个线程是否仍然存在生成相同序列的风险?或者线程的某些属性是否有助于生成的序列? (2认同)
  • 为什么不能使用锁(随机)? (2认同)
  • @smiron 我觉得这不太可能;复制将非常有趣 (2认同)
  • @smiron你很可能只是在锁外使用随机数.锁定并不会阻止对您锁定的内容的所有访问 - 它只是确保同一实例上的两个锁定语句不会同时运行.所以`lock(syncObject)`只有在*all*`random.Next()`调用也在`lock(syncObject)`中时才有用.如果您描述的场景确实发生了,即使使用正确的"锁定",它也可能*非常*可能发生在单线程场景中(例如,"随机"被巧妙地破坏). (2认同)
  • @kchahat20 两个连续的数字相同是*有效的随机性*,但是可以肯定的是:您可以保留最后一个值并循环直到它发生变化 - 只是......带有警告:https://assets.amuniversal.com/321a39e06d6401301d80001dd8b71c47 (2认同)

Phi*_*hil 115

为了便于在整个应用程序中重复使用,静态类可能会有所帮助.

public static class StaticRandom
{
    private static int seed;

    private static ThreadLocal<Random> threadLocal = new ThreadLocal<Random>
        (() => new Random(Interlocked.Increment(ref seed)));

    static StaticRandom()
    {
        seed = Environment.TickCount;
    }

    public static Random Instance { get { return threadLocal.Value; } }
}
Run Code Online (Sandbox Code Playgroud)

然后你可以使用静态随机实例和代码

StaticRandom.Instance.Next(1, 100);
Run Code Online (Sandbox Code Playgroud)


Han*_*rbe 61

Mark的解决方案非常昂贵,因为它需要每次都同步.

我们可以通过使用特定于线程的存储模式来解决同步需求:


public class RandomNumber : IRandomNumber
{
    private static readonly Random Global = new Random();
    [ThreadStatic] private static Random _local;

    public int Next(int max)
    {
        var localBuffer = _local;
        if (localBuffer == null) 
        {
            int seed;
            lock(Global) seed = Global.Next();
            localBuffer = new Random(seed);
            _local = localBuffer;
        }
        return localBuffer.Next(max);
    }
}

Run Code Online (Sandbox Code Playgroud)

测量两个实现,您应该看到显着的差异.

  • 当它们没有争议时锁是非常便宜的......即使有争议,我也希望"现在用数字做一些代码"来使大多数有趣场景中锁的成本相形见绌. (11认同)
  • 同意,这解决了锁定问题,但这对于一个微不足道的问题来说仍然是一个非常复杂的解决方案:你需要编写"两行"代码来生成一个随机数而不是一个.这是否真的值得省去阅读一行简单的代码? (4认同)
  • +1使用额外的全局`Random`实例来获取种子是个不错的主意.还要注意,使用.NET 4中引入的`ThreadLocal <T>`类可以进一步简化代码(如下面的Phil [http://stackoverflow.com/a/11473510/69809]). (4认同)

naw*_*fal 38

我在这里回答:

重申正确的解决方案:

namespace mySpace
{
    public static class Util
    {
        private static rnd = new Random();
        public static int GetRandom()
        {
            return rnd.Next();
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

所以你可以打电话:

var i = Util.GetRandom();
Run Code Online (Sandbox Code Playgroud)

一切都在.

如果你真的需要一个真正的无状态静态方法来生成随机数,你可以依靠a Guid.

public static class Util
{
    public static int GetRandom()
    {
        return Guid.NewGuid().GetHashCode();
    }
}
Run Code Online (Sandbox Code Playgroud)

它会慢一点,但可能任何时候都要Random.Next,至少从我的经验来看.

不是:

new Random(Guid.NewGuid().GetHashCode()).Next();
Run Code Online (Sandbox Code Playgroud)

不必要的对象创建会使它变慢,特别是在循环中.

从未:

new Random().Next();
Run Code Online (Sandbox Code Playgroud)

它不仅速度慢(在一个循环内),它的随机性......根据我不是很好......

  • 我不同意Guid案.Random类实现了均匀分布.在Guid中并非如此.Guid目标是唯一的不均匀分布(并且它的实现大部分时间基于某些硬件/机器属性,这与......随机性相反). (10认同)
  • "随机"有两种理解:1.模式的_lack或2.模式跟随由概率分布描述的进化的模式_(2包含在1中).您的Guid示例在案例1中是正确的,而不是在案例2中.相反:`Random`类匹配案例2(因此,案例1也是如此).你只能用'Guid + Hash`替换'Random`的用法,如果你不是*在案例2中.案例1可能足以回答问题,然后,你的'Guid + Hash`工作正常.但是没有明确说明(ps:[这个制服](http://en.wikipedia.org/wiki/Uniform_distribution_(discrete))) (5认同)
  • 如果你不能证明Guid生成的一致性,那么将它作为随机使用是错误的(而Hash将是远离均匀性的另一步).同样,碰撞不是问题:碰撞的一致性.关于Guid一代不再在硬件上,我要去RTFM,我的坏(任何参考?) (4认同)
  • @Askolein对于一些测试数据,我运行了几批"Random"和"Guid.NewGuid().GetHashCode()`到Ent(http://www.fourmilab.ch/random/),两者同样是随机的.`new Random(Guid.NewGuid().GetHashCode())`也可以使用同步的"master"`Random`为"child"`Random`s生成种子..当然,它确实依赖关于你的系统如何生成Guids - 对于我的系统来说,它们是随机的,而在其他系统上它甚至可能是加密随机的.所以Windows或MS SQL现在似乎很好.但是,单声道和/或移动设备可能会有所不同. (2认同)
  • @EdB正如我之前在评论中所说的那样,虽然Guid(大量)是唯一的,但是.NET中Guid的`GetHashCode`是从它的字符串表示中派生出来的.根据自己的喜好,输出是随机的. (2认同)
  • 不要依赖 Guid。Guid 并不意味着在任何可以想象的意义上用作随机。它是唯一的,唯一的,唯一的,仅此而已——独一无二。 (2认同)

fAR*_*cRY 24

我宁愿使用以下类来生成随机数:

byte[] random;
System.Security.Cryptography.RNGCryptoServiceProvider prov = new System.Security.Cryptography.RNGCryptoServiceProvider();
prov.GetBytes(random);
Run Code Online (Sandbox Code Playgroud)

  • 我不是选民之一,但请注意标准的PNRG确实有需要 - 即能够从已知的种子中重复地重现序列.有时,真正的加密RNG的纯粹*成本*太多了.有时需要加密RNG.马可以说是课程. (30认同)
  • 根据[文档](http://msdn.microsoft.com/en-GB/library/system.security.cryptography.rngcryptoserviceprovider.aspx),这个类是线程安全的,所以这对它有利. (4认同)

sab*_*and 14

1)正如Marc Gravell所说,尝试使用一个随机发生器.将它添加到构造函数中总是很酷:System.Environment.TickCount.

2)一个提示.假设您要创建100个对象,并假设每个对象都应该有自己的随机生成器(如果您在很短的时间内计算随机数的LOADS,则会很方便).如果你在一个循环(生成100个对象)中这样做,你可以这样做(以确保完全随机性):

int inMyRandSeed;

for(int i=0;i<100;i++)
{
   inMyRandSeed = System.Environment.TickCount + i;
   .
   .
   .
   myNewObject = new MyNewObject(inMyRandSeed);  
   .
   .
   .
}

// Usage: Random m_rndGen = new Random(inMyRandSeed);
Run Code Online (Sandbox Code Playgroud)

干杯.

  • 我会将System.Environment.TickCount移出循环.如果在迭代时它结束,那么你将有两个项目初始化为同一个种子.另一种选择是将tickcount与i组合不同(例如System.Environment.TickCount << 8 + i) (3认同)
  • 无论如何,默认的`Random()`构造函数调用`Random(Environment.TickCount)` (2认同)

Jom*_*oma 10

每次执行

Random random = new Random (15);
Run Code Online (Sandbox Code Playgroud)

执行数百万次都没有关系,您将始终使用相同的种子。

如果你使用

Random random = new Random ();
Run Code Online (Sandbox Code Playgroud)

您会得到不同的随机数序列,如果黑客猜到了种子并且您的算法与系统的安全性有关 - 您的算法已损坏。我你执行多。在这个构造函数中,种子由系统时钟指定,如果在很短的时间(毫秒)内创建了多个实例,它们可能具有相同的种子。

如果您需要安全的随机数,则必须使用该类

System.Security.Cryptography.RNGCryptoServiceProvider

public static int Next(int min, int max)
{
    if(min >= max)
    {
        throw new ArgumentException("Min value is greater or equals than Max value.");
    }
    byte[] intBytes = new byte[4];
    using(RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider())
    {
        rng.GetNonZeroBytes(intBytes);
    }
    return  min +  Math.Abs(BitConverter.ToInt32(intBytes, 0)) % (max - min + 1);
}
Run Code Online (Sandbox Code Playgroud)

用法:

int randomNumber = Next(1,100);
Run Code Online (Sandbox Code Playgroud)

  • `执行数百万次都没有关系,您将始终使用相同的种子。` 除非您自己指定种子,否则这是不正确的。 (2认同)

The*_*ias 5

从 .NET 6 开始,该类Random现在配备了一个名为 的静态属性Shared

提供可以从任何线程同时使用的线程安全的 Random 实例。

你可以这样使用它:

// Function to get random number
public static int RandomNumber(int min, int max)
{
    return Random.Shared.Next(min, max);
}
Run Code Online (Sandbox Code Playgroud)

访问线程安全对象的开销很小,因此,如果您计划在单个线程上尽快生成数百万个随机数,那么最好创建一个专用Random实例,而不是依赖Shared.