简体   繁体   中英

How to generate 8 characters, 900000 Upper alpha/numeric unique keys with C#?

I'm trying to generate 900,000 keys and store them to a db.

Each value has to be 8 chars and different from generated ones.

Here is my code look like, it's doing ok but it's too slow...

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
    }

    private void button1_Click(object sender, EventArgs e)
    {
        richTextBox1.Visible = false;
        Generate();
    }

    public int num = 0;

    private void Den1()
    {
        for (int i = 0; i < 900000; i++)
        {
            string randomStr = RandomString(8);
            string textToSearchFor = randomStr;

            int index = richTextBox1.Text.IndexOf(textToSearchFor, StringComparison.OrdinalIgnoreCase);

            if (index >= 0)
            {
                MessageBox.Show("Duplicate ignored from 1!");
                i--;
            }
            else
            {
                richTextBox1.Text += randomStr + "\n";
                num++;
            }

            label1.Text = "" + num;
        }

        richTextBox1.Visible = true;
    }

    public void Generate()
    {
        CheckForIllegalCrossThreadCalls = false;
        Thread thread1 = new Thread(Deneme1);

        thread1.Start();
    }

    private string RandomString(int length, string allowedChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789")
    {
        if (length < 0) throw new ArgumentOutOfRangeException("length", "length cannot be less than zero.");
        if (string.IsNullOrEmpty(allowedChars)) throw new ArgumentException("allowedChars may not be empty.");

        const int byteSize = 0x100;
        var allowedCharSet = new HashSet<char>(allowedChars).ToArray();
        if (byteSize < allowedCharSet.Length) throw new ArgumentException(String.Format("allowedChars may contain no more than {0} characters.", byteSize));

        // Guid.NewGuid and System.Random are not particularly random. By using a
        // cryptographically-secure random number generator, the caller is always
        // protected, regardless of use.
        using (var rng = new System.Security.Cryptography.RNGCryptoServiceProvider())
        {
            var result = new StringBuilder();
            var buf = new byte[128];
            while (result.Length < length)
            {
                rng.GetBytes(buf);
                for (var i = 0; i < buf.Length && result.Length < length; ++i)
                {
                    // Divide the byte into allowedCharSet-sized groups. If the
                    // random value falls into the last group and the last group is
                    // too small to choose from the entire allowedCharSet, ignore
                    // the value in order to avoid biasing the result.
                    var outOfRangeStart = byteSize - (byteSize % allowedCharSet.Length);
                    if (outOfRangeStart <= buf[i]) continue;
                    result.Append(allowedCharSet[buf[i] % allowedCharSet.Length]);
                }
            }
            return result.ToString();
        }
    }
}

Well to start with, I would generate them all and then put them into the UI. I also wouldn't use string.IndexOf to find if you've already got a match. Use a HashSet<string> :

HashSet<string> codes = new HashSet<string>();
while (codes.Count < 900000)
{
    codes.Add(RandomString(8));  
}
// Then do what you want with the codes

You also shouldn't be setting CheckForIllegalCrossThreadCalls to false. Just generate them all in the background and then use Invoke to get to the UI thread to update your UI. If you really want to update as you go, you could do so periodically... although I wouldn't put them all into a single text box.

Next, I'd look at the RandomString method. Do you really need to create a new RNGCryptoServiceProvider on every iteration? Why not create one and then reuse it?

Likewise, why bother creating a new HashSet<char> and calling ToArray on every call ? It sounds like you should probably have a separate class as a RandomCodeGenerator which remembers:

  • The size of code you're generating
  • The random number generator
  • The allowed characters

That will let you get away from doing a lot of setup work on every iteration.

I'd then personally create a char[] of the right size and just iterate until you've filled it, then create a string from that... I don't see the need for StringBuilder ... but that's probably not hurting you.

You could use a Guid to quickly generate your keys:

Guid guid = Guid.NewGuid();

string theString = guid.ToString().Replace("-", string.Empty).Substring(0, 8).ToUpperInvariant();

You could do that directly in SQL, like: https://stackoverflow.com/a/9631898/403335

But even if you do that with code, I would fill a container with a sequence of all the possible values.

Then randomly select elements from the sequence container, use the element and delete it from the container. This way you will surely get all the numbers, without wasting time in finding a "hole" number (not already picked up).

In code:

    Random random = new Random();
    List<string> uniqueStrings = new List<string>(900000);
    List<string> randomString = new List<string>(900000);

    for (char c1 = 'A'; c1 <= 'Z'; ++c1) 
        for (char c2 = 'A'; c2 <= 'Z'; ++c2) 
            for (char c3 = 'A'; c3 <= 'Z'; ++c3) 
                for (char c4 = 'A'; c4 <= 'Z'; ++c4) 
                    for (char c5 = 'A'; c5 <= 'Z'; ++c5) 
                        for (char c6 = 'A'; c6 <= 'Z'; ++c6) 
                            for (char c7 = 'A'; c7 <= 'Z'; ++c7) 
                                for (char c8 = 'A'; c8 <= 'Z'; ++c8) 
                                    uniqueStrings.Add(string.Format("{0}{1}{2}{3}{4}{5}{6}{7}", c1, c2, c3, c4, c5, c6, c7, c8));

    for (int i = 0; i < uniqueStrings.Count; i++)
    {
        const int index = random.Next(uniqueStrings.Count) + 1;
        randomString.Add(uniqueStrings[index]);
        uniqueStrings.RemoveAt(index);
    }

I think this code may be helpful

public static string GeneratePassword()
{
    string strPwdchar = "abcdefghijklmnopqrstuvwxyz0123456789#+@&$ABCDEFGHIJKLMNOPQRSTUVWXYZ";
    string strPwd = "";
    Random rnd = new Random();
    for (int i = 0; i <= 7; i++)
    {
        int iRandom = rnd.Next(5, strPwdchar.Length - 1);
        strPwd += strPwdchar.Substring(iRandom, 1);
    }
    return strPwd;
}

Iterate it 90,000 times on a mouse click event.....I hope things will work

Have a look at this discussion.
My preferred solution, which I use for generating +100.000 unique strings is the linked answer

First of all RichTextBox is not an appropriate value store. The IndexOf method of the TextBox is most likely the slowest part of your code you need to replace it with something else, I would rather use a HashSet<string> to figure out of there are duplicates.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM