文章目录
- 问题
- 分析
- 代码
- 伪随机(使用Random类)
- 加密伪随机(使用RNGCryptoServiceProvider类)
- 实际使用案例
问题
使用
C#编写一个函数,给定的参数为最小值min,最大值max,忽略的结果(哈希集)iset和用于生成随机数的Random对象r,要求返回一个在最小值和最大值的范围内且不在给定哈希集中的一个整数re。
在 C# 编程中生成 随机数,我们一般都习惯于使用 Random.Next() 方法,这个方法可以返回一个在给定的两个 Int32 类型数值之间的随机 Int32 值。
但是,在某些情况下,Int32 的取值范围就会 不够使用 了,所以 System.Numerics.BigInteger 类应运而生。
于是,新的问题 出现了:Random 类并没有能与之配套的方法来生成 BigInteger 类型的随机数。
所以,经过了一段时间的思考,我决定给大家分享这个函数的实现。
希望大家能有所收获,如有错误之处望指正!
分析
使用 do...while 循环进行迭代,直到生成的值不在 iset 中或达到 最大迭代次数(10 000 000 次)。
- 在循环内部:
- 计算
零基准上限(zeroBasedUpperBound):max - min。 - 将
zeroBasedUpperBound转换为字节数组bytes。 - 通过
遍历最高有效字节的位来确定需要保留的位数,确保随机数不会超过指定的范围。lastByteMask用于此目的,它在循环中逐步调整以保留有效位。
- 在一个内部循环中,使用
Random或RNGCryptoServiceProvider生成与zeroBasedUpperBound相同长度的随机字节序列。 - 通过与
lastByteMask应用按位与(&)操作来确保最高有效字节中的随机数不会超过范围。 - 将这个字节序列转换成
BigInteger实例re。 - 检查
re是否大于zeroBasedUpperBound。- 如果是,则重新生成。
- 否则,将
min加到re上,使其落在正确的范围内。
- 如果生成的随机数
re已经在iset中,或者尝试次数i达到10 000 000次,则继续尝试。
- 计算
代码
由于需要使用 HashSet<BigInteger> 作为函数参数,所以需要引入 System.Linq 命名空间和 System.Numerics 命名空间:
using System.Linq;
using System.Numerics;
伪随机(使用Random类)
引入 System 命名空间以使用 Random 类:
using System;
函数实现:
/// <summary>
/// Generates a random <see cref="BigInteger"/> value within the specified range, excluding the numbers in the given HashSet.
/// </summary>
/// <remarks>
/// This method generates a random <see cref="BigInteger"/> value within the range specified by min and max, while ensuring that the generated number is not present in the provided HashSet. <br/>
/// If a suitable number cannot be found within 10,000,000 iterations, a <see cref="NotImplementedException"/> is thrown.
/// </remarks>
/// <param name="min">The minimum value of the range.</param>
/// <param name="max">The maximum value of the range.</param>
/// <param name="iset">The HashSet containing the numbers to be excluded.</param>
/// <param name="r">The Random object used for generating random numbers.</param>
/// <returns>A random <see cref="BigInteger"/> value within the specified range, excluding the numbers in the HashSet.</returns>
/// <exception cref="NotImplementedException">Thrown when the maximum number of iterations is reached without finding a suitable number.</exception>
private BigInteger Generate(BigInteger min, BigInteger max, HashSet<BigInteger> iset, Random r)
{
int i = 0;
BigInteger re;
do
{
BigInteger zeroBasedUpperBound = max - min;
byte[] bytes = zeroBasedUpperBound.ToByteArray();
byte lastByteMask = 0b11111111;
for (byte mask = 0b10000000; mask > 0; mask >>= 1, lastByteMask >>= 1) { if ((bytes[bytes.Length - 1] & mask) == mask) break; } // We found it.
do
{
r.NextBytes(bytes);
bytes[bytes.Length - 1] &= lastByteMask;
re = new BigInteger(bytes);
}
while (re > zeroBasedUpperBound);
re += min;
i++;
}
while (iset.Contains(re) && i <= 10000000);
if (i == 10000001) throw new NotImplementedException();
else return re;
}
加密伪随机(使用RNGCryptoServiceProvider类)
使用时请一定要记得引入 System.Security.Cryptography 命名空间:
using System.Security.Cryptography;
函数实现:
/// <summary>
/// Generates a random <see cref="BigInteger"/> value within the specified range, excluding the numbers in the given HashSet.
/// </summary>
/// <remarks>
/// This method generates a random <see cref="BigInteger"/> value within the range specified by min and max, while ensuring that the generated number is not present in the provided HashSet. <br/>
/// If a suitable number cannot be found within 10,000,000 iterations, a <see cref="NotImplementedException"/> is thrown.
/// </remarks>
/// <param name="min">The minimum value of the range.</param>
/// <param name="max">The maximum value of the range.</param>
/// <param name="iset">The HashSet containing the numbers to be excluded.</param>
/// <param name="r">The RNGCryptoServiceProvider object used for generating random numbers.</param>
/// <returns>A random <see cref="BigInteger"/> value within the specified range, excluding the numbers in the HashSet.</returns>
/// <exception cref="NotImplementedException">Thrown when the maximum number of iterations is reached without finding a suitable number.</exception>
private BigInteger Generate(BigInteger min, BigInteger max, HashSet<BigInteger> iset, RNGCryptoServiceProvider r)
{
int i = 0;
BigInteger re;
do
{
BigInteger zeroBasedUpperBound = max - min;
byte[] bytes = zeroBasedUpperBound.ToByteArray();
byte lastByteMask = 0b11111111;
for (byte mask = 0b10000000; mask > 0; mask >>= 1, lastByteMask >>= 1) { if ((bytes[bytes.Length - 1] & mask) == mask) break; } // We found it.
do
{
r.GetBytes(bytes);
bytes[bytes.Length - 1] &= lastByteMask;
re = new BigInteger(bytes);
}
while (re > zeroBasedUpperBound);
re += min;
i++;
}
while (iset.Contains(re) && i <= 10000000);
if (i == 10000001) throw new NotImplementedException();
else return re;
}
实际使用案例
我的一个小项目:Lottery.MainWindow (GitHub)
代码遵守Apache 2.0软件开源许可证,大家放心使用!
创作不易,如果这篇博客帮到了您,还请点赞收藏,Thanks!
本文详细介绍了如何在C#中使用Random类和RNGCryptoServiceProvider类生成不在给定哈希集合内的BigInteger范围内的随机数,通过do...while循环和位操作确保随机性和排除特定值。
575

被折叠的 条评论
为什么被折叠?



