随机字符串生成器返回相同的字符串

230

我已经开发了一个随机字符串生成器,但它的表现并不完全如我所希望。我的目标是能够运行两次并生成两个不同的四个字符随机字符串。然而,它只生成了一个四个字符的随机字符串两次。

以下是代码和输出示例:

private string RandomString(int size)
{
    StringBuilder builder = new StringBuilder();
    Random random = new Random();
    char ch;
    for (int i = 0; i < size; i++)
    {
        ch = Convert.ToChar(Convert.ToInt32(Math.Floor(26 * random.NextDouble() + 65)));                 
        builder.Append(ch);
    }

    return builder.ToString();
}

// get 1st random string 
string Rand1 = RandomString(4);

// get 2nd random string 
string Rand2 = RandomString(4);

// create full rand string
string docNum = Rand1 + "-" + Rand2;

...输出结果看起来像这样: UNTE-UNTE ...但应该类似于这个样子 UNTE-FWNU

我如何确保两个截然不同的随机字符串?


良好的性能 - mola10
3
请注意,即使是两个完全随机的字符串也不能保证唯一。对于长字符串(120+位),它们非常可能是唯一的,但对于像这样的短字符串,碰撞是很常见的。 - CodesInChaos
虽然这是一个旧的线程,但如果适用的话,你可以生成一个GUID并将其转换为文本。 - user3657408
30个回答

4
这是因为每个新的Random实例生成的数字都很相似,因为它们被快速调用。不要反复创建新实例,只需调用next()并在方法外声明您的随机类。

3

你应该在构造函数中初始化一个类级别的Random对象,并在每次调用时重复使用它(这将继续使用相同的伪随机数序列)。无参构造函数已经在内部使用Environment.TickCount种子生成器。


2

我添加了选择长度选项,使用了Ranvir的解决方案。

public static string GenerateRandomString(int length)
    {
        {
            string randomString= string.Empty;

            while (randomString.Length <= length)
            {
                randomString+= Path.GetRandomFileName();
                randomString= randomString.Replace(".", string.Empty);
            }

            return randomString.Substring(0, length);
        }
    }

2

这里是我对当前被接受的答案进行修改后的结果,我认为它更快更简短:

private static Random random = new Random();

private string RandomString(int size) {
    StringBuilder builder = new StringBuilder(size);
    for (int i = 0; i < size; i++)
        builder.Append((char)random.Next(0x41, 0x5A));
    return builder.ToString();
}

请注意,我没有使用全部的乘法、Math.floor()Convert等操作。

编辑:random.Next(0x41, 0x5A) 可以更改为任何 Unicode 字符范围。


嘘...你实际上没有回答OP所遇到的问题... - Andrew Barber
@Andrew 这实际上解决了问题,请注意这也是我对已接受答案进行的优化。 - quantum
你的代码中包含了这个,而你的描述中并没有提到。至于它是否为优化,我想在同意这种说法之前要看一些基准测试;有一些你(显然)不知道的事情正在发生,比如隐式转换。此外,你还错过了可能是最大的性能提升——指定 StringBuilder 的起始大小。(仅代表个人意见) - Andrew Barber
@Andrew 我将修改我的答案以反映这一点,但是使用.NET提供的方法生成一个均匀分布的值,而不是涉及乘法和floor操作的自定义方法,速度应该更快。 - quantum

2

如果您想生成一个由数字和字符组成的强密码字符串。

private static Random random = new Random();

private static string CreateTempPass(int size)
        {
            var pass = new StringBuilder();
            for (var i=0; i < size; i++)
            {
                var binary = random.Next(0,2);
                switch (binary)
                {
                    case 0:
                    var ch = (Convert.ToChar(Convert.ToInt32(Math.Floor(26*random.NextDouble() + 65))));
                        pass.Append(ch);
                        break;
                    case 1:
                        var num = random.Next(1, 10);
                        pass.Append(num);
                        break;
                }
            }
            return pass.ToString();
        }

请注意,Random类的实例成员未记录为线程安全。因此,如果从多个线程同时调用此方法(例如制作Web应用程序),则此代码的行为将是未定义的。您需要在随机数上使用锁定或使其每个线程独立。 - Greg Beech
1
@GregBeech 真的吗?又来了?无聊吗? - Jimmy D

2

我认为这也是可行而简单的。

Guid.NewGuid().ToString() 

