C# RNGCryptoServiceProvider-如何在不考虑字符长度的情况下获得完全无偏的结果?

C# RNGCryptoServiceProvider-如何在不考虑字符长度的情况下获得完全无偏的结果?,c#,C#,下面的方法是使用RNGCryptoServiceProvider返回一个随机字符字符串。返回字符串result是通过应用%chars.length从字符串chars中选取字符来构建的 关于GetBytes()返回的字节数组中的字节值(0-255)。这意味着某些字符可能比其他字符更受欢迎,具体取决于字符的长度 如何重新编写该方法,使chars中的所有字符都有相同的被拾取机会 /// <summary> /// Returns a string of cryptographically

下面的方法是使用
RNGCryptoServiceProvider
返回一个随机字符字符串。返回字符串
result
是通过应用
%chars.length
从字符串
chars
中选取字符来构建的 关于
GetBytes()
返回的字节数组中的字节值(0-255)。这意味着某些字符可能比其他字符更受欢迎,具体取决于
字符的长度

如何重新编写该方法,使
chars
中的所有字符都有相同的被拾取机会

/// <summary>
/// Returns a string of cryptographically sound random characters
/// </summary>
/// <param name="type">Accepted parameter variables are HEX (0-F), hex (0-f),
/// DEC/dec/NUM/num (0-9), ALPHA (A-Z), alpha (a-z), ALPHANUM (A-Z and 0-9),
/// alphanum (a-z and 0-9) and FULL/full (A-Z, a-z and 0-9)</param>
/// <param name="length">The length of the output string</param>
/// <returns>String of cryptographically sound random characters</returns>
private static string Serial(string type, int length)
{
    if (length < 1) return "";
    string chars;
    switch (type)
    {
        case "HEX":
            chars = "0123456789ABCDEF";
            break;
        case "hex":
            chars = "0123456789abcdef";
            break;
        case "DEC":
        case "dec":
        case "NUM":
        case "num":
            chars = "0123456789";
            break;
        case "ALPHA":
            chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
            break;
        case "alpha":
            chars = "abcdefghijklmnopqrstuvwxyz";
            break;
        case "ALPHANUM":
            chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
            break;
        case "alphanum":
            chars = "abcdefghijklmnopqrstuvwxyz0123456789";
            break;
        case "FULL":
        case "full":
            chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
            break;
        default:
            chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
            break;
    }
    byte[] data = new byte[length];
    using (RNGCryptoServiceProvider crypto = new RNGCryptoServiceProvider())
    {
        crypto.GetBytes(data);
    }
    StringBuilder result = new StringBuilder(length);
    foreach (byte b in data)
    {
        result.Append(chars[b % chars.Length]);
    }
    return result.ToString();
}
//
///返回以加密方式发音的随机字符字符串
/// 
///接受的参数变量为十六进制(0-F)、十六进制(0-F),
///12/12/NUM/NUM(0-9),ALPHA(A-Z),ALPHA(A-Z),ALPHANUM(A-Z和0-9),
///alphanum(a-z和0-9)和FULL/FULL(a-z、a-z和0-9)
///输出字符串的长度
///加密声音随机字符串
专用静态字符串序列(字符串类型,整数长度)
{
如果(长度<1)返回“”;
串字符;
开关(类型)
{
案例“十六进制”:
chars=“0123456789ABCDEF”;
打破
案例“十六进制”:
chars=“0123456789abcdef”;
打破
案例“DEC”:
案例“dec”:
案例“NUM”:
案例“num”:
chars=“0123456789”;
打破
案例“ALPHA”:
chars=“abcdefghijklmnopqrstuvxyz”;
打破
案例“alpha”:
chars=“abcdefghijklmnopqrstuvxyz”;
打破
案例“ALPHANUM”:
chars=“abcdefghijklmnopqrstuvxyz012456789”;
打破
案例“alphanum”:
chars=“abcdefghijklmnopqrstuvxyz012456789”;
打破
案例“完整”:
案例“完整”:
chars=“abcdefghijklmnopqrstuvxyzabefghijklmnopqrstuvxyzo123456789”;
打破
违约:
chars=“abcdefghijklmnopqrstuvxyzabefghijklmnopqrstuvxyzo123456789”;
打破
}
字节[]数据=新字节[长度];
使用(RNGCryptoServiceProvider crypto=new RNGCryptoServiceProvider())
{
加密.GetBytes(数据);
}
StringBuilder结果=新StringBuilder(长度);
foreach(数据中的字节b)
{
追加(字符[b%字符长度]);
}
返回result.ToString();
}
似乎是最实用的完美无偏方法

private const int blockSize = 1024;
private readonly RNGCryptoServiceProvider crypto = new RNGCryptoServiceProvider();

private static IEnumerable<byte> GetCryptoStream()
{
    var data = new byte[blockSize];
    while (true)
    {
        crypto.GetBytes(data);
        foreach (var b in data) yield return b;
    }
}

private static IEnumerable<int> GetCryptoStream(int radix)
{
    if (radix <= 0 || radix >= 256) throw new ArgumentException("radix should be > 0 and < 256");

    var rem = 256 % radix;
    foreach (var b in GetCryptoStream())
    {
        if (b < rem) continue;      // rejection sampling
        yield return b % radix;
    }
}

private static string Serial(string type, int length)
{
    if (length < 1) return "";
    string chars;
    switch (type)
    {
        // ...
    }

    var result = new StringBuilder(length);
    foreach (var k in GetCryptoStream(chars.Length).Take(length))
    {
        result.Append(chars[k]);
    }

    return result.ToString();
}
请看这里:
[TestCase(3)]
[TestCase(6)]
[TestCase(5)]
[TestCase(75)]
public void TestCrypto(int n)
{
    const int m = 1000 * 1000 * 100;
    var count = new int[n];
    foreach (var k in GetCryptoStream(n).Take(m))
    {
        count[k] ++;
    }

    Console.WriteLine($"Expected : {1.0 / n:F4}, Actual :");
    for (int i = 0; i < count.Length; i++)
    {
        Console.WriteLine($"{i} : {count[i] / (double) m:F4}");
    }
}
Expected : 0.1667, Actual :
0 : 0.1667
1 : 0.1667
2 : 0.1667
3 : 0.1667
4 : 0.1667
5 : 0.1666