随机数发生器 - 可变长度

时间:2012-01-04 16:45:51

标签: c# math random numbers integer

目前我的程序(见下文)生成长度为8-12个字符的随机字符串(由数字组成)。

public static string GenerateNewCode(int CodeLength)
{
    string newCode = String.Empty;
    int seed = unchecked(DateTime.Now.Ticks.GetHashCode());
    Random random = new Random(seed);

    // keep going until we find a unique code       `
    do
    {
        newCode = random.Next(Convert.ToInt32(Math.Pow(10, CodeLength - 1)), Convert.ToInt32(Math.Pow(10, CodeLength) - 1)).ToString("0000");
    }
    while (!ConsumerCode.isUnique(newCode));

    // return
    return newCode;
}

然而,它们是该方法的问题,当codeLength为10或更大时,它会导致错误,因为10 9 大于int32.MaxValue

不确定如何解决这个问题。

5 个答案:

答案 0 :(得分:17)

不是生成0到10 n -1的数字,然后将其转换为字符串,而是生成0到9之间的n个数字,将每个数字转换为字符串,并将它们连接在一起。 / p>

我注意到这种技术产生的范围内的数字(比如n = 4)0到9999;您的原始版本产生的数字从1000到9999.如果您想要保留该属性,您可以生成从1到9而不是0到9的第一个数字。

  

当然,我这样做的方式更随机,然后'n'连接0 - 9值。

请解释你为什么这么认为。我很着迷于了解为什么人们会相信谎言。

  

你能否创建一个Guid.NewGuid()。Tostring()并截断最重要的字符以获得一个长度为N的随机字符串?

可以,但你不应该。 GUID不保证是随机的,并且GUID的任何比特子集都不保证是唯一的。从GUID中取出位并期望这些位具有GUID的属性就像将舵从飞机上取下并期望方向舵飞行一样。从来没有这样做过。

相反,请将工具用于其设计目的。 GUID旨在提供全局唯一标识符,因此无需使用它们。

你没有提出但可能应该提出的问题:

  

我的代码还有什么问题或可疑之处?

int seed = unchecked(DateTime.Now.Ticks.GetHashCode()); 

该代码究竟在做什么?首先,随机类已经将当前时间用作种子;这是不必要的。第二,究竟什么是“未经检查的算术”表达式不包含算术的目的?第三,为什么你会得到哈希码?你没有平衡哈希表!

Random random = new Random(seed);  

如果在同一个tick中调用此方法两次,那么种子将是相同的,因此随机数的序列将是相同的,这意味着您可能会等待很长时间,因为每个生成的数字将是在你的'独特'集中碰撞。

更好的技术是创建一个随机的静态实例,一次播种。如果您的程序是单线程的,那就没问题了。如果它是多线程的,请确保您不从多个线程访问Random;它不是线程安全的,它的线程安全违规失败模式不好。

   newCode = random.Next(Convert.ToInt32(Math.Pow(10, CodeLength - 1)),                         Convert.ToInt32(Math.Pow(10, CodeLength) - 

每次循环时是否真的有必要计算两次电源,每次都完全相同?在循环开始之前解决一次。代码将更短,更清晰,更快。

while (!ConsumerCode.isUnique(newCode));    

如果集合已满,则会永久循环。如果集合几乎已满,则会循环 long 时间。这是一种用于在范围内生成唯一随机数的潜在不良技术。只有在您提前知道可能生成的数字远远大于集合的最大大小时才这样做。

  

那么你的方法对于这个功能会是什么样的呢?

我倾向于做这样的事情。首先,我想要无穷无尽的数字:

// Yield an infinite sequence of pseudo-random digits 0-9
// This method is not thread-safe.
private static Random random = new Random();
static IEnumerable<int> Digits()
{
    while(true)
        yield return random.Next(0, 10);
}

现在我可以生成一个唯一的数字字符串:

// Generates a random code of given length. If it is not
// in the set, adds it and returns the code. If it is
// already in the set, tries again. 
static string AddUniqueCode(int length, HashSet<string> set)
{
    while(true)
    {
        string code = string.Join(null, Digits().Take(length));
        if (set.Add(code))
            return code;
    }
}    

我喜欢我的方法。

如果不可预测性对您很重要,您还可以使Digits方法使用加密强度随机性而不是伪随机性。伪随机数很容易预测。

答案 1 :(得分:4)

你的代码做了同样的事情而没有一些怪胎:

public static string GenerateNewCode(int CodeLength)
{
    Random random = new Random();
    StringBuilder output = new StringBuilder();

    do
    {   
        for (int i = 0; i < CodeLength; i++)
        {
            output.Append(random.Next(0, 10));
        }
    }
    while (!ConsumerCode.isUnique(output.ToString()));
    return output.ToString();
}

答案 2 :(得分:3)

生成0到9之间的随机数;添加'0';把它扔给一个炭;现在你有一个随机数字。根据需要重复多个数字。

像这样:char c = (char)(random.Next(10) + '0');

答案 3 :(得分:0)

一种方法可以使用Random.NextDouble()返回0..1之间的数字,然后将其缩放到0..26并使用查找表根据索引返回字母字符。例如:

char lookup = new char[] { 'a', 'b', 'c' ... 'z' }; // ensure length 26
int index = (int)(random.NextDouble() * 25.0);
return lookup[index]

更有效的方法是使用ascii table将整数转换为所需的字母数字字符。

致以最诚挚的问候,

答案 4 :(得分:0)

您可以创建两个随机数,其中第一个随机数的长度为6-10,第二个随机数的长度为2.生成两个数后,将它们组合成一个长度为8-12。

示例:

public static string GenerateNewCode(int CodeLength)
{
string newCode = String.Empty;
int seed = unchecked(DateTime.Now.Ticks.GetHashCode());
Random random = new Random(seed);

// keep going until we find a unique code       `
do
{   
// The firstPart int will be a random number that has a length of 6, 7, 8, ,9, or 10 digits
int firstPart = random.Next(100000,2147483647);

// The secondPart int will be a random number that has a length of two digits
int secondPart = random.Next(10,99);        

// Concatenate firstPart and secondPart. This will create a string that has a length of 8, 9, 10, 11, or 12 chars.
newCode = firstPart.ToString() + secondPart.ToString();

}
while (!ConsumerCode.isUnique(newCode));

// return
return newCode;
}