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