封装C# BigInteger随机数函数

文章目录

  • 问题
  • 分析
  • 代码
    • 伪随机(使用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 用于此目的,它在循环中逐步调整以 保留有效位
    • 在一个内部循环中,使用 RandomRNGCryptoServiceProvider 生成与 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!

  • 13
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值