在C#中生成随机值

33

如何使用C#中的Random类生成随机的Int64和UInt64值?

10个回答

76

这应该能解决问题。(这是一个扩展方法,因此您可以像调用Random对象上的普通NextNextDouble方法一样调用它)。

public static Int64 NextInt64(this Random rnd)
{
    var buffer = new byte[sizeof(Int64)];
    rnd.NextBytes(buffer);
    return BitConverter.ToInt64(buffer, 0);
}

如果您想使用无符号整数,只需在所有地方将Int64替换为UInt64即可,所有内容都应该正常工作。

注意:由于未提供有关所生成数字的安全性或所需随机性的上下文(事实上,OP特别提到了Random类),因此我的示例仅涉及Random类,当随机性(通常量化为information entropy)不是问题时,这是首选解决方案。出于兴趣,请参阅其他答案,其中提到了RNGCryptoServiceProvider(位于System.Security命名空间中提供的RNG),可以几乎完全相同地使用。


1
如果使用Random存在限制,而且这种限制对于预期使用来说过于严格,那么除了提到使用Random的局限性之外,还要给出替代方案,并满足OP的需求。 - JeffH
5
请注意,这种方法会返回负数和Int64.MaxValue,而System.Random.Next()只返回包括0但不包括Int32.MaxValue在内的正数。 - Christoph Rüegg
@Christoph:是的,观察得很好。但是您可以很容易地修改我的方法,通过忽略缓冲区的最高有效位(MSB)只生成正值。 - Noldorin
我尝试实现这个解决方案,但发现结果并不令人满意地随机。在生成数千个数字后,我发现绝大多数数字有19位,偶尔有18位数字,非常罕见的数字有17位。在我生成的数千个数字中,我从未看到任何少于16位的数字。如何修改此解决方案以修复此问题? - kd7iwp
2
@kd7iwp:我担心你对“随机”有一些糊涂的认识。这里关键是生成值的分布。整数是否均匀分布?当然是的,因为它们的位是独立的和均匀分布的随机变量。现在观察一下,具有(n + 1)位数字的数字比具有n位数字的数字多大约10倍,你应该已经很清楚了。 - Noldorin
@Noldorin:我们如何忽略最高有效位?你能分享一下吗? - Görkem Öğüt

29

使用 Random.NextBytes()BitConverter.ToInt64 / BitConverter.ToUInt64

// Assume rng refers to an instance of System.Random
byte[] bytes = new byte[8];
rng.NextBytes(bytes);
long int64 = BitConverter.ToInt64(bytes, 0);
ulong uint64 = BitConverter.ToUInt64(bytes, 0);
请注意,使用两次Random.Next(),移动一个值,然后进行OR/添加操作是行不通的。Random.Next()只生成非负整数,即它生成31位而不是32位,因此两次调用的结果仅产生62个随机位,而不是覆盖完整范围的64位Int64/UInt64所需的64位。( Guffa的答案展示了如何使用三个调用Random.Next()来实现。)

@AlexZhukovskiy:用哪种方法?你认为我在哪里丢失了 MSB? - Jon Skeet
@JonSkeet 他们是的,但是有一个很大的问题,即它们是否按照中心极限定理正确分布。 - Alex Zhukovskiy
通过这些字节,我们获得一个分布Z = X0 + X1*256 + X2*256*256 + ...,其中独立随机生成的X0X1... X7,因此在一般情况下,Z不会是均匀分布变量。Z可以是范围内的任何值,但并非所有值具有相等概率。 - Alex Zhukovskiy
@JonSkeet 嗯,我同意。它将完全均匀分布。CLT在这里不适用,因为字节之间没有交集(对我们来说很明显,但对数学来说不是),所以 Z = X0 + X1*256 + X2*256*256 + ... 在这里有点不正确。最后,我们可以通过仅指定底部边界 int.MaxValue,使用 4(或 3 个正值) rand.Next() 调用生成一个值。这比 8 次调用更好(因为 Random.NextBytes 对每个字节调用 InternalSample)。 - Alex Zhukovskiy
1
@AlexZhukovskiy:这完全改变了你的反对意见,而且是基于实现细节的。即使你想要完整的范围,你也可以通过3次调用Random.Next来实现,因为每次调用会生成31位的随机数据,而我们只需要总共64位。 - Jon Skeet
显示剩余8条评论

