115

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.

4

18 Answers 18

184
$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 = (int)$now % $base;
    $result = $charset[$i] . $result;
    $now /= $base;
  }
  return substr(str_repeat($charset[0], $len) . $result, -$len); 
}

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")

Sign up to request clarification or add additional context in comments.

6 Comments

The problem with using md5() however is that you get a string made of a 16-character set (10 digits and a to f, i.e. 4 bits per string character). This may be enough for some purposes but may be too little for cryptographical purposes (five characters out of 16 symbols = 16^5 = 20 bits = 1048576 possibilities).
array_rand($seed, 5) will return array key, not value, so your code won't work
I think that using a cryptographic hash function to generate random characters is at least inappropriate.
Is it possible also to include ", ' and backslash in $charset? Do I need to use escape sequences to include those characters?
The second snippet does not even use the $len input param...did you forget it?
|
97

If for loops are on short supply, here's what I like to use:

$s = substr(str_shuffle(str_repeat("0123456789abcdefghijklmnopqrstuvwxyz", 5)), 0, 5);

9 Comments

Interesting solution... very concise, though I wonder about whether this is faster or slower than using for. Can't be bothered to benchmark though :-)
@matthew str_shuffle will produce duplicates continously
@user2826057: str_shuffle('ab') would give ab or ba but never aa. Adding the str_repeat allows for that. But that said, the solution I gave is not really a serious one... although it does work.
There is a 1/60466176 chance of that happening, assuming the RNG is uniformly distributed.
This is perfect for our use case of generating voucher codes. We removed confusing chars, like l, i, 1, 0, O, etc and out of 500 vouchers got no duplicates.
|
38

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

2 Comments

Can be a little bit make clear with substr(str_shuffle(implode(range('a', 'z'))), 0, $length)
this is nice, nothing fancy, straight forward and simple. [+1]
32

A speedy way is to use the most volatile characters of the uniqid function.

For example:

$rand = substr(uniqid('', true), -5);

2 Comments

-5 is for the length is that true?
@gumuruh The question was "…create exact 5 random characters string…", so passing -5 as the offset arg to substr will use the last five characters in the string. As per the PHP docs… "If offset is negative, the returned string will start at the offset'th character from the end of string."
16

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];
?>

3 Comments

First example more clever with $result .= $characters[mt_rand(0, strlen($characters)-1)]
In that case you should determine the length beforehand and store it in a variable at least. Using strlen() in a loop is unnecessary overhead, especially if you already know that the character set is not going to change in the meantime.
I doubt it, php is optimized, anyways it is an option :)
8

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;
}

1 Comment

Nice code. However $alt always flips from one to 0. Wouldn't it be better to randomize $alt instead?
8

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);

1 Comment

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"

Comments

3
$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

Comments

2

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.

6 Comments

Using time() is up to 1 million times more predictable than microtime().
Also note my comments to Brad's answer, the bigger issue is that the generated hash is a hexadecimal representation consisting of 16 valid characters, therefore leaving only 1024 variations for $str.
@Archimedix, perhaps, but the OP didn't ask for security, the question was defined to have string generating algorithm consisting of a combination of 5x26 different characters, and avoiding duplicates. This is probably for a confirmation reference number in a registration process (or billing process), or something
It was my understanding that he did ask for least possibility of getting duplicated, and this has 0.1% probability (1 in 1024) which could have been almost 1 in 1 billion.
however, if you need to generate a reference key of 5 characters and give that you a client/customer, you don't want to give them "˫§»⁋⅓" simply because it's more secure... you increase your reference key to 7 or more characters. (BTW: correction in my previous comment, it's 5x36 characters)
|
2

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.

Comments

1

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

Comments

1

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);

Comments

1

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);

Comments

1
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;
}

1 Comment

Pro tip - Include some explanation about how your answer solves the OP's problem, and why it might be different to the other answers already provided.
0

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.

1 Comment

I see that lost time idly developing this function. I googled little! @Andrew
0

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{|}~

Comments

0

Don't know if can help anyone but after 13 year the technology has changed:

$lenght=50;
$token=random_bytes($lenght/2);//divided by 2 because when converted to hex double it
echo $token=bin2hex($token);

That's all you need

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.