unique combination of arrays

I want to write a function that return random unique pair numbers every time call it from a range until resetting it. Something like this:

function randomUniquePairs($ranges, $reset = false){
   if ($reset === false){
      // some code for reseting
   }
   /*
     some code for creating random unique pair numbers
   */
   return $result;  
} 

randomUniquePairs(range(1,10), range(1,20));
/*
  this function returns for example:
   array(2,9)
*/
randomUniquePairs(range(1,10), range(1,20));
/*
  this function returns for example:
   array(5,19)
*/

randomUniquePairs(range(1,10), range(1,20));
/*
  this function returns for example:
   array(5,19)
*/
 //this function returns random unique pairs until we pass reset paramer true

I try two approaches:

1)one them is make all of possible pairs then select from them randomly, but it is very inefficient, because if ranges are so wide, it consumes a lot of memory. the code:

  class a {
    private $asqar;

    function __construct() ($range) {

        // cycle for ranges
        foreach ($range[0] as  $v1) {
            foreach ($range[1] as $v2) {
                $asqar[] =  array(
                    $v1,
                    $v2,
                );
            }
        }
    }

    function randomUniquePairs($reset = false){

        if ($reset === true){
           $this->asgar = array();
        }

        $rndKey = array_rand($this->asgar);
        $result = $this->asqar[$rndkey];
        unset($this->asqar[$rndkey]);
        return $result;
    }
}

$c = new a(range(1,10), range(1,20));
$c->randomUniquePairs();

2)second is write a function that produce a pair from these ranges, then store it in a variable, every time that function calls after producing a pair , it checks if this pair produced before call function recursively, it continues until it produce a unique pair. this code:

class a{ 

    private $__uniqueVariables = array();

    public function randomUniquePairs($range, $reset = false) {

        if ($reset === true){
       $this->__uniqueVariables = array();
    }

    // cycle for each value
    foreach ($range as $value) {

        // shuffle
        shuffle($value);

        // selected id
        $selectedId[] = $value[0];
    }

    // check for selected variable
    if (in_array($rndUnqNum, $this->__uniqueVariables)) {

        // return for try again
        return $this->uniqueVariable($range);
    }

    // added to current unique variables
    $this->__uniqueVariables[] = $rndUnqNum;

    // return
    return $rndUnqNum;
}

}

but This has an issue that sometimes throw Fatal error: Maximum function nesting level of '100' reached.

I want any better algorithm.

Upvotes: 0

Views: 233

Answers (1)

timafield
timafield

Reputation: 41

This seems to do well even at large ranges:

The rand() function is a great way to get a random integer in a specified range without having to construct or manipulate an array of values, as in your examples above.

Update I added a stop case (when history is equal to or greater than the max possible unique pairs it returns an empty array). Feel free to change that code to automatically reset the range.

<?php
class c {

    function c($min_X,$max_X,$min_Y,$max_Y) {
        $this->setRange($min_X,$max_X,$min_Y,$max_Y);
    }

    function getRandUniquePair($reset = false) {
        if ($reset) {
            $this->__history = array();
        }

        if (count($this->__history) >= $this->__max_pairs) {
            return array();
        }

        $candidate = array(rand($this->__range[0],$this->__range[1]),rand($this->__range[2],$this->__range[3]));

        if (in_array($candidate,$this->__history)) {
            return $this->getRandUniquePair();
        }

        $this->__history[] = $candidate;

        return $candidate;
    }

    function setRange($min_X,$max_X,$min_Y,$max_Y) {
        $this->__range = array($min_X,$max_X,$min_Y,$max_Y);
        $this->__max_pairs = ($max_X - $min_X) * ($max_Y - $min_Y);
        $this->__history = array();
    }
}

// test

$c = new c(0,10,0,20);

$i = 0;
$pairs = array();
while ($i < 100) {
    $i++;
    $pair = $c->getRandUniquePair();

    if (in_array($pair,$pairs)) {
        die('Duplicate pairs!');
    }

    echo $pair[0].', '.$pair[1]."\n";
    $pairs[] = $pair;
}

$c->setRange(0,100000000000,0,100000000000);
echo "I perform well even at large ranges!\n";

$i = 0;
while ($i < 1000) {
    $i++;
    $pair = $c->getRandUniquePair();

    echo $pair[0].', '.$pair[1]."\n";
}

?>

Upvotes: 1

Related Questions