10

这里是代码,使用的是加密服务(而不是Random类),理论上比Random类更好的随机数生成器。你可以很容易地将其作为Random的扩展或创建自己的Random类,其中RNGCryptoServiceProvider是一个类级别的对象。

using System.Security.Cryptography;
public static Int64 NextInt64()
{
   var bytes = new byte[sizeof(Int64)];    
   RNGCryptoServiceProvider Gen = new RNGCryptoServiceProvider();
   Gen.GetBytes(bytes);    
   return BitConverter.ToInt64(bytes , 0);        
}

这绝对值得注意,尽管根据问题,OP似乎并不太关心生成数字的随机性。此外,重要的是要意识到RNGCryptoServiceProvider比Random慢得多(尽管在这里性能可能或可能不重要)。 - Noldorin

6

您可以使用位移操作将三个31位的随机数组合成一个64位的随机数,但需要使用三个31位的随机数来获取足够的比特位:

long r = rnd.Next();
r <<= 31;
r |= rnd.Next();
r <<= 31;
r |= rnd.Next();

5

我经常使用以下代码获取随机种子(为简洁起见删除了错误检查):

m_randomURL = "https://www.random.org/cgi-bin/randnum?num=1&min=1&max=1000000000";
HttpWebRequest req = (HttpWebRequest)WebRequest.Create(m_randomURL);
StreamReader stIn = new StreamReader(req.GetResponse().GetResponseStream());
Random rand = new Random(Convert.ToInt32(stIn.ReadToEnd()));

random.org使用大气噪声来生成随机性,显然用于彩票等方面。


3

你并没有说明你将如何使用这些随机数...请记住,Random返回的值不是“密码学安全”的,不应该用于涉及(大量的)秘密或(大量的)金钱的事情。


-1 没有提到 .Net 加密 RNG。http://msdn.microsoft.com/zh-cn/library/system.security.cryptography.rngcryptoserviceprovider.aspx - Samuel
1
而 RNGCryptoServiceProvider 生成加密安全的随机数,并满足他回答中的条件。 - Samuel
1
约翰·冯·诺伊曼:“任何考虑使用算术方法生成随机数的人,当然是处于一种罪恶状态中。” - sipsorcery
虽然他说得很有道理,但如果他再多花一分钟的时间,他就会找到RNGCryptoServiceProvider,这样他的答案就会更有价值了。 - Samuel
4
提问者没有提到他需要安全的随机数,但你指出Random类不会产生安全随机数是很好的,但你真的应该提到一个替代方案,而不是留下死胡同。 - Samuel
显示剩余2条评论

2
您可以创建一个 byte 数组,填充随机数据,然后将其转换为 longInt64)或 ulongUInt64)。
byte[] buffer = new byte[sizeof(Int64)];
Random random = new Random();

random.NextBytes(buffer);
long signed = BitConverter.ToInt64(buffer, 0);

random.NextBytes(buffer);
long unsigned = BitConverter.ToUInt64(buffer, 0);

0

另一个使用 RNGCryptoServiceProvider 而不是 Random 的答案。在这里,您可以看到如何移除 MSB,使结果始终为正。

public static Int64 NextInt64()
{
    var buffer = new byte[8];
    new RNGCryptoServiceProvider().GetBytes(buffer);
    return BitConverter.ToInt64(buffer, 0) & 0x7FFFFFFFFFFFFFFF;
}

0
从 .NET 6 开始,Random 类有一个生成随机长整型的方法。
var r = new Random();
long randomLong = r.NextInt64();

-3
Random r=new Random();
int j=r.next(1,23);
Console.WriteLine(j);

随机类用于随机选择值。随机类为用户提供了良好的环境来这样做。 - ali

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接