Random number generator only generating one random number
有以下功能:
1 2 3 4 5 6
| //Function to get random number
public static int RandomNumber (int min, int max )
{
Random random = new Random ();
return random .Next(min, max );
} |
我叫它:
1 2 3
| byte[] mac = new byte[6];
for (int x = 0; x < 6; ++x )
mac [x ] = (byte)(Misc .RandomNumber((int)0xFFFF, (int)0xFFFFFF ) % 256); |
如果这步环与调试器在运行时得到不同的值(这是什么?我想)。 然而,如果放在一个断点,下面的两行代码,全行业的"Mac"阵列具有平等的价值。
这是为什么发生?
- 使用new Random().Next((int)0xFFFF, (int)0xFFFFFF) % 256);不会产生比.Next(0, 256)更好的"随机"数。
- 您可能会发现这个Nuget包很有用。它提供了一个静态的Rand.Next(int, int)方法,该方法提供对随机值的静态访问,而不锁定或陷入种子重用问题。
每次执行new Random()时,都会使用时钟进行初始化。这意味着在一个紧密的循环中,你会多次得到相同的值。您应该保留单个Random实例,并在同一实例上继续使用Next。
1 2 3 4 5 6 7 8 9
| //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 );
}
} |
编辑(见评论):为什么我们需要一个lock在这里?
基本上,Next将改变Random实例的内部状态。如果我们同时从多个线程执行此操作,您可能会说"我们只是使结果更加随机",但实际上我们所做的操作可能会破坏内部实现,并且我们也可能从不同的线程开始获取相同的数字,这可能是一个问题,而且可能不会。但是,对内部发生的事情的保证是更大的问题;因为Random不保证线程安全。因此,有两种有效的方法:
- 同步,这样我们就不会同时从不同的线程访问它。
- 每个线程使用不同的Random实例
两者都可以,但同时将多个调用方的单个实例静音只会带来麻烦。
lock实现了这些方法中的第一种(和更简单的方法);然而,另一种方法可能是:
1 2
| private static readonly ThreadLocal <Random > appRandom
= new ThreadLocal <Random >(() => new Random ()); |
这是每个线程的,所以不需要同步。
- 在这种情况下,我不确定这是否是答案,因为问题表明,当代码的这段附近没有断点时,函数工作正常。当然,我同意他应该使用一个随机对象的实例,但我认为这并不能回答他的问题。
- @约翰-仅仅是想让他保持在视线中就够难了(主要是作为中间距离的一个点)。
- 一般来说,所有静态方法都应该是线程安全的,因为很难保证多个线程不会同时调用它。通常不需要使实例(即非静态)方法具有线程安全性。
- hmm,相反,我认为实例方法需要更多的同步,因为大多数情况下,它们使用在中定义的对象的外部状态,而静态方法使用在不同(可能是并行)方法调用之间不共享的局部变量(基于堆栈)。所以我认为在这个特殊的情况下,同步可能是可以的(因为它使用一个共享资源),但我不会将其作为一般的最佳实践规则。
- @Florin-两者之间没有"基于堆栈"的区别。静态字段和"外部状态"一样多,绝对会在调用方之间共享。对于实例,不同的线程很可能有不同的实例(一个公共模式)。使用statics,可以保证它们都共享(不包括[threadstatic])。
- 为什么多个线程调用静态"randomnumber"方法会导致错误?
- @格多伦,你有错误吗?"锁"应该可以防止线程在这里相互绊倒…
- 不。。。我一点也没有出错,也没有使用lock。你能在答题框里再详细说明一下需要什么吗?
- @你的意思是:为什么这里需要lock?
- 应添加来自msdn的此行"此类型的任何公共静态(在VisualBasic中共享)成员都是线程安全的。任何实例成员都不能保证线程安全。"谢谢!
- @gdoron也看到了"社区内容"中的"随机不线程安全"——混乱的内部状态是非常糟糕的
- @MarcGravell使用ThreadLocal方法,如果两个线程同时访问appRandom,那么两个线程产生相同序列的风险是否仍然存在?或者线程的某些属性对生成的序列有贡献吗?
- @脚本从无到有,我想是的;如果这很重要,你可以一直有一个同步的主服务器Random……P
- 你为什么不能用lock(random)?
- @如果物体从未公开暴露:你可以。(非常理论上的)风险是其他线程正在以您不期望的方式锁定它。
- 我使用了锁机制,经过数百万次迭代,生成的唯一随机数是0。使用ThreadLocal解决了问题。理论上,锁应该工作得很好。但是在多次迭代之后每次都会出错。谢谢
- @斯米伦,我觉得这不太可能;一个重复会很有趣。
- @斯米隆,很可能你只是在锁外随意使用。锁定并不能阻止所有对您所锁定内容的访问——它只是确保同一实例上的两个lock语句不会同时运行。因此,只有当所有的random.Next()呼叫都在lock (syncObject)内时,lock (syncObject)才有帮助。如果您描述的场景确实发生在正确使用lock的情况下,那么它也极有可能发生在单线程场景中(例如,Random被巧妙地破坏)。
- 我在最近的一个项目(fungenerators.com/random/number)上遇到了类似的问题,尽管它使用的是PHP。结果发现其中一个包含的PHP类以错误的方式初始化了随机生成器的种子。所以它也可能是代码之外的东西。
为了便于在整个应用程序中重用,静态类可能会有所帮助。
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| 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; } }
} |
然后可以使用静态随机实例和代码,例如
1
| StaticRandom.Instance.Next(1, 100); |
Mark的解决方案可能非常昂贵,因为它每次都需要同步。
我们可以使用线程特定的存储模式来绕过同步的需要:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| 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 );
}
} |
测量这两个实现,您应该看到一个显著的区别。
- 锁是非常便宜的,当他们没有竞争…即使有争议,我也希望"现在就用数字做点什么"代码在最有趣的场景中降低锁的成本。
- 同意,这解决了锁定问题,但对于一个小问题来说,这不是一个非常复杂的解决方案吗:您需要编写"两"行代码来生成一个随机数,而不是一个。读一行简单的代码真的值得吗?
- +1使用一个额外的全局Random实例来获取种子是一个不错的主意。还请注意,可以使用.NET 4中引入的ThreadLocal类进一步简化代码(Phil也在下面写到)。
我的回答是:
重申正确的解决方案:
1 2 3 4 5 6 7 8 9 10 11
| namespace mySpace
{
public static class Util
{
private static rnd = new Random ();
public static int GetRandom ()
{
return rnd .Next();
}
}
} |
所以你可以打电话给:
1
| var i = Util.GetRandom(); |
贯穿始终。
如果您严格地需要一个真正的无状态静态方法来生成随机数,那么您可以依赖一个Guid。
1 2 3 4 5 6 7
| public static class Util
{
public static int GetRandom()
{
return Guid.NewGuid().GetHashCode();
}
} |
它会慢一点,但比Random.Next更随机,至少从我的经验来看。
但不是:
1
| new Random (Guid .NewGuid().GetHashCode()).Next(); |
不必要的对象创建会使它变慢,特别是在循环下。
永远不会:
它不仅速度慢(在循环中),而且随机性…嗯,据我说不是很好。
- 我不同意guid的情况。随机类实现了统一的分布。在guid中不是这样的。guid的目标是唯一的,而不是均匀分布的(它的实现大部分时间是基于一些硬件/机器属性,这与…随机性)。
- @作为一个,我不确定。它是我们讨论的guid的散列值,而不是guid本身。而且很随意。你测试过这两个吗?我发现与Random.Next的碰撞更多。第二,MS不再基于硬件属性生成guid。
- 如果您不能证明guid生成的一致性,那么将其作为随机的使用是错误的(散列将是距离一致性的另一步)。同样,碰撞也不是问题:碰撞的均匀性是。关于guid的生成不再在硬件上了,我要去rtfm,我的坏(有参考吗?)
- @askolein我不需要证明guid生成的一致性,正如我所说的,重复一遍,guid的散列。.NET guid的GetHashCode基于guid的字符串表示,而不是数字本身(无论如何都是不可能的),而且据我测试,它是随机的。不可能显示出一些可预测的特征。如果散列是离统一性又一步的话,这不是一件好事吗?在随机讨论中,一致性是一件好事吗?或者我读错了你的"一致性"?
- 对"随机"有两种理解:1。缺少图案或2。在概率分布描述的进化过程中缺乏模式(2包含在1中)。您的guid示例在案例1中是正确的,而不是在案例2中。相反,Random类与case 2(因此,case 1也一样)匹配。如果您不在第2种情况下,您只能将Random的用法替换为Guid+Hash。案例1可能足够回答这个问题,然后,你的Guid+Hash就可以了。但没有清楚地说(附:这件制服)
- 我认为使用guid+hash作为种子,然后使用random可以解决案例1和2。这就是我需要的,它不是一个时间依赖的发电机。
- @作为Kolein的一部分测试数据,我通过ent(fourmilab.ch/random)运行了几批Random和Guid.NewGuid().GetHashCode(),两者都是随机的。new Random(Guid.NewGuid().GetHashCode())也可以工作,使用同步的"主"Random来为"子"Random生成种子。当然,这取决于您的系统如何生成guid——对于我的系统来说,它们是相当随机的,而对于其他系统,它甚至可能是加密随机的。所以现在Windows或MS SQL似乎很好。但Mono和/或Mobile可能不同。
- @六安,我没想到,但好吧…非常有趣。谢谢你的信息。
- 在guid和随机数之间有一个细微的区别。guid被设计为尽可能独特,但不一定是随机的。虽然理论上可以发生碰撞(即两次获得相同的guid),但发生碰撞的可能性应该非常小。它们在概念上与顺序整数相同,但其设计目的是在"值空间"中提供(合理的)均匀分布,而不是递增整数。随机数可以也应该产生碰撞。对于介于1和10之间的随机整数,您将得到相同的数字,每十次两次。
- @正如我之前在评论中所说的,edb虽然guid(一个大数字)是唯一的,但是.NET中guid的GetHashCode是从其字符串表示中派生出来的。根据我的喜好,输出是随机的。
- @纳法尔-我不同意这一点。这里重要的区别是guid应该是唯一的,没有碰撞(可能是随机的),而随机数应该有碰撞,例如系列5 2 2 7 4 5是"随机的",但有重复的数字。
- 不要依赖guid。guid在任何可能的意义上都不应该被用作随机的。它是独一无二的,独一无二的,独一无二的。
我宁愿使用以下类生成随机数:
1 2 3
| byte[] random ;
System.Security.Cryptography.RNGCryptoServiceProvider prov = new System.Security.Cryptography.RNGCryptoServiceProvider();
prov .GetBytes(random ); |
- 我不是低落的选民之一,但请注意,标准PNRG确实服务于真正的需要,即能够重复复制已知种子的序列。有时真正的加密RNG的纯粹成本太高了。有时需要加密RNG。可以说,马是为课程而生。
- 根据文档,这个类是线程安全的,所以这对它是有利的。
- 两个随机字符串的概率是多少?如果字符串只有3个字符,我想这很有可能发生,但是如果255个字符的长度是可能有相同的随机字符串,或者保证从算法中不会发生这种情况呢?
1)正如Marc Gravell所说,尝试使用一个随机生成器。把它添加到构造函数中总是很酷的:System.Environment.TickCount。
2)一个提示。假设您想要创建100个对象,并且假设每个对象都应该有自己的随机生成器(如果您在很短的时间内计算随机数的负载,这很方便)。如果要在循环中执行此操作(生成100个对象),可以这样做(以确保完全随机性):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| 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); |
干杯。
- 我会把System.Environment.TickCount移出循环。如果在迭代过程中循环,那么将有两个项目初始化为相同的种子。另一种选择是以不同的方式组合TickCount和I(例如System.Environment.TickCount<<8+I)
- 如果我理解正确:你的意思是,它可能发生,"system.environment.tickcount+i"可能产生相同的值吗?
- 编辑:当然,循环中不需要有滴答数。我的坏处:
- 默认的Random()构造函数无论如何都调用Random(Environment.TickCount)。
每次执行时
1
| Random random = new Random (15); |
如果你成百上千万次执行它并不重要,你将永远使用相同的种子。
如果你使用
1
| Random random = new Random (); |
如果黑客猜到了种子,并且你的算法与你的系统的安全性相关,你会得到不同的随机数序列,你的算法就被破坏了。我是你处决穆特的。在此构造函数中,种子由系统时钟指定,如果在很短的时间(毫秒)内创建了多个实例,则它们可能具有相同的种子。
如果你需要安全的随机数,你必须使用该类
System.Security.Cryptography.RNGCryptoServiceProvider
1 2 3 4 5 6 7 8 9 10 11 12 13
| 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);
} |
用途:
1
| int randomNumber = Next(1,100); |
- 除非你自己指定种子,否则这不是真的。
- 修理好了。正如你所说,谢谢,如果总是指定相同的种子,那么总是会生成相同的随机数序列。在我的答案中,如果您总是使用相同的种子,那么我引用带有参数的构造函数。随机类只生成伪随机数。如果有人发现您在算法中使用了什么种子,它可能会损害算法的安全性或随机性。使用rngCryptoServiceProvider类,您可以安全地拥有随机数。我已经改正了,非常感谢你的改正。
像这样声明随机类变量:
1 2 3 4
| Random r = new Random ();
// ... Get three random numbers.
// Here you'll get numbers from 5 to 9
Console .WriteLine(r .Next(5, 10)); |
如果每次从列表中获得不同的随机数,请使用
1
| r.Next(StartPoint,EndPoint) //Here end point will not be included |
每次申报一次。
有很多解决方案,这里有一个:如果你只想要数字,删除字母,方法会收到一个随机的结果长度。
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| public String GenerateRandom(Random oRandom, int iLongitudPin)
{
String sCharacters ="123456789ABCDEFGHIJKLMNPQRSTUVWXYZ123456789";
int iLength = sCharacters.Length;
char cCharacter;
int iLongitudNuevaCadena = iLongitudPin;
String sRandomResult ="";
for (int i = 0; i < iLongitudNuevaCadena; i++)
{
cCharacter = sCharacters[oRandom.Next(iLength)];
sRandomResult += cCharacter.ToString();
}
return (sRandomResult);
} |