GUID不保证是随机的,因此您不应将它们滥用为PRNG。目前在Windows上的实现是随机的,但在旧版上它基于MAC地址和时间。谁知道未来版本会使用什么。 - CodesInChaos
我不这么认为。现在,许多人使用GUID作为主键,是的,GUID可能会冲突,但 2^128 写出来大约是: 34,028,236,692,093,846,346,337,460,743,177,000,000。统计学上来说,如果你每秒计算1000个GUID,仍需要数万亿年才能得到重复的结果。顺便说一下,使用GUID非常简单,我认为这不是一个坏主意。 - wener
GUID的意义在于它是唯一的,而不是随机的。如果您将其用作唯一标识符,我不会抱怨,但您将其销售为随机字符串,目前确实是这样,但不能保证永远如此。(由于生日悖论,您只需要大约2^61个值即可达到重复,而不是2^122,因此您所说的“数万亿年”实际上只有大约7500万年) - CodesInChaos
这可以生成随机字符串,仅此而已。虽然它无法生成相同的序列。 - wener
在当前的实现中,生成的字符串大部分是随机的(其中一些部分不是随机的)。不能保证它是随机的,过去的实现也不是随机的,未来的实现也可能不是随机的。 - CodesInChaos

2

我的RandomString()方法用于生成随机字符串。

private static readonly Random _rand = new Random();

/// <summary>
/// Generate a random string.
/// </summary>
/// <param name="length">The length of random string. The minimum length is 3.</param>
/// <returns>The random string.</returns>
public string RandomString(int length)
{
    length = Math.Max(length, 3);

    byte[] bytes = new byte[length];
    _rand.NextBytes(bytes);
    return Convert.ToBase64String(bytes).Substring(0, length);
}

1

结合“Pushcode”的答案和使用随机生成器种子的答案,我需要创建一系列伪可读的“单词”。

private int RandomNumber(int min, int max, int seed=0)
{
    Random random = new Random((int)DateTime.Now.Ticks + seed);
    return random.Next(min, max);
}

据我所知,C#没有默认参数。 - quantum
@xiaomao,你说错了。我一直都在用它们。顺便提一下,它们被称为“可选参数”。 - Andrew Barber
@Andrew 好的,这是一个新的补充,来自 MSDN:“Visual C# 2010 引入了命名和可选参数”。 - quantum
2
@xiaomao,我不会把超过3年的历史和一个之前的完整版本称为“新添加”的功能。在去年9月发布这个答案时,它已经是一个成熟的特性了。 - Andrew Barber

1

这里有另一个基于GUID的想法。我已经在Visual Studio性能测试中使用它来生成仅包含字母数字字符的随机字符串。

public string GenerateRandomString(int stringLength)
{
    Random rnd = new Random();
    Guid guid;
    String randomString = string.Empty;

    int numberOfGuidsRequired = (int)Math.Ceiling((double)stringLength / 32d);
    for (int i = 0; i < numberOfGuidsRequired; i++)
    {
        guid = Guid.NewGuid();
        randomString += guid.ToString().Replace("-", "");
    }

    return randomString.Substring(0, stringLength);
}

1

我创建了这个方法。

它运行得非常好。

public static string GeneratePassword(int Lenght, int NonAlphaNumericChars)
    {
        string allowedChars = "abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNOPQRSTUVWXYZ0123456789";
        string allowedNonAlphaNum = "!@#$%^&*()_-+=[{]};:<>|./?";
        Random rd = new Random();

        if (NonAlphaNumericChars > Lenght || Lenght <= 0 || NonAlphaNumericChars < 0)
            throw new ArgumentOutOfRangeException();

            char[] pass = new char[Lenght];
            int[] pos = new int[Lenght];
            int i = 0, j = 0, temp = 0;
            bool flag = false;

            //Random the position values of the pos array for the string Pass
            while (i < Lenght - 1)
            {
                j = 0;
                flag = false;
                temp = rd.Next(0, Lenght);
                for (j = 0; j < Lenght; j++)
                    if (temp == pos[j])
                    {
                        flag = true;
                        j = Lenght;
                    }

                if (!flag)
                {
                    pos[i] = temp;
                    i++;
                }
            }

            //Random the AlphaNumericChars
            for (i = 0; i < Lenght - NonAlphaNumericChars; i++)
                pass[i] = allowedChars[rd.Next(0, allowedChars.Length)];

            //Random the NonAlphaNumericChars
            for (i = Lenght - NonAlphaNumericChars; i < Lenght; i++)
                pass[i] = allowedNonAlphaNum[rd.Next(0, allowedNonAlphaNum.Length)];

            //Set the sorted array values by the pos array for the rigth posistion
            char[] sorted = new char[Lenght];
            for (i = 0; i < Lenght; i++)
                sorted[i] = pass[pos[i]];

            string Pass = new String(sorted);

            return Pass;
    }

再次表明安全测试很难。虽然它“运行良好”,但并不安全。使用安全 PRNG 生成密码。 - CodesInChaos

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接