文章目录
- 问题
- 分析
- 代码
- 伪随机(使用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!