I'm trying to generate a process in order to create aproximately 5000 unique keys into a table with rand_md5 function. Sometimes it is giving a repeated "unique" key constraint violation. What can I do to solve this?
function rand_md5($length) {
$max = ceil($length / 32);
$random = '';
for ($i = 0; $i < $max; $i ++) {
$random .= md5(microtime(true).mt_rand(10000,90000));
}
return substr($random, 0, $length);
This function is called inside a for loop from 1 to 5000 iterations (for example).
Any ideas how to solve this?
You have to keep track of the allocated values and check for each newly generated one if it was already used.
function rand_md5($length) {
$max = ceil($length / 32);
$random = '';
for ($i = 0; $i < $max; $i ++) {
$random .= md5(microtime(true).mt_rand(10000,90000));
}
return substr($random, 0, $length);
}
$randoms = [];
do {
$rnd = rand_md5($length);
} while( in_array( $rnd, $randoms ) );
$randoms[] = $rnd;
First suggestion, use a library better at generating unique tokens, i..e, uuid().
Secondly, ensure that your database column is long enough to store the string. Whilst probable you will generate collisions with your algorithm, frequently seems to imply you (or the database) are truncating the string.
Using openssl_random_pseudo_bytes:
function randomKey($length) {
//Need a length divisible by two.
$length = intval($length);
if (!$length || $length % 2)
return false;
//Get random bytes & hex encode.
$key = openssl_random_pseudo_bytes($length / 2);
return (is_string($key) ? bin2hex($key) : false);
}
I think it can help you
function rand_md5($length) {
$random = md5(microtime(true).mt_rand(10000,90000));
$random = str_shuffle($random);
return substr($random, 0, $length);
}
Related
I have a loop that generates all the possible combinations of bits by giving the number of bits desired, bu the issue is that I got out of memory when number of bits goes beyond 20, is there any optimizations that I can do, to solve this issue.
here my code :
function bitsGenerator($N)
{
$listN = $N;
$bits = ['0', '1'];
//check if input is valid or not
if (!is_int($listN)) {
echo 'Input must be numeric!';
}
if ($listN >= 1 && $listN <= 65) {
if ($listN == 1) {
echo '1';
exit;
}
for ($i = 1; $i <= ($listN - 1); $i++) {
$reverseBits = array_reverse($bits);
$prefixBit = preg_filter('/^/', '0', $bits);
$prefixReverseBits = preg_filter('/^/', '1', $reverseBits);
$bits = array_merge($prefixBit, $prefixReverseBits);
unset($prefixBit, $prefixReverseBits, $reverseBits);
}
$finalBits = array_slice($bits, -$listN);
foreach ($finalBits as $k => $v) {
echo $v . "\n";
}
} else {
echo 'Invalid input!';
}
}
The purpose of this function is to get the last $N combinations and display them all other combinations are thrown away, I'm looking for some kind of optimization to my code so that the $bits array will not store more than 65 item because the maximum number to bits thus the maximum number of combination to display is 65.
Thanks to every one for helping me.
The main problem is the size of the $bits array holding all your results. The array will contain 2^$N elements, each of $N length times 8 bits for each character (because you are using strings to have leading zeroes) so you'll end up with a memory consumption of approx (2^$N)*$N*8 which is 167772160 bytes. It won't get any smaller when using RAM.
Your working copies of $bits, preg_filter and array_merge will also consume a lot of RAM. Running your function with $N = 20 consumes 180375552 (172MiB).
BTW: unseting the variables will not reduce the consumed RAM because they would get overwritten in the next iteration anyway (or destroyed at the end of the function)
The following function was my first sketch based on your function and uses a bit less RAM: 171970560 bytes or 164MiB (vs 172MiB).
function myBitsGenerator($length)
{
if (!is_int($length)) {
die('Input must be numeric!');
}
if ($length < 1 || $length > 65) {
die('Input must be between 1 and 65');
}
$bitsArray = ['0', '1'];
$count = 2;
if ($length > 1) {
for ($i = 0; $i < $length; $i++) {
for ($j = 0; $j < $count; $j++) {
$bitsArray[] = $bitsArray[$j] . '1';
$bitsArray[$j] = $bitsArray[$j] . '0';
}
$count += $j;
}
}
$printArray = array_slice($bitsArray, $count - $length);
array_walk(
$printArray,
function ($value) {
echo $value . PHP_EOL;
}
);
}
However, the generation of those numbers is too complicated and should be simplified:
Theoretical: A binary number can be written in decimal and vice versa. The number of possible combinations of binary numbers with the fixed length of $N are x = 2 ^ $N. Each decimal number from 0 to x represents a binary number in the results.
Practical example: (binary) (0b101 === 5) (int)
All you have to do is to pad the calculated binary number with zeroes.
The simplified generator looks like:
$n = pow(2, $length);
for ($i = 0; $i < $iterations; $i++) {
$binary = str_pad(decbin($i), $length, '0', STR_PAD_LEFT);
}
You can use this generator to generate
If you really need to use even less RAM you should think about storing it in a file, which makes the whole think slower but it will use way less RAM.
function fileBitsGenerator($length)
{
$handle = fopen('bits.txt', 'w+');
$iterations = pow(2, $length);
for ($i = 0; $i < $iterations; $i++) {
fwrite($handle, str_pad(decbin($i), $length, '0', STR_PAD_LEFT) . PHP_EOL);
}
}
This consumes just 2097152 bytes and scales!
But be aware that the performance will depend on your HDD/SSD speed and it executes a lot of write operations (which might shorten your SSD life span). For example: the resulting file bits.txt is 92MB big if length = 22
So I am trying to do math on an array of integers while enforcing a maximum integer in each piece of the array. Similar to this:
function add($amount) {
$result = array_reverse([0, 0, 0, 100, 0]);
$max = 100;
for ($i = 0; $i < count($result); ++$i) {
$int = $result[$i];
$new = $int + $amount;
$amount = 0;
while ($new > $max) {
$new = $new - $max;
++$amount;
}
$result[$i] = $new;
}
return array_reverse($result);
}
add(1); // [0, 0, 0, 100, 1]
add(100); // [0, 0, 0, 100, 100]
add(101); // [0, 0, 1, 0, 100]
So what I have above works but it is slow when adding larger integers. I've tried to do this with bitwise shifts and gotten close but I just can't get it to work for some reason. I think I need a third-party perspective. Does anyone have some tips?
The part that is taking up the majority of the time is the while loop. You are reducing the value down repeatedly until you have a sub-100 value. However, using PHP to loop down like that takes an incredible amount of time (a 12-digit integer clocked in at over 20 seconds on my local machine). Instead, use multiplication and division (along with an if). It is magnitudes faster. The same 12-digit integer took less than a second to complete with this code:
function add($amount) {
$result = array_reverse([0, 0, 0, 100, 0]);
$max = 100;
for ($i = 0, $size = count($result); $i < $size; ++$i) {
$int = $result[$i];
$new = $int + $amount;
$amount = 0;
if( $new > $max ) {
$remainder = $new % $max;
// Amount is new divided by max (subtract 1 if remainder is 0 [see next if])
$amount = ((int) ($new / $max));
// If remainder exists, new is the the number of times max goes into new
// minus the value of max. Otherwise it is the remainder
if( $remainder == 0 ) {
$amount -= 1;
$new = $new - ((($new / $max) * $max) - $max);
} else {
$new = $remainder;
}
}
$result[$i] = $new;
}
return array_reverse($result);
}
Also note that I moved your count($result) call into the variable initialization section of the for loop. When it is inside the expression section it gets executed each time the for loop repeats which can also add to the overall time of executing the function.
Also note that with a large math change like this you may want to assert a range of values you expect to calculate to ensure there are no outliers. I did a small range and they all came out the same but I encourage you to run your own.
Use min($max, $number) to get $number limited to $max.
for ($i = 0; $i < count($result); ++$i) {
$result[$i] = min($max, $result[$i] + $amount);
}
I want to create exact 5 random characters string with least possibility of getting duplicated. What would be the best way to do it? Thanks.
$rand = substr(md5(microtime()),rand(0,26),5);
Would be my best guess--Unless you're looking for special characters, too:
$seed = str_split('abcdefghijklmnopqrstuvwxyz'
.'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
.'0123456789!##$%^&*()'); // and any other characters
shuffle($seed); // probably optional since array_is randomized; this may be redundant
$rand = '';
foreach (array_rand($seed, 5) as $k) $rand .= $seed[$k];
Example
And, for one based on the clock (fewer collisions since it's incremental):
function incrementalHash($len = 5){
$charset = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
$base = strlen($charset);
$result = '';
$now = explode(' ', microtime())[1];
while ($now >= $base){
$i = $now % $base;
$result = $charset[$i] . $result;
$now /= $base;
}
return substr($result, -5);
}
Note: incremental means easier to guess; If you're using this as a salt or a verification token, don't. A salt (now) of "WCWyb" means 5 seconds from now it's "WCWyg")
If for loops are on short supply, here's what I like to use:
$s = substr(str_shuffle(str_repeat("0123456789abcdefghijklmnopqrstuvwxyz", 5)), 0, 5);
You can try it simply like this:
$length = 5;
$randomletter = substr(str_shuffle("abcdefghijklmnopqrstuvwxyz"), 0, $length);
more details: http://forum.arnlweb.com/viewtopic.php?f=7&t=25
A speedy way is to use the most volatile characters of the uniqid function.
For example:
$rand = substr(uniqid('', true), -5);
The following should provide the least chance of duplication (you might want to replace mt_rand() with a better random number source e.g. from /dev/*random or from GUIDs):
<?php
$characters = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
$result = '';
for ($i = 0; $i < 5; $i++)
$result .= $characters[mt_rand(0, 61)];
?>
EDIT:
If you are concerned about security, really, do not use rand() or mt_rand(), and verify that your random data device is actually a device generating random data, not a regular file or something predictable like /dev/zero. mt_rand() considered harmful:
https://spideroak.com/blog/20121205114003-exploit-information-leaks-in-random-numbers-from-python-ruby-and-php
EDIT:
If you have OpenSSL support in PHP, you could use openssl_random_pseudo_bytes():
<?php
$length = 5;
$randomBytes = openssl_random_pseudo_bytes($length);
$characters = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
$charactersLength = strlen($characters);
$result = '';
for ($i = 0; $i < $length; $i++)
$result .= $characters[ord($randomBytes[$i]) % $charactersLength];
?>
I always use the same function for this, usually to generate passwords. It's easy to use and useful.
function randPass($length, $strength=8) {
$vowels = 'aeuy';
$consonants = 'bdghjmnpqrstvz';
if ($strength >= 1) {
$consonants .= 'BDGHJLMNPQRSTVWXZ';
}
if ($strength >= 2) {
$vowels .= "AEUY";
}
if ($strength >= 4) {
$consonants .= '23456789';
}
if ($strength >= 8) {
$consonants .= '##$%';
}
$password = '';
$alt = time() % 2;
for ($i = 0; $i < $length; $i++) {
if ($alt == 1) {
$password .= $consonants[(rand() % strlen($consonants))];
$alt = 0;
} else {
$password .= $vowels[(rand() % strlen($vowels))];
$alt = 1;
}
}
return $password;
}
It seems like str_shuffle would be a good use for this.
Seed the shuffle with whichever characters you want.
$my_rand_strng = substr(str_shuffle("ABCDEFGHIJKLMNOPQRSTUVWXYZ"), -5);
I also did not know how to do this until I thought of using PHP array's. And I am pretty sure this is the simplest way of generating a random string or number with array's. The code:
function randstr ($len=10, $abc="aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ0123456789") {
$letters = str_split($abc);
$str = "";
for ($i=0; $i<=$len; $i++) {
$str .= $letters[rand(0, count($letters)-1)];
};
return $str;
};
You can use this function like this
randstr(20) // returns a random 20 letter string
// Or like this
randstr(5, abc) // returns a random 5 letter string using the letters "abc"
$str = '';
$str_len = 8;
for($i = 0, $i < $str_len; $i++){
//97 is ascii code for 'a' and 122 is ascii code for z
$str .= chr(rand(97, 122));
}
return $str
Similar to Brad Christie's answer, but using sha1 alrorithm for characters 0-9a-zA-Z and prefixed with a random value :
$str = substr(sha1(mt_rand() . microtime()), mt_rand(0,35), 5);
But if you have set a defined (allowed) characters :
$validChars = array('0','1','2' /*...*/,'?','-','_','a','b','c' /*...*/);
$validCharsCount = count($validChars);
$str = '';
for ($i=0; $i<5; $i++) {
$str .= $validChars[rand(0,$validCharsCount - 1)];
}
** UPDATE **
As Archimedix pointed out, this will not guarantee to return a "least possibility of getting duplicated" as the number of combination is low for the given character range. You will either need to increase the number of characters, or allow extra (special) characters in the string. The first solution would be preferable, I think, in your case.
If it's fine that you'll get only letters A-F, then here's my solution:
str_pad(dechex(mt_rand(0, 0xFFFFF)), 5, '0', STR_PAD_LEFT);
I believe that using hash functions is an overkill for such a simple task as generating a sequence of random hexadecimal digits. dechex + mt_rand will do the same job, but without unnecessary cryptographic work. str_pad guarantees 5-character length of the output string (if the random number is less than 0x10000).
Duplicate probability depends on mt_rand's reliability. Mersenne Twister is known for high-quality randomness, so it should fit the task well.
works fine in PHP (php 5.4.4)
$seed = str_split('abcdefghijklmnopqrstuvwxyz');
$rand = array_rand($seed, 5);
$convert = array_map(function($n){
global $seed;
return $seed[$n];
},$rand);
$var = implode('',$convert);
echo $var;
Live Demo
Source: PHP Function that Generates Random Characters
This simple PHP function worked for me:
function cvf_ps_generate_random_code($length=10) {
$string = '';
// You can define your own characters here.
$characters = "23456789ABCDEFHJKLMNPRTVWXYZabcdefghijklmnopqrstuvwxyz";
for ($p = 0; $p < $length; $p++) {
$string .= $characters[mt_rand(0, strlen($characters)-1)];
}
return $string;
}
Usage:
echo cvf_ps_generate_random_code(5);
Here are my random 5 cents ...
$random=function($a, $b) {
return(
substr(str_shuffle(('\\`)/|#'.
password_hash(mt_rand(0,999999),
PASSWORD_DEFAULT).'!*^&~(')),
$a, $b)
);
};
echo($random(0,5));
PHP's new password_hash() (* >= PHP 5.5) function is doing the job for generation of decently long set of uppercase and lowercase characters and numbers.
Two concat. strings before and after password_hash within $random function are suitable for change.
Paramteres for $random() *($a,$b) are actually substr() parameters. :)
NOTE: this doesn't need to be a function, it can be normal variable as well .. as one nasty singleliner, like this:
$random=(substr(str_shuffle(('\\`)/|#'.password_hash(mt_rand(0,999999), PASSWORD_DEFAULT).'!*^&~(')), 0, 5));
echo($random);
function CaracteresAleatorios( $Tamanno, $Opciones) {
$Opciones = empty($Opciones) ? array(0, 1, 2) : $Opciones;
$Tamanno = empty($Tamanno) ? 16 : $Tamanno;
$Caracteres=array("0123456789","abcdefghijklmnopqrstuvwxyz","ABCDEFGHIJKLMNOPQRSTUVWXYZ");
$Caracteres= implode("",array_intersect_key($Caracteres, array_flip($Opciones)));
$CantidadCaracteres=strlen($Caracteres)-1;
$CaracteresAleatorios='';
for ($k = 0; $k < $Tamanno; $k++) {
$CaracteresAleatorios.=$Caracteres[rand(0, $CantidadCaracteres)];
}
return $CaracteresAleatorios;
}
I`ve aways use this:
<?php function fRand($len) {
$str = '';
$a = "abcdefghijklmnopqrstuvwxyz0123456789";
$b = str_split($a);
for ($i=1; $i <= $len ; $i++) {
$str .= $b[rand(0,strlen($a)-1)];
}
return $str;
} ?>
When you call it, sets the lenght of string.
<?php echo fRand([LENGHT]); ?>
You can also change the possible characters in the string $a.
Simple one liner which includes special characters:
echo implode("", array_map(function() {return chr(mt_rand(33,126));}, array_fill(0,5,null)));
Basically, it fills an array with length 5 with null values and replaces each value with a random symbol from the ascii-range and as the last, it joins them together t a string.
Use the 2nd array_fill parameter to control the length.
It uses the ASCII Table range of 33 to 126 which includes the following characters:
!"#$%&'()*+,-./0123456789:;<=>?#ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~
After using sites like JSFiddle I noticed that they auto generate a random and unique URL made up of various upper and lower case characters.
We could benefit from this for our booking pages. How is it done?
This is not random, there based of the ID of your database record.
How it works:
basically you have a string that is unique but it can be decrypted to represent a number, you should look at it as a short encryption / decryption.
You have a function that would take an unique ID and then it creates a Unique 'short string' from that id, and then you can reverse the process to get the unique id from the short string.
Here's a snipped I have found of a website:
function alphaID($in, $to_num = false, $pad_up = false, $passKey = null)
{
$index = "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
if ($passKey !== null)
{
/* Although this function's purpose is to just make the
* ID short - and not so much secure,
* with this patch by Simon Franz (http://blog.snaky.org/)
* you can optionally supply a password to make it harder
* to calculate the corresponding numeric ID */
for ($n = 0; $n<strlen($index); $n++)
{
$i[] = substr( $index,$n ,1);
}
$passhash = hash('sha256',$passKey);
$passhash = (strlen($passhash) < strlen($index)) ? hash('sha512',$passKey) : $passhash;
for ($n=0; $n < strlen($index); $n++)
{
$p[] = substr($passhash, $n ,1);
}
array_multisort($p, SORT_DESC, $i);
$index = implode($i);
}
$base = strlen($index);
if ($to_num)
{
// Digital number <<-- alphabet letter code
$in = strrev($in);
$out = 0;
$len = strlen($in) - 1;
for ($t = 0; $t <= $len; $t++)
{
$bcpow = bcpow($base, $len - $t);
$out = $out + strpos($index, substr($in, $t, 1)) * $bcpow;
}
if (is_numeric($pad_up))
{
$pad_up--;
if ($pad_up > 0)
{
$out -= pow($base, $pad_up);
}
}
$out = sprintf('%F', $out);
$out = substr($out, 0, strpos($out, '.'));
}
else
{
// Digital number -->> alphabet letter code
if (is_numeric($pad_up))
{
$pad_up--;
if ($pad_up > 0)
{
$in += pow($base, $pad_up);
}
}
$out = "";
for ($t = floor(log($in, $base)); $t >= 0; $t--)
{
$bcp = bcpow($base, $t);
$a = floor($in / $bcp) % $base;
$out = $out . substr($index, $a, 1);
$in = $in - ($a * $bcp);
}
$out = strrev($out); // reverse
}
return $out;
}
Example
alphaID(9007199254740989); //-> PpQXn7COf
alphaID('PpQXn7COf', true); //-> 9007199254740989
there's a link to the script here: https://github.com/kvz/deprecated/blob/kvzlib/php/functions/alphaID.inc.php
Use the uniqid() function if you just want a random id of a specified length or use the md5() hash of your long url if you need something repeatable. (output always the same for a given input). Creating a short URL service using PHP and MySQL gives a good rundown of how to put it all together.
It is very simple.
your url is something like
www.domain.com/in/here/RANDOMPART
you rewrite www.domain.com/in/here/* to a script of your choice. In this script you can get the requested uri with the globals and you can split it and use the part after the last '/'. This is your RANDOMPART. yust check it against your db or whatever.
To create such a url you simply have to add somthing behind "www.domain.com/in/here/". You could genereate a nice hopefully unique string withmd5(uniqid(rand(), true)).
Do not forget to save this string in a db or whatever.
Hope, that helps.
Something along these lines:
<?php
$url_length = rand(10,20); //generate a random number between 10 and 20 for the length of the URL
$url = array($url_length);
for($i=0; $i < $url_length; $i++)
{
$char = 0;
while($char < 65 OR $char > 122 OR ($char > 91 AND $char < 97))
{
$char = rand(65,122);
}
$url[] = chr($char);
}
print_r($url);
?>
Note this is partially psuedo code. This creates a URL with a random length (somewhere between 10 and 20), and populates each character with a random ASCII code that cooresponds to a letter in the alphabet. There are a few "junk" characters between the upper and lower case letters in the ASCII table, so this will reroll a random number until a valid ASCII number is generated.
This assumes you are storing this URL in a database. To ensure that this string is unique, you need to check to see if it exists yet in the database. If not, then add it and you're fine. If it exists, then redo the process again until you get a unique string.
You could simply create an array of all potential characters then randomly pick out however many you want and return them as the URL "code"?
$arr = array('a','b','c','d');
$count = count($arr) - 1;
$str = $arr[rand(0, $coun)].$arr[rand(0, $coun)];
I am trying to create a "random" string based on a fixed string. I'd like to be able, if at all possible, create the same random string (i know its an oxymoron) provided I use the same seed. like so:
$base = '0123456789abcdef';
$seed = 'qwe123';
function get_seeded_random_string($base, $seed){
???
}
The expected behavior would be that as long as I give the same $base and $seed I always get the same random string.
Sorry, but accordingly to the documentation the shuffle function is seeded automatically.
Normally, you shouldn't try to come up with your own algorithms to randomize things since they are very likely to be biased. The Fisher-Yates algorithm is known to be both efficient and unbiased though:
function fisherYatesShuffle(&$items, $seed)
{
#mt_srand($seed);
$items = array_values($items);
for ($i = count($items) - 1; $i > 0; $i--)
{
$j = #mt_rand(0, $i);
$tmp = $items[$i];
$items[$i] = $items[$j];
$items[$j] = $tmp;
}
}
Same function for a string in php7
function fisherYatesShuffle(string &$items, int $seed)
{
#mt_srand($seed);
for ($i = strlen($items) - 1; $i > 0; $i--)
{
$j = #mt_rand(0, $i);
$tmp = $items[$i];
$items[$i] = $items[$j];
$items[$j] = $tmp;
}
}
Yes, with mt_srand you can specify the seed for the "better" random number generator mt_rand.