19

I am using this code to generate a 8 digit unique number.

byte[] buffer = Guid.NewGuid().ToByteArray();
return BitConverter.ToUInt32(buffer, 8).ToString();

Does this code really generate a unique number or might it repeat the same number again?

tshepang
  • 12,111
  • 21
  • 91
  • 136
dev
  • 898
  • 1
  • 13
  • 33
  • 2
    You might need to discuss your requirements further; why won't `static counter=0; counter++; sprintf(buffer,"%8d", counter);` kind of code do the job? Does it have to be generated from a GUID? Should there be any kind of distribution of numbers? – sarnold May 23 '11 at 22:13
  • it's not necessary to use GUID. Only reason i use GUID becasue i have find some good articale about GUID and that's why i use GUID.But if you have any other way to generate unique number please let me know. – dev May 23 '11 at 22:18
  • 2
    A GUID is not unique, it's just very very unlikely to produce two identical sequences. By reducing the GUID to an 8-digit number you significantly increase the chance of a repeat. Please further explain your need for a unique number; if you want a random number you should not use GUID, and if you want a primary key value you should probably just increase a counter to make sure you use the maximum range of numbers available before having to repeat yourself (which you will know because it will throw an OverflowException). – SirViver May 23 '11 at 22:21
  • 1
    None of the answers thus far seem to address the uniqueness requirement of the question. – Will A May 23 '11 at 22:30
  • @Will A, mostly because it seems like a bad way to solve this particular problem. It might or might not be unique for a sequence of certain length, but is it really worth the trouble to find out? – Can Gencer May 23 '11 at 22:35
  • @WillA the answer is: no, it will not generate a unique number. It has been pointed out that for a unique number (as far as the 8-digit range allows) a simple counter is probably the best choice. Unless of course the OP requires a random-looking number to prevent guessing the next in sequence, but he hasn't stated that. – SirViver May 23 '11 at 22:35
  • @Can - could be the case, but perhaps uniqueness is required for e.g. a product key or some form of PIN number? – Will A May 23 '11 at 22:37
  • @Will A, yes but in that case it's better to use a strong RNG instead of byte trickery using GUIDs in my opinion. Unless you need to derive the said number from a GUID.. – Can Gencer May 23 '11 at 22:38
  • @Can - totally agree re: GUIDs, globally unique, yes - secure, definitely not - and picking one apart is just asking for trouble. – Will A May 23 '11 at 22:40
  • 1
    using http://jeff.aaron.ca/cgi-bin/birthday, you get 40% chance of a collision with only 10,000 entries in a number space of 8 digits. But still, we don't know what the intention for the digits are. – Can Gencer May 23 '11 at 22:43
  • This code doesn't generate a unique *or* eight digit number. You can certainly get a collision on these – Jake T. May 23 '11 at 22:44
  • One question, you stated that you didn't want to repeat the same number. My question is is this requirement only during the operation of the application or do you wish this feature to persist between different sessions? – Mark Kram May 23 '11 at 23:10
  • There are 2 good answers already. Just increment a number from X to Y. If you want the appearance of randomness, just deterministically shuffle the bits of each generated value. – Kevin Hsu May 23 '11 at 23:15
  • `(for var everything in world){ where(anything == everything && nothing == everything){ nothing = RaNDoM; nothing = UNIQUE; } }` **Programming is 99.9999999999% implementation and 0.00000000001% Code.** I don't see the problem. _Yes, yes your code generates an "unique" number, you just didn't check to ensure that there is no fluke duplicate._ It shouldn't be called GUID, should be GRID "lol". For small solutions just compare a "GUID" to a list of previous "GUID's" and loop that exits if "unique". For larger solutions definitely go the incremental route. – LokizFenrir Jan 07 '15 at 08:05
  • I needed to generate a unique key with no additional information. DateTime.Now doesn't work. I did not want to create a look-up of used values. Although I did. I persisted it as a 64K hex 'string' giving 512K bits. Looped through a random number based on 512K indexing into the bit string till I found a zero and then flipped it. The only problem was that the 64K had to be 'saved' after every generation. And of course the max number of entries was 500,000 or so. It was quicker than going to a database to get a unique incremented value and it didn't gave away information about the sequence. – Paulustrious Apr 06 '17 at 12:02

9 Answers9

11

Any random sequence is bound to have some collisions. It's just a matter of when. Using the birthday paradox formula, with 100,000,000 possible values (8 digits), the chance that you will have a collision with only 10,000 elements is around 40% and 99% with 30,000 elements. (see here for a calculator).

If you really need a random sequence, you should not use a GUID for this purpose. GUIDs have very specific structure and should only be taken as a whole. It is easy enough to create a random 8 digit sequence generator. This should give you an 8 digit sequence:

 public string Get8Digits()
 {
   var bytes = new byte[4];
   var rng = RandomNumberGenerator.Create();
   rng.GetBytes(bytes);
   uint random = BitConverter.ToUInt32(bytes, 0) % 100000000;
   return String.Format("{0:D8}", random);
 }

You can also take the RandomNumberGenerator and place it somewhere to avoid creating a new one everytime.

Can Gencer
  • 8,822
  • 5
  • 33
  • 52
11

A GUID is not just a random number; it's composed of segments. Some of the segments will not change at all if the guid is generated on the same computer. By using only 64-bits of the original 128-bits you are breaking the structure of the guid and most likely breaking the uniqueness of the generated number.

This question has more info on uniqueness of guids, check this link as well for more info on why it's bad to use only part of a guid if you need a unique number.

If you need to limit duplication to an absolute minimum, an incremental counter will give you what you need. If your application is using multiple threads or processes, a counter may be hard (or even impossible) to implement correctly.

