1

I want some generator script to generate unique numbers but not in one order. We need to sell tickets.

For example currently ticket numbers are like this:

100000
100001
100002
...

So the users can see how many are sold.

How can I generate unique numbers?

for example:

151647
457561
752163
...

I could use random number generator, but then I have always check in database if such number has not been generated.

Hmm, maybe when using index on that column - the check would not take long.

Still now I have to get last card number, if I want to add 1 to it, but getting last is fast enough.

And the more tickets will be sold, then bigger chance that RNG will generate existing number. So migth be more checks in future. SO the best would be to take last number and generate next by it.

Imane Fateh
  • 2,418
  • 3
  • 19
  • 23
Dariux
  • 3,953
  • 9
  • 43
  • 69
  • Use [`uniqid`](http://php.net/manual/en/function.uniqid.php)? See: [How unique is uniqid?](http://stackoverflow.com/q/4070110/1456376). – insertusernamehere Aug 21 '13 at 11:40
  • possible duplicate of [Generate random looking code from consecutive integers](http://stackoverflow.com/questions/15073971/generate-random-looking-code-from-consecutive-integers) – Joni Aug 21 '13 at 11:43
  • This kind of problem can be solved with format-preserving encryption, recently there was an answer on IT-security [Encrypting short identifiers](http://security.stackexchange.com/a/33784/8343). I'm not aware of a PHP implementation though. – martinstoeckli Aug 21 '13 at 12:02

5 Answers5

3

Here's a simple way to scramble ticket numbers (note: you need 64-bit PHP, or change the code to use the bcmath library):

function scramble($number) {
    return (305914*($number-100000)+151647) % 999983;
}

Look, the output even looks like your example:

Input     Output
------    ------
100000    151647
100001    457561
100002    763475
100003    069406

If you want to you can reverse it, so you can use these codes in URLs and then recover the original number:

function unscramble($number) {
    return (605673*($number-151647)+100000) % 999983 ;
}

Is this safe? Someone with access to many sequential numbers can find the pattern so don't use this if the ticket numbers are extremely sensitive.

Joni
  • 108,737
  • 14
  • 143
  • 193
  • The fact that an inverse function exists is a hint. 999983 is a prime number, so for example multiplication by any number 1 - 999982 modulo 999983 is a bijection (one-to-one function). Of course, the results are only unique modulo 999983. – Joni Aug 22 '13 at 07:39
  • "Of course, the results are only unique modulo 999983" - I am not sure about this- does this mean that numbers which are bigger than this - can be not unique when encoded? – Dariux Aug 22 '13 at 10:42
  • So you have more than a million tickets? There are bigger prime numbers. The choice of 999983 ensures that your codes will never be longer than 6 digits; if you have more than a million codes 6 there not enough 6-digit numbers to map them to. If you want codes with at most 9 digits you can use for example 999999937. – Joni Aug 22 '13 at 10:45
  • At the beggining there is not millio tickets but over lets say 10 years, who knows how many will be sold :) – Dariux Aug 22 '13 at 11:51
  • That would be a "nice problem to have" wouldn't it? You could start by assuming that there won't be more than a million tickets, and if you sell, say, 100 000, start looking for a solution, for example using codes with letters in addition to digits. – Joni Aug 22 '13 at 12:04
  • Of course we can do with some assumptions, but we must be sure if it will not be even harder later to make a change in the system. At the beggging when systen is not running live, its easy to make changes. So its best to make really safe solution. – Dariux Aug 23 '13 at 05:46
  • Then use 12-digit ticket numbers or even longer, instead of 6-digit numbers. You can generate them randomly or based on mathematical principles, just keep in mind that if you pick random numbers, finding unique numbers gets harder with time: when 50% of the numbers have been used you need two tries, with 80% you need 5, with 90% you need 10 etc – Joni Aug 23 '13 at 06:43
  • Btw looked closely at numbers I typed randomly and your result, its insane, how could I put 2 identical numbers of 6 digits by typing randomly :) – Dariux Sep 16 '13 at 05:36
  • Also wanted to ask: are those numbers: 305914 151647 random? 100000 is the first number as I understand? So if I want with more digits - just add more zeros. – Dariux Sep 16 '13 at 06:00
  • noticed that 151647 is the first number of output. So the remaining question is - does 305914 has some meaning?:) edit: found - it shows by how much is increased the next number – Dariux Sep 16 '13 at 06:24
  • How could you put two identical numbers by typing randomly - depends on how many you generate, if you generate 1000 the probability of repeating one is about 50% (look up the birthday problem). If you want more digits you'll need larger numbers. The modulus doesn't have to be a prime, just make sure it's relatively prime with the multiplier you choose. – Joni Sep 16 '13 at 08:10
  • return (605673*($number-1)+151647) % 999983 ; - would you see any bug if I use this formula? We are thinking numbering from 1, so if I pass 1, I get negative if subtracting 100000 when using suggested code – Dariux Sep 16 '13 at 13:03
  • Looks OK to me, you don't need the -1 or these particular numbers though, I just picked them to match your example. – Joni Sep 16 '13 at 14:51
  • Btw I still needed to use bcmod function, cause otherwise was getting bad results and I checked I do have 64bit php. And also made a test - created table with unique index and ran from 100000 to 999982 and if there was error it would have stopped. Also checked count of rows - its good. (Just in case someone is not so good at math and wants proof :) ) – Dariux Sep 17 '13 at 05:45
