Reputation: 21
I want to create user accounts with a public_id which is always a unique, integer random (not incremental) value.
I can use loops to check if the random integer is unique, but that doesn't seem like a really nice solution.
I found some alphabetic-numeric generators, and I guess I could convert them to integers using some string to integer converter, but are there an integer -specific ways?
I also worry about possible collisions, but it looks like the chance will be always there in a long run.(?)
Upvotes: 1
Views: 3423
Reputation: 1633
You can either use one of native php functions like mt_rand or use more reliably way - generating integer based on microtime function.
To ensure that the value is unique you need to add a unique index on a column in DB and write 'ON DUPLICATE UPDATE' to insert/update queries which will add some digits to the value if it is not unique
Upvotes: 1
Reputation: 351369
You can use a linear congruential generator with a large period.
Here is one that generates unique integers which always have 6 digits. It will not generate duplicates until it has generated all numbers between 100000 and 996722, which gives you almost 900 000 different numbers.
The condition is that you can provide the function the number it last generated. So if you store the number in the database, you have to somehow retrieve the last assigned one, so you can feed it to this function:
function random_id($prev) {
return 100000 + (($prev-100000)*97 + 356563) % 896723;
}
$prev = 100000; // must be a 6 digit number: the initial seed.
// Generate the first 10 pseudo-random integers.
for ($i = 0; $i < 10; $i++) {
$prev = random_id($prev);
echo $prev . "\n";
}
The above generation of the first 10 numbers yields:
456563
967700
331501
494085
123719
963860
855744
232445
749606
697735
You can do this for other ranges by following the rules in the referenced article on getting a full period in linear congruential generators. Concretely, if you want to generate numbers with n digits, where the first digit cannot be zero (so between 10n-1 and 10n-1), then I find it easiest to find a large prime just below 9⋅10n-1 to serve as the last number of the formula. The other two numbers can then be any positive integer, but better keep the first one small to avoid overflow.
However, PHP integers are limited to PHP_INT_MAX
(typically 2147483647), so for numbers with 10 or more digits you will need to use floating point operators. The %
operator should not be used then. Use fmod
instead.
For example, to generate numbers with 12 digits, you could use this formula:
function random_id($prev) {
return 100000000000 + fmod((($prev-100000000000)*97 + 344980016453), 899999999981);
}
$prev = 100000000000; // must be a 12 digit number: the initial seed.
// Generate the first 10 pseudo-random integers.
for ($i = 0; $i < 10; $i++) {
$prev = random_id($prev);
echo $prev . "\n";
}
Upvotes: 0
Reputation: 1
Using timeStamp will really do a great job since it uses time to generate it random numbers .you can also concatenate the below function with other random generated numbers.
function passkey($format = 'u', $utimestamp = null){
if (is_null($utimestamp)) {
$utimestamp = microtime(true);
}
$timestamp = floor($utimestamp);
$milliseconds = round(($utimestamp - $timestamp) * 1000000);
return date(preg_replace('`(?<!\\\\)u`', $milliseconds, $format),$timestamp);
}
echo passkey(); // 728362
Upvotes: 0
Reputation: 2152
There are 2 possible solutions:
1) If your "long run" is really really long - it means this is possible, that you are out of PHP_INT_MAX and there is no only-integer-specific way.
2) If you are not out of PHP_INT_MAX - then you need some storage for checking the ids.
In case of 1 you can use library hashids. To avoid collisions - you'll need some incremental counter on input. Then you can convert strings by each letter back to integer.
In case of 2 - you can use some in-memory database like redis for performance.
Upvotes: 0