0
\$\begingroup\$

The bellow functions are used to generate a string with random characters, and they have a parameter to specify the string length.

The string can be composed of letters, numbers or a conjunction of both.

I've been using this for a very long time now, and currently, I look at it and it seems to damn extensive for the end result.

Functions

function assign_rand_value($num) {

    // accepts 1 - 36
    switch($num) {
        case "1"  : $rand_value = "a"; break;
        case "2"  : $rand_value = "b"; break;
        case "3"  : $rand_value = "c"; break;
        case "4"  : $rand_value = "d"; break;
        case "5"  : $rand_value = "e"; break;
        case "6"  : $rand_value = "f"; break;
        case "7"  : $rand_value = "g"; break;
        case "8"  : $rand_value = "h"; break;
        case "9"  : $rand_value = "i"; break;
        case "10" : $rand_value = "j"; break;
        case "11" : $rand_value = "k"; break;
        case "12" : $rand_value = "l"; break;
        case "13" : $rand_value = "m"; break;
        case "14" : $rand_value = "n"; break;
        case "15" : $rand_value = "o"; break;
        case "16" : $rand_value = "p"; break;
        case "17" : $rand_value = "q"; break;
        case "18" : $rand_value = "r"; break;
        case "19" : $rand_value = "s"; break;
        case "20" : $rand_value = "t"; break;
        case "21" : $rand_value = "u"; break;
        case "22" : $rand_value = "v"; break;
        case "23" : $rand_value = "w"; break;
        case "24" : $rand_value = "x"; break;
        case "25" : $rand_value = "y"; break;
        case "26" : $rand_value = "z"; break;
        case "27" : $rand_value = "0"; break;
        case "28" : $rand_value = "1"; break;
        case "29" : $rand_value = "2"; break;
        case "30" : $rand_value = "3"; break;
        case "31" : $rand_value = "4"; break;
        case "32" : $rand_value = "5"; break;
        case "33" : $rand_value = "6"; break;
        case "34" : $rand_value = "7"; break;
        case "35" : $rand_value = "8"; break;
        case "36" : $rand_value = "9"; break;
    }
    return $rand_value;
}

function get_rand_alphanumeric($length) {
    if ($length>0) {
        $rand_str="";
        for ($i=1; $i<=$length; $i++) {
            mt_srand((double)microtime() * 1000000);
            $num = mt_rand(1,36);
            $rand_str .= assign_rand_value($num);
        }
    }
    return $rand_str;
}

function get_rand_numbers($length) {
    if ($length>0) {
        $rand_str="";
        for($i=1; $i<=$length; $i++) {
            mt_srand((double)microtime() * 1000000);
            $num = mt_rand(27,36);
            $rand_str .= assign_rand_value($num);
        }
    }
    return $rand_str;
}

function get_rand_letters($length) {
    if ($length>0) {
        $rand_str="";
        for($i=1; $i<=$length; $i++) {
            mt_srand((double)microtime() * 1000000);
            $num = mt_rand(1,26);
            $rand_str .= assign_rand_value($num);
        }
    }
    return $rand_str;
}

Usage:

Basically I have a main function with the values, then I call secondary functions to build my string based on the length parameter:

Letters:

$str = get_rand_letters(8); // Only Letters

Numbers:

$str = get_rand_numbers(8); // Only Numbers

AlphaNumeric:

$str = get_rand_alphanumeric(8); // Numbers and Letters

My question:

How would I go, as to reduce the amount of code, keeping the end result the same?

\$\endgroup\$

1 Answer 1

2
\$\begingroup\$

First: I'm sure there's even simpler way than this (i.e. a library that just does what you need, period), but heck, I figured I'd try. Been years since I spent my days with PHP (don't particularly miss it).

Anyway, skip the whole switch lookup. chr() will take an int and give you its ASCII value. The 97-122 range (inclusive) is a-z (65-90 is A-Z). As for numbers, well, that's what a random function gives you - no need to look that up. See Rene Geuze's comment below; while chr works just fine, range is indeed more readable.

function get_rand($min, $max) {
  mt_srand((double) microtime() * 1000000);
  return mt_rand($min, $max);
}

function get_rand_alphanumeric($length) {
  $alnum = "";
  $range = range("a", "z");
  $limit = count($range) + 9;
  while(strlen($alnum) < $length) {
    $rand = get_rand(0, $limit);
    if( $rand < 10 ) {
      $alnum .= $rand;
    } else {
      $alnum .= $range[$rand - 10];
    }
  }
  return $alnum;
}

function get_rand_numbers($length) {
  if( $length <= 8 ) { // avoid int overflow
    return (string) get_rand(pow(10, $length-1), pow(10, $length));
  } else {
    $numbers = "";
    while(strlen($numbers) < $length) $numbers .= get_rand_numbers(8);
    return substr($numbers, 0, $length);
  }
}

function get_rand_letters($length) {
  $letters = "";
  $range   = range("a", "z");
  $limit   = count($range) - 1;
  while(strlen($letters) < $length) $letters .= $range[get_rand(0, $limit)];
  return $letters;
}

You get results like this

get_rand_alphanumeric(42) => upop8eoome0y0av2qav1j7yn5linyjshiurc8lbjja
get_rand_numbers(42)      => 365818982423371436493339856184748778003731
get_rand_letters(42)      => abfmthyuxdlganhfthebfjaugeeoniqawocgavowpx

Edit I just realized that your original alphanumeric function will pick randomly from your entire list, meaning it's 2.6 times more likely that the pick will be a letter. I edited mine to do the same to keep the results similar.

Also, the calls to mt_srand are by far the most expensive operation. Consider calling it less frequently, if speed is a concern. If I omit it, the functions above are 7.5-10x faster than the original; if I leave it as you see in the code, the functions are still faster, but only very marginally so (1.1x to 1.4x). In either case, get_rand_numbers obviously sees the biggest speedup since there's not lookup going on.

Edit 2 Replaced chr usage with a range array, re: the comments

\$\endgroup\$
2
  • 1
    \$\begingroup\$ Using the range() function is probably more human readable than using chr. Used by filling one array with range and just pick randomly from the array instead. \$\endgroup\$
    – René
    Oct 4, 2012 at 16:41
  • \$\begingroup\$ @ReneGeuze Good point. Adding it to my answer, thanks \$\endgroup\$
    – Flambino
    Oct 4, 2012 at 17:15

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.