Generate array of random unique numbers in PHP

前端 未结 7 889
再見小時候
再見小時候 2020-12-11 15:09

I\'m trying to generate an array of random numbers from 0-n then shuffle (but ensure that the keys and values DO NOT match).

For example:

0 => 3
1         


        
相关标签:
7条回答
  • 2020-12-11 15:36
    $max = 5;
    $done = false;
    while(!$done){
        $numbers = range(0, $max);
        shuffle($numbers);
        $done = true;
        foreach($numbers as $key => $val){
            if($key == $val){
                $done = false;
                break;
            }
        }
    }
    
    0 讨论(0)
  • 2020-12-11 15:36

    Naive solution:

    $n = 10;
    $rands = array();
    for($i=0; $i<$n;$i++) {
      $ok = false;
      while(!$ok) {
        $x=mt_rand(0,$n-1);
        $ok = !in_array($x, $rands) && $x != $i;
      }
      $rands[$i]=$x;
    }
    
    var_dump($rands);
    

    Efficient solution:

    $n = 100;  
    $numbers = range(0, $n-1);
    $rands = array();
    for ($i=0; $i < $n; $i++) {
      $ok = false;
      while (!$ok) {
        $x = array_rand($numbers);
        $ok = !in_array($numbers[$x], $rands) && $numbers[$x] != $i;
      }
      $rands[$i] = $numbers[$x];
      unset($numbers[$x]);
    }
    
    var_dump($rands);
    

    edit: s/rand/mt_rand/

    edit #2: both solutions can end up in a deadlock, as mentioned by @AMayer. I stand corrected.

    0 讨论(0)
  • 2020-12-11 15:37

    Here's a rather long, but also pretty efficient solution, I believe. Contrary to other solutions posted here, this cannot deadlock (unless $size<2), and this will not do a full shuffle every time one value doesn't fit. Instead, it will only replace that value with another, random value.

    function unique_list($size=5) {
    
        function all_unique($numbers) {
            foreach ($numbers as $key=>$value)
                if ($key==$value) return false;
            return true;
        }
        function flip($a, $b, &$numbers) {
            $numbers[$a] = $numbers[$a] + $numbers[$b];
            $numbers[$b] = $numbers[$a] - $numbers[$b];
            $numbers[$a] = $numbers[$a] - $numbers[$b];
        }
    
        $flip_count = 0;
        $numbers = range(0,$size-1);
        shuffle($numbers);
    
        while (!all_unique($numbers)) {
            foreach ($numbers as $key=>$value) {
                if ($key==$value) {
                    flip($key, rand(0,$size-1), $numbers);
                    $flip_count++;
                    break;
                }
            }
        }
    
        printf("Flipped %d values\n", $flip_count);
        return $numbers;
    
    }
    
    $list = unique_list(10);
    print_r($list);
    

    The above will print something similar to

    Flipped 1 value(s)
    Array
    (
        [0] => 2
        [1] => 5
        [2] => 7
        [3] => 9
        [4] => 6
        [5] => 3
        [6] => 1
        [7] => 8
        [8] => 0
        [9] => 4
    )
    
    0 讨论(0)
  • 2020-12-11 15:39

    The approach here is to create a range of numbers from 0 to n, and then shuffle. We then look for key value matches, and swap pairs of them. If we don't have a pair to swap, we swap with the last item, unless the item is the last item, then we swap with the first.

    <?php
    $n = 5;
    $values = range(0, $n);
    shuffle($values);
    $matched = array_filter($values, function($v, $k) {return $k === $v;}, ARRAY_FILTER_USE_BOTH);
    foreach(array_chunk($matched, 2) as list($a, $b)) {
        if($b === null) {
            $swap_key = array_key_last($values);
            if($swap_key == $a) {
                $swap_key = array_key_first($values);
            }
            list($values[$a], $values[$swap_key]) = [$values[$swap_key], $a];
        } else {
            list($values[$a], $values[$b]) = [$b, $a];
        }
    }
    

    Example shuffle when n is 5:

    array (
      0 => 0,
      1 => 5,
      2 => 1,
      3 => 3,
      4 => 4,
      5 => 2,
    )
    

    Here we have matches for keys:

    0, 3 and 4.
    

    So we swap the values for keys 0 and 3, and 4 with the last.

    array (
      0 => 3,
      1 => 5,
      2 => 1,
      3 => 0,
      4 => 2,
      5 => 4,
    )
    

    (array_key_first could be swapped for 0 given the range here. I've left it as it is more explicit.)

    0 讨论(0)
  • 2020-12-11 15:40

    A even shorter solution:

    $random_number_array = range(0, 100);
    shuffle($random_number_array );
    $random_number_array = array_slice($random_number_array ,0,10);
    
    print_r($random_number_array);
    

    Result will be:

    [0] => 53
    [1] => 6
    [2] => 16
    [3] => 59
    [4] => 8
    [5] => 18
    [6] => 62
    [7] => 39
    [8] => 22
    [9] => 26
    
    0 讨论(0)
  • 2020-12-11 15:41

    This will generate an array with 10 random numbers from 0 to 100:

    array_map(function () {
           return rand(0, 100);
       }, array_fill(0, 10, null)
    );
    

    Result:

    array(10) {
      [0]=>
      int(15)
      [1]=>
      int(97)
      [2]=>
      int(20)
      [3]=>
      int(64)
      [4]=>
      int(57)
      [5]=>
      int(38)
      [6]=>
      int(16)
      [7]=>
      int(53)
      [8]=>
      int(56)
      [9]=>
      int(22)
    }
    

    Explanation:

    • array_fill(0, 10, null) will generate an array with 10 empty items
    • array_map Applies the callback (first argument) to each item of the array it receives (second argument). In this example, we just return a random number for each array item.

    Playground: https://3v4l.org/FffN6

    0 讨论(0)
提交回复
热议问题