1

Generate random numbers, make the ticket number unique index, insert the record with the new ticket, if fails means that you had a collision, so you have to generate another id. With a good random space, say 32 bit integer, the chance of collision is minimal. The SQL implementation behind if the column is index and numerical is lightning fast.

sanyi
  • 5,999
  • 2
  • 19
  • 30
0

You can have your number generated, store in a pool, when you need new number, get one with RNG index of the pool, remove from the pool and return it.

if the pool nearly run out, just generate another batch of it

Lookis
  • 65
  • 4
0

The easy way, you can simply use md5() function..

And to get a 6 digit string, you can do

$x = md5(microtime());

echo substr($x, 0, 6);

Edit:

session_start();

    $x = md5(microtime().session_id());

    echo substr($x, 0, 6);
Niket Malik
  • 1,075
  • 1
  • 14
  • 23
  • Couldn't that lead to identical results, when two requests are made at the very same time? – insertusernamehere Aug 21 '13 at 11:48
  • @insertusernamehere not sure, is the possibility for that high enough? Plus i edited my answer. – Niket Malik Aug 21 '13 at 11:53
  • It can, yet the chances are small – sanyi Aug 21 '13 at 11:54
  • Nope, you are mapping a big space into a small one, mathematically speaking there are chances of collision. – sanyi Aug 21 '13 at 12:15
  • A primary key on the column? – Niket Malik Aug 21 '13 at 12:19
  • you mean like database autoincremented id? that would be not good, clients can see that it increments by 1. Actuyally we decided to add time() to the id so it should always be unique. WIll try to implement now – Dariux Aug 22 '13 at 08:03
  • Not auto increment, you can add a unique index to a column, which tells mysql not to insert duplicates, it returns number of affected rows as 0 if there is a duplicate. – Niket Malik Aug 22 '13 at 08:54
  • I am using unique index. We decided to do this: take 1 000 000, insert the row, then take its inserted id, add to that million and then use uniqid(). Then convert from hex to dec and take last chars of this 16 chars decimal number. Those x last chars are arred to that million+id so the numvber would be 13 chars long. Probably there can be repeated numbers but not often and unique index will not allow to add. But those numbers are incrementing of uniqid() and begginng is not changing often. And btw md5 we dont want because it has various chars, we want numeric. – Dariux Aug 22 '13 at 10:29
  • its interestign why there is not premade such function in php or sql, it should be needed long time ago for developers. – Dariux Aug 22 '13 at 10:30
  • Your method is ok, but got more chances for collisions, you should look for better options. – Niket Malik Aug 22 '13 at 10:49
0
 function generateCode() {



    $chars = '01234567890';
    do {
            $code = '';
            for ($x = 0; $x < 6; $x++) {
                $code .= $chars[ rand(0, strlen($chars)-1) ];
            }

you may check here in databse if this code has been generated earlier, if yes, return;

  } while (true);

    return $code;

    }
Prince Singh
  • 5,023
  • 5
  • 28
  • 32