This is the realm that guids were designed for, to be unique across multiple machines. So if uniqueness across machines is a requirement you should use guids. The whole guid.

Community
  • 1
  • 1
Marnix van Valen
  • 13,265
  • 4
  • 47
  • 74
  • You'll hit the same random number again sooner than just counting from 0 to 99999999. – Vladislav Zorov May 23 '11 at 22:40
  • 1
    That's not exactly right. Version 4 GUIDs (which are generated by `Guid.NewGuid()`) [have exactly 6 bits fixed](http://en.wikipedia.org/wiki/Universally_Unique_Identifier#Version_4_.28random.29), the rest is pseudo-random. The old version of GUIDs behaved the way you describe. – svick May 23 '11 at 22:44
  • I won't vote this down, but a random number generator will still yield duplicates. Either you need a function that guarantees non-duplicate enumeration of all values, or you need memory. – Kevin Hsu May 23 '11 at 23:12
5

Here is another version

public static string GetFormNumber()
    {
        byte[] buffer = Guid.NewGuid().ToByteArray();
        var FormNumber = BitConverter.ToUInt32(buffer, 0) ^ BitConverter.ToUInt32(buffer, 4) ^ BitConverter.ToUInt32(buffer, 8) ^ BitConverter.ToUInt32(buffer, 12);
        return FormNumber.ToString("X");

    }

it assures to be unique!

Kamran Qadir
  • 466
  • 10
  • 20
  • 7
    Generated it for 1000000 records using linqpad and got 119 duplicates. Generated it a few more times for the same number of records and the duplicate count is often with 110 and 200. Still impressive though. If only there was a way to remove the duplicates without the need to compare with the previously saved values. Code used is `Enumerable.Range(1,1000000).Select(x=>GetFormNumber()).GroupBy (x => x).Where(x=>x.Count()>1).Dump();` – ritcoder May 30 '15 at 19:47
3

My first answer did not address the uniqueness problem. My second one does:

static int counter;
public static int GetUniqueNumber()
{ 
    return counter++; 
}

If you want to have unique numbers across app restarts, you need to persist the value of counter to a database or somewhere else after each and every GetUniqueNumber call.

Mike Christiansen
  • 1,104
  • 2
  • 13
  • 30
usr
  • 168,620
  • 35
  • 240
  • 369
  • To further explain Hans comment, interlock is necessary in a multithreaded environment because 2 threads could wind up with the same unique number as addition is not an atomic operation. Declaring counter volatile would also solve this problem. – William Morrison Feb 08 '13 at 15:55
  • @WilliamMorrison if i `lock` the counter that would prevent the threads, wouldn't it? – Sid Jun 22 '17 at 16:29
  • 1
    @Sid You are correct, that would also prevent the issue. – William Morrison Jun 23 '17 at 18:08
2

This method will generate a random string, it doesn't rely on the Random method and also is far better than the guid approch :

public static string gen_Digits(int length)
{
    var rndDigits = new System.Text.StringBuilder().Insert(0, "0123456789", length).ToString().ToCharArray();
    return string.Join("", rndDigits.OrderBy(o => Guid.NewGuid()).Take(length));
}

you can increase the length for less collision chance, but to have a 100% unique sequence you have to persist the old generated values and check the newly created value for uniquness.

Chtioui Malek
  • 11,197
  • 1
  • 72
  • 69
2

The range of values is too small. An incrementing counter is the best solution, like in an ERP system - you set the first customer number to 1000 and the next one is 1001, 1002,...,99999999. Otherwise, if you get a random number (or part of GUID) from these, you'll hit the same number again. Depending on your app, sooner or later but it's guaranteed to happen sooner than just iterating over them.

Vladislav Zorov
  • 2,998
  • 19
  • 32
1

If you want a unique number between 10000000 and 99999999, start an integer from 10000000 and just start incrementing it. Generating sequentially ordered numbers is no less random than any other generated sequence, and a whole lot easier to generate.

Kevin Hsu
  • 1,726
  • 11
  • 14
0

If you express the no as a combination of day( 2digit), hour(2digit), minute(2digit), second(2digit) and year ( 4 digit) , then it will be 12 digit but always unique no.

 DateTime _now = DateTime.Now;
 string _dd = _now.ToString("dd"); //
 string _mm = _now.ToString("MM");
 string _yy = _now.ToString("yyyy");
 string _hh = _now.Hour.ToString();
 string _min = _now.Minute.ToString();
 string _ss = _now.Second.ToString();

 string _uniqueId= _dd+ _hh+ _mm+_min+_ss + _yy;
agileDev
  • 423
  • 5
  • 14
  • Year can be made of 2 digit , making the the total no. of 10 digit as there are a chance of matching 2 no. after 100 years. – agileDev Jan 26 '17 at 16:22
  • Wonderful, but the OP said 8 digits – demongolem Jan 26 '17 at 16:29
  • 1
    Calling this multiple times in the same second won't give you a unique id. Even adding microseconds will not guarantee this. – Michiel Aug 21 '18 at 09:13
  • Yes that's true then you have to think of micro or nano according to the probability but if the unique no. is generated by a user click then I think one second is fine. – agileDev Aug 23 '18 at 08:42
0
System.Threading.Thread.Sleep(1);
long code = (long)DateTime.UtcNow.Subtract(new DateTime(2018, 1, 1, 0, 0, 0, DateTimeKind.Utc)).TotalMilliseconds;

OR

System.Threading.Thread.Sleep(1000);
long code = (long)DateTime.UtcNow.Subtract(new DateTime(2018, 1, 1, 0, 0, 0, DateTimeKind.Utc)).TotalSeconds;