简体   繁体   中英

How can I write a method that returns an unique string of 6 characters length?

If I do it like this I get some duplicates...

private const string _chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ123654987";

public string RandomString(int size)
{
    var random = new Random((int)DateTime.Now.Ticks);
    Thread.Sleep(random.Next(1, 3));

    var buffer = new char[size];

    for (int i = 0; i < size; i++)
    {
        buffer[i] = _chars[random.Next(_chars.Length)];
    }
    return new string(buffer);
}

The first 100000 request must be unique how can I guarantee it basically... if possible I do not want to keep a list and query it...

  • Figure out the largest prime that's less than 35^6 (the number of possible combinations).
  • Choose a random number that's less than that but greater than one.
  • Take (your prime % (your random number * iteration index)). This is what you base your string from.
  • Express the result in base 35, and create your string.

These strings won't overlap over 100,000 iterations because your random number is relatively prime to the larger number. No caching needed.

You can run through a random number of iterations before recording the strings to give yourself a larger result space.

If your possible length of random string is limited it would be easiest to use GUIDs:

A crude implementation might be:

    public string RandomString(int size)
    {
        return Guid.NewGuid()
            .ToString()
            .Replace("-","")
            .Substring(0, size);
    }

If you need longer then you could concatenate multiple GUID strings together.

Don't keep a list of all earlier values. Just use a counter.

If you want to make the value less predictable (guessable) to a user, use a hash function to scramble the bits before using them. But still generate the next input to the hash from a simple counter.

If you used an int to represent bit positions, you could do it easily.

int bits=0

...

while(bitCount(bits)!=6) // Write your own bitCount method--or there is probably one on the net
    bits++;

Now you know you have 6 bits in your int, so convert them to the string

For instance with your data:

"ABCDEFGHIJKLMNOPQRSTUVWXYZ123654987"

if you were counting and had reached 111111, (The first one you will hit) you'd return "234567", the next one I believe will be 1011111 which will return "134567" then "1101111" which will return "124567". (I could be getting the sequence wrong, it's just off the top of my head).

It will always be unique, and the iterating isn't as expensive as you'd think, although you could probably be a bit smarter than just iterating (you could skip large groups if you figured out it was mathematically impossible to reach the next 6-"on" bit number before n increments, or you could just come up with a more straight-forward algorithm to generate the next integer with 6 1's.

You may be able to use current time stamp (milliseconds, microseconds or nannoseconds) (reverse it or change the digit order if you need to show a randomness) and replace the digits in the timestamp number with AZ and 0-9 characters on some criteria.

Otherwise If you don't have any caching mechanism store previously generated values, I think you cannot get random and UNIQUE character sequences.

If they don't need to be random but only unique then this code works using an odometer type of output.

public class Class1
{
    List<char> _chars = new List<char>() { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N',
        'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '1', '2','3','4', '5', '6', '7', '8', '9', '0' };
    private static int[] index = new int[6] {0, 0, 0, 0, 0, 0};
    private const int charMax = 35;

    public string UniqueString()
    {
        if (index[5] > charMax)
        {
            IncromentParent(5);
        }

        StringBuilder result = new StringBuilder();
        result.Append(_chars[index[0]]);
        result.Append(_chars[index[1]]);
        result.Append(_chars[index[2]]);
        result.Append(_chars[index[3]]);
        result.Append(_chars[index[4]]);
        result.Append(_chars[index[5]]);

        index[5]++;
        return result.ToString();
    }

    private void IncromentParent(int active)
    {
        if (active == 0)
            throw new Exception("out of numbers");

        int parent = active - 1;
        index[active] = 0;
        index[parent]++;
        if (index[parent] > charMax)
            IncromentParent(parent);
    } 
}

And here is a passing unit test, but it takes a long time to run...

[TestMethod]
public void MyTestMethod()
{
    Class1 target = new Class1();
    List<string> results = new List<string>();

    for (int i = 0; i < 100000; i++)
    {            
        string result = target.UniqueString();

        if (!results.Contains(result))
            results.Add(result);
        else
            Assert.Fail(string.Format("The string '{0}' is already in the list", result));
    }
   Console.WriteLine(results.Count.ToString());
}

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