I have a few problems just trying to plunge into this problem programmatically. This is not exactly what I am doing, but to simplify things, let's say we have a certain number of balls and a certain number of people. Each person must choose exactly one ball, and people can be limited by the type of ball that they can choose. The goal is to determine what options people choose after eliminating all the impossible combinations.
Example 1:
In the simple case, let's say we have two people, one red ball and one green ball. Person 1 can choose any ball, but person 2 can select only the green ball. This can be illustrated as follows:
Person 1: RG Person 2: G
Since we know that person 2 must select the green ball, this means that person 1 cannot select this ball and therefore must select the red ball. Therefore, this can be simplified:
Person 1: R Person 2: G
So, in this case, we know for sure what everyone will choose.
Example 2:
Now let's add a little complexity. Now we have 4 people who need to select 2 red balls, 1 green ball and 4 blue balls. Person 1 can choose any ball, person 2 and 3 can choose red or green balls, and person 4 must choose a red ball. Thus, we have the following options:
Person 1: RRGBBBB Person 2: RRG Person 3: RRG Person 4: RR
Since person 4 has only one option, we know that a person must choose a red ball. Therefore, we can exclude 1 red ball from all other people:
Person 1: RGBBBB Person 2: RG Person 3: RG Person 4: RR
However, it is here that it becomes very complex. As we see, people 2 and 3 must choose one red and one green ball, we just donβt know which one will choose. However, since we only have one of the remaining balls left, red and green can also be excluded as an option from person 1:
Person 1: BBBB Person 2: RG Person 3: RG Person 4: RR
Now we can remove duplicates from each record left with the following parameters:
Person 1: B Person 2: RG Person 3: RG Person 4: R
We know the choice of person 1 and person 4, and person 2 and 3 have a choice between red and green.
To solve this problem, I loop over people, and first, if people have only one type of ball, remove that person from the array and put it in the results array (since I know what exactly this person should choose), and then delete one of this type of ball is every other person in the array, if any.
After that, it seems to me that the rule:
If there is $n number of people who all have the same number $n (or a subset of them), these parameters can be removed from all other people where $n less than the total number of people.
So, what I am doing is sorting out people again and checking out other people having the same parameters (or a subset of these options), and if this is equal to the total number of options for this person, remove them from the options for all other people.
Here is what I have so far to solve these two cases:
// The quantity of each ball $balls = array( 'r' => 1, 'g' => 1, 'b' => 1, 'k' => 1, ); // The options available for each person $options = array( array('r', 'g', 'b', 'k'), array('r', 'g'), array('r', 'b'), array('b', 'g'), ); // Put both of these together into one array $people = []; foreach ($options as $option) { $person = []; foreach ($option as $ball_key) { $person[$ball_key] = $balls[$ball_key]; } $people[] = $person; } print_r($people); // This produces an array like: // Array // ( // [0] => Array // ( // [r] => 2 // [g] => 1 // [b] => 4 // ) // // [1] => Array // ( // [r] => 2 // [g] => 1 // ) // // [2] => Array // ( // [r] => 2 // [g] => 1 // ) // // [3] => Array // ( // [r] => 2 // ) // // ) // This will be used to hold the final result $results = []; do { // If anything changes, this needs to be set to true. Any time anything // changes we loop through everything again in case it caused a cascading // effect $has_change = false; // Step 1: // Find out if there are any people who have only one option and remove it // from the array and add it to the result and subtract one from all other // people with this option foreach ($people as $p_index => $p_options) { if (count($p_options) === 1) { $color = key($p_options); foreach ($people as $p_index_tmp => $p_options_tmp) { // It the current person, so skip it if ($p_index_tmp === $p_index) { continue; } if (isset($p_options_tmp[$color])) { // Subtract 1 from this color from this person and if zero, // remove it. if (--$people[$p_index_tmp][$color] === 0) { unset($people[$p_index_tmp][$color]); } $has_change = true; } } // Add to results array and remove from people array $results[$p_index] = array($color => 1); unset($people[$p_index]); } } // Step 2: // If there are $n number of people who all have the same $n number of // options (or a subset of them), these options can be removed // from all other people, where $n is less than the total number of people foreach ($people as $p_index => $p_options) { $num_options = array_sum($p_options); if ($num_options < count($people)) { // Look for other people with no different options from the ones // that this person has $people_with_same_options = []; foreach ($people as $p_index_tmp => $p_options_tmp) { foreach (array_keys($p_options_tmp) as $color) { if (array_search($color, array_keys($p_options)) === false) { // This color was not found in the options, so we can // skip this person. // (Make sure we break out of both foreach loops) continue 2; } } // This person has all the same options, so append to the array $people_with_same_options[] = $p_index_tmp; } // Remove these options from the other people if the number of // people with only these options is equal to the number of options if (count($people_with_same_options) === $num_options) { foreach ($people as $p_index_tmp => $p_options_tmp) { if (array_search($p_index_tmp, $people_with_same_options) === false) { foreach (array_keys($p_options) as $option) { unset($people[$p_index_tmp][$option]); $has_change = true; } } } } } } } while ($has_change === true); // Combine any remaining people into the result and sort it $results = $results + $people; ksort($results); print_r($results);
This leads to the following result:
Array ( [0] => Array ( [b] => 1 ) [1] => Array ( [r] => 1 [g] => 1 ) [2] => Array ( [r] => 1 [g] => 1 ) [3] => Array ( [r] => 1 ) )
Example 3:
This example does not work with the above code. Suppose there is 1 red ball, 1 green ball, 1 blue ball, one yellow ball and 4 people. Person 1 can choose any ball, person 2 can choose red or green, person 3 can choose green or blue, person 4 can choose red or blue.
Visually, it will look like this:
Person 1: RGBY Person 2: RG Person 3: GB Person 4: RB
Since 3 colors of red, green and blue are the only options for faces 2, 3 and 4, they are completely contained within these 3 people, and therefore they can be eliminated from person 1, that is, person 1 must choose yellow, If person 1 had to choose anything other than yellow, it would be impossible for other people to pick their eggs.
By introducing it into my PHP program, I have these input values:
// The quantity of each ball $balls = array( 'r' => 1, 'g' => 1, 'b' => 1, 'y' => 1, ); // The options available for each person $options = array( array('r', 'g', 'b', 'y'), array('r', 'g'), array('r', 'b'), array('b', 'g'), );
However, I cannot figure out how to go through the cycle to find such cases without repeating every possible combination of people. Any ideas how to do this?