skip to Main Content

I have some code that generates a random output based on prior parameters from different arrays

Data setup:

$persons = array("Old", "Young", "Child");
$moods = array("neutral", "happy", "sad");
$newarray= "";

shuffle($persons );
$persons2 = array_merge(...array_fill(1, 92, $persons ));
$persons2 = $array = array_slice($persons2, 1, 30);

shuffle($moods);
$moods2 = array_merge(...array_fill(1, 92, $moods));
$moods2 = $array = array_slice($moods2, 1, 30);

The desired output is an if-loop that generates strings of equally many old, young, and child objects that are each an equal amount of neutral, happy, and sad – something like

for ($x = 0; $x <= 30; $x++) {

$mood = $moods2[$x % (count($moods2))];
$person = $persons2 [$x % (count($persons2))];
$newarray .= "|".$person.",".$mood."|";

  }

The problem with this code is that it does not ensure that the moods "happy", "sad", "neutral" are combined evenly with the person categories "Old", "Young", "Child".
The desired output is something like

$newarray= "|Old,happy||Old,sad||Old,netrual||Young,happy||Young,sad||Young,netrual||Child,happy||Child,sad||Child,neutral|"; 

such that there is 30 iterations and the possible persons/moods combinations occur equally frequently.

Not that I would also like that final output to be randomly ordered and that the code should work even when changing the number of iterations of the for-loop.

2

Answers


  1. What you want to do is create to set of all possible permutations of your input, then fill an array of the desired size with the set of permutations then shuffle it.

    <?php
    
    $person = ['Old', 'Young', 'Child'];
    $moods = ['neutral', 'happy', 'sad'];
    
    // Create a buffer for our set of combinations
    $cartesianProduct = [];
    
    // Nested loop to create the total set of combinations
    foreach($person as $currPerson)
    {
        foreach($moods as $currMood)
        {
            // Create string representation of the combination
            $cartesianProduct[] = $currPerson.' '.$currMood;
        }
    }
    
    // Desired size of output array
    $limit = 30;
    
    // Buffer for the final output
    $output = [];
    
    // Start at a random point and keep track of where we are in the set of possible combinations
    $cartIndex = array_rand($cartesianProduct);
    
    // Loop until we have the desired buffer size
    while (sizeof($output) < $limit)
    {
        // Add the current input to the output and increment the input index
        $output[] = $cartesianProduct[$cartIndex++];
    
        // If we have reached the end of the input buffer, reset to zero
        if ($cartIndex == sizeof($cartesianProduct))
        {
            $cartIndex = 0;
        }
    }
    
    // Randomize the output buffer
    shuffle($output);
    
    // Let's look at the results...
    print_r($output);
    

    You can do this in a more generic and reusable way (and support any number of input sets, not just two) using a class to create the Cartesian product.

    <?php
    
    class Cartesian
    {
        public static function build($set)
        {
            if (!$set) {
                return [[]];
            }
    
            $subset = array_shift($set);
            $cartesianSubset = self::build($set);
    
            $result = [];
            foreach ($subset as $value)
            {
                foreach ($cartesianSubset as $p)
                {
                    array_unshift($p, $value);
                    $result[] = $p;
                }
            }
    
            return $result;
        }
    }
    
    // Input set - can by any number of "rows"
    $input = [
        ['Old', 'Young', 'Child'],
        ['neutral', 'happy', 'sad']
    ];
    
    // Build our total set of permutations
    $cartesianProduct = Cartesian::build($input);
    
    // Desired size of output array
    $limit = 30;
    
    // Buffer for the final output
    $output = [];
    
    // Start at a random point and keep track of where we are in the set of possible combinations
    $cartIndex = array_rand($cartesianProduct);
    
    
    // Loop until we have the desired buffer size
    while (sizeof($output) < $limit)
    {
        // Get the current input and increment the input index
        $currProduct = $cartesianProduct[$cartIndex++];
        
        // Add a string representation of the current entry to the output 
        $output[] = $currProduct[0].' '.$currProduct[1];
    
        // If we have reached the end of the input buffer, reset to zero
        if ($cartIndex == sizeof($cartesianProduct))
        {
            $cartIndex = 0;
        }
    }
    
    // Randomize the output buffer
    shuffle($output);
    
    // Let's look at the results...
    print_r($output);
    
    Login or Signup to reply.
  2. Populate your cartesian product, then loop as many times as needed and grab the the next element in the shuffled array using circular index access (via modulus calculation).

    Code: (Demo)

    $persons = ["Old", "Young", "Child"];
    $moods = ["neutral", "happy", "sad"];
    $size = 30;
    
    $cartesianStrings = [];
    foreach($persons as $p) {
        foreach($moods as $m) {
            $cartesianStrings[] = $p . ' ' . $m;
        }
    }
    
    shuffle($cartesianStrings);
    $cartesianSize = count($cartesianStrings);
    for ($x = 0; $x <= $size; ++$x) {
        $result[] = '|' . $cartesianStrings[$x % $cartesianSize] . '|';
    }
    shuffle($result);
    echo implode($result);
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search