Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/asp.net-core/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Php 消除不可能的选择_Php_Combinations_Combinatorics - Fatal编程技术网

Php 消除不可能的选择

Php 消除不可能的选择,php,combinations,combinatorics,Php,Combinations,Combinatorics,我只是想用编程的方式来解决这个问题,有点麻烦。这不完全是我正在做的,但是为了简化事情,假设我们有一定数量的球和一定数量的人。每个人必须只选择一个球,而且人们可能会被限制在他们可以选择的球的类型上。目标是确定在消除所有不可能的组合后,人们必须从中选择什么选项 例1: 在一个简单的例子中,假设我们有两个人,一个红球和一个绿球。第一个人可以选择任意一个球,但是第二个人只能选择绿色的球。这可以如下所示: Person 1: RG Person 2: G 因为我们知道人2必须选择绿色的球,这意味着人1不

我只是想用编程的方式来解决这个问题,有点麻烦。这不完全是我正在做的,但是为了简化事情,假设我们有一定数量的球和一定数量的人。每个人必须只选择一个球,而且人们可能会被限制在他们可以选择的球的类型上。目标是确定在消除所有不可能的组合后,人们必须从中选择什么选项

例1: 在一个简单的例子中,假设我们有两个人,一个红球和一个绿球。第一个人可以选择任意一个球,但是第二个人只能选择绿色的球。这可以如下所示:

Person 1: RG
Person 2: G
因为我们知道人2必须选择绿色的球,这意味着人1不能选择那个球,因此必须选择红色的球。因此,这可以简化为:

Person 1: R
Person 2: G
所以在这种情况下,我们确切地知道每个人会选择什么

例2: 现在让我们增加一点复杂性。现在我们有4个人需要从2个红球、1个绿球和4个蓝球中进行选择。第一人可以选择任何球,第二人和第三人可以选择红色或绿色球,第四人必须选择红色球。因此,我们有以下选择:

Person 1: RRGBBBB
Person 2: RRG
Person 3: RRG
Person 4: RR
因为第四个人只有一种选择,我们知道他必须选择一个红色的球。因此,我们可以从所有其他人身上消除1个红球:

Person 1: RGBBBB
Person 2: RG
Person 3: RG
Person 4: RR
然而,这正是它变得非常棘手的地方。正如我们所看到的,第二人和第三人必须选择一个红色和一个绿色的球,我们只是不知道谁会选择哪个。但是,由于每个球只剩下1个,红色和绿色也可以作为选项从第1人中删除:

Person 1: BBBB
Person 2: RG
Person 3: RG
Person 4: RR
现在,我们可以使用以下选项从每个条目中删除重复项:

Person 1: B
Person 2: RG
Person 3: RG
Person 4: R
我们知道人1和人4的选择,人2和人3可以选择红色和绿色


为了解决这个问题,我要做的是在人身上循环,首先如果人只有一个球类型,从数组中移除那个人并将其放入结果数组(因为我知道这是那个人必须选择的),然后从数组中的其他人身上移除一个球类型(如果存在)

在这之后,我觉得规则是:

如果有
$n
人数相同的
$n
人数 选项(或其子集),可以删除这些选项 从所有其他人中,
$n
小于总人数

因此,我要做的是再次遍历这些人,并检查是否有其他人具有相同的可用选项(或这些选项的子集),如果这等于该人的选项总数,则将其从所有其他人的选项中删除

到目前为止,我已经掌握了解决这两个问题的方法:

例3: 此示例不适用于上述代码。假设有一个红色的球,一个绿色的球,一个蓝色的球,一个黄色的球和4个人。第一个人可以选择任何球,第二个人可以选择红色或绿色,第三个人可以选择绿色或蓝色,第四个人可以选择红色或蓝色

从视觉上看,这看起来像:

Person 1: RGBY
Person 2: RG
Person 3: GB
Person 4: RB
由于3种颜色红色、绿色和蓝色是2号、3号和4号人员的唯一选择,它们完全包含在3号人员中,因此可以从1号人员中删除,这意味着1号人员必须选择黄色。如果第一个人选择黄色以外的任何东西,其他人就不可能选择他们的球

将其放入我的PHP程序中,我有以下输入值:

// 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'),
);

然而,我想不出如何通过循环找到这样的案例,而不迭代每个可能的人员组合。你知道怎么做吗?

我更喜欢一种类似面向对象的方法。所以我基本上是从零开始的。我希望你能接受

因此,以下(无可否认)看起来很难看,除了你的三个例子之外,我还没有用任何东西对其进行过测试,但它是这样的:

class Ball {
    private $color;
    public function __construct($color) {
        $this->color = $color;
    }
    public function getColor() {
        return $this->color;
    }
}
class Ball_resource extends Ball {
    private $num_available;

    public function __construct($color, $number) {
        parent::__construct($color);
        $this->num_available = $number;
    }
    public function take() {
        $this->num_available--;
    }

    public function isExhausted() {
        return $this->num_available <= 0;
    }
}
class Person {
    /**
     *
     * @var Ball
     */
    private $allowed_balls = array();

    public function addConstraint($color) {
        $this->allowed_balls[$color] = new Ball($color);
        return $this;
    }
    public function getConstraints() {
        return $this->allowed_balls;
    }

    public function getNumberOfConstraints() {
        return count($this->allowed_balls);
    }

    /**
     * return true if removal was successful; false otherwise
     */
    public function removeConstraint(Ball $ball) { // todo remove
        if (isset ($this->allowed_balls [$ball->getColor()])) {
            unset ($this->allowed_balls [$ball->getColor()]);
            return true;
        }
        else {
            // this means our puzzle isn't solvable
            return false;
        }
    }
}
class Simplifier {
    /**
     *
     * @var Person
     */
    private $persons = array ();
    /**
     *
     * @var Ball_resource
     */
    private $availableBalls = array ();

    public function addPerson(Person $person) {
        $this->persons[] = $person;
        return $this;
    }
    public function addBallRessource(Ball_resource $ball_resource) {
        $this->availableBalls[] = $ball_resource;
        return $this;
    }


    public function getChoices() {      
        $queue = $this->persons; // shallow copy

        while (count($queue) > 0) {
            // find most constrained person(s)
            $numberOfConstraints = 1; // each person must have at least one constraint
            while (true) {
                $resolve_queue = array();
                foreach($queue as $person) {
                    if ($person->getNumberOfConstraints() === $numberOfConstraints) {
                        $resolve_queue[] = $person;
                    }
                }
                // find mutually dependent constraint groups connected with a person
                $first_run = true;
                foreach ($resolve_queue as $startPerson) {
                    // check if we havent already been removed via dependencies
                    if ($first_run || !self::contains($queue, $startPerson)) {
                        $dependent_persons = $this->findMutuallyDependentPersons($startPerson, $resolve_queue);
                        // make a set out of their combined constraints
                        $combinedConstraints = $this->getConstraintsSet($dependent_persons);
                        $this->adjustResources($dependent_persons);
                        $this->removeFromQueue($dependent_persons, $queue);
                        // substract each ball of this set from all less constrained persons
                        $this->substractConstraintsFromLessConstrainedPersons($queue, $combinedConstraints, $numberOfConstraints);
                        $first_run = false;
                        continue 3;
                    }
                }
                $numberOfConstraints++;
            }

        }
        return $this->persons; // has been altered implicitly   
    }

    private static function contains(array $haystack, Person $needle) {
        foreach ($haystack as $person) {
            if ($person === $needle) return true;
        }
        return false;
    }

    private function findMutuallyDependentPersons(Person $startPerson, array $persons) {
        // add recursion
        $output = array();
        //$output[] = $startPerson;
        foreach($persons as $person) {
            foreach ( $person->getConstraints () as $constraint ) {
                foreach ( $startPerson->getConstraints () as $targetConstraint ) {
                    if ($constraint->getColor () === $targetConstraint->getColor ()) {
                        $output [] = $person;
                        continue 3;
                    }
                }
            }   
        }
        return $output;
    }

    private function getConstraintsSet(array $persons) {
        $output = array();
        foreach ($persons as $person) {
            foreach ($person->getConstraints() as $constraint) {
                foreach($output as $savedConstraint) {
                    if ($savedConstraint->getColor() === $constraint->getColor()) continue 2;                   
                }
                $output[] = $constraint;
            }
        }
        return $output;
    }

    private function substractConstraintsFromLessConstrainedPersons(array $persons, array $constraints, $constraintThreshold) {
        foreach ($persons as $person) {
            if ($person->getNumberOfConstraints() > $constraintThreshold) {
                foreach($constraints as $constraint) {
                    foreach($this->availableBalls as $availableBall) {
                        if ($availableBall->isExhausted()) {
                            $person->removeConstraint($constraint);
                        }
                    }                   
                }
            }
        }
    }

    private function adjustResources(array $persons) {
        foreach($persons as $person) {
            foreach($person->getConstraints() as $constraint) {
                foreach($this->availableBalls as &$availableBall) {
                    if ($availableBall->getColor() === $constraint->getColor()) {
                        $availableBall->take();
                    }
                }
            }
        }
    }

    private function removeFromQueue(array $persons, array &$queue) {       
        foreach ($persons as $person) {
            foreach ($queue as $key => &$availablePerson) {
                if ($availablePerson === $person) {
                    unset($queue[$key]);
                }
            }
        }
    }
}
同样,例如3:

// Example 3
{
    $person1 = new Person();
    $person1->addConstraint("R")->addConstraint("G")->addConstraint("B")->addConstraint("Y");
    $person2 = new Person();
    $person2->addConstraint("R")->addConstraint("G");
    $person3 = new Person();
    $person3->addConstraint("G")->addConstraint("B");
    $person4 = new Person();
    $person4->addConstraint("R")->addConstraint("B");

    $redBalls = new Ball_resource("R", 1);
    $greenBalls = new Ball_resource("G", 1);
    $blueBalls = new Ball_resource("B", 1);
    $yellowBalls = new Ball_resource("Y", 1);   

    $simplifier = new Simplifier();
    $simplifier->addPerson($person1)->addPerson($person2)->addPerson($person3)->addPerson($person4);
    $simplifier->addBallRessource($redBalls)->addBallRessource($greenBalls)->addBallRessource($blueBalls)->addBallRessource($yellowBalls);
    $output = $simplifier->getChoices();

    print_r($output);
}
为了简洁起见,我省略了原始输出。但对于第二个例子,它基本上等于你在问题中的最后一个列表,例如3,它产生了以下等价物:

Person 1: Y
Person 2: RG
Person 3: GB
Person 4: RB

我最终决定做的是一个半暴力的力量,但是我调整了它,这样它就不必经历每一个组合,所以在大多数情况下,迭代次数应该比每一个组合都少

组合的总数等于exp(2,count(balls))(即2^{types of balls}),因此如果我们有20种类型的球,那就是1048576种不同的球组合,如果我们要检查每种球,我们必须检查它们。不仅是太多的迭代,我实际上已经用光了之前只有16种球颜色的内存

要获得每个可能的组合,可以使用以下函数():

但是,如果我们回到原来的规则:

如果有$n个用户拥有相同的$n个选项(或其子集),则可以从所有其他用户中删除这些选项,其中$n小于用户总数

正如我们所看到的,如果我们已经超过了
$n
的选项数量,我们可以跳过任何未来的迭代。请注意,当我们有多个相同颜色的球时,在这个函数中它被视为多个球

一旦我们得到了不同的可能子集,我们就对这些人进行循环,以查看是否有
$n
不同的人使用该子集,并从所有其他人中删除这些值。这就是我最后想到的:

/**
 * This just gets a sum of the number of balls a person can choose
 * from
 */
function getNumberOfOptions($person, $balls) {
    $n = 0;
    foreach ($person as $allowed_ball) {
        $n += $balls[$allowed_ball];
    }
    return $n;
}

/**
 * This function finds all the subsets of the balls array that we can look for
 * in the people array later to remove options from them
 */
function getBallSubsets($balls, $max_n) {
    // initialize by adding the empty set
    $results = array(array( ));

    // Remove all balls that have more options than max_n
    foreach ($balls as $color => $number) {
        if ($number > $max_n) {
            unset($balls[$color]);
        }
    }

//    $n = 0;
    foreach ($balls as $color => $number) {
        foreach ($results as $combination) {
//            $n++;
            $set = array_merge(array($color), $combination);
            if (getNumberOfOptions($set, $balls) <= $max_n) {
                $results[] = $set;
            }
        }
    }

//    echo $n; exit;  
    return $results;
}

function removeFromOthers($colors, $people, $skip_indexes) {
    foreach ($people as $p_index => $person) {
        if (array_search($p_index, $skip_indexes) === false) {
            foreach ($colors as $color) {
                $index = array_search($color, $person);
                if ($index !== false) {
                    unset($people[$p_index][$index]);
                }
            }
        }
    }
    return $people;
}

function getOptions($people, $balls) {
    $ball_subsets = getBallSubsets($balls, count($people) -1);

    foreach ($ball_subsets as $sub) {
        $num_colors = count($sub);
        $num_people = getNumberOfOptions($sub, $balls);

        // Loop through people and if we find $n people with only the elements
        // from this subset, we can remove these elements from all other people
        $found = [];
        $found_colors = [];
        foreach ($people as $p_index => $person_arr) {
            foreach ($person_arr as $color) {
                // Contains another color, so skip this one
                if (array_search($color, $sub) === false) {
                    continue 2;
                }
            }
            $found[] = $p_index;
            foreach ($person_arr as $color) {
                if (array_search($color, $found_colors) === false) {
                    $found_colors[] = $color;
                }
            }
        }
        if (count($found) === $num_people && count($found_colors) == $num_colors) {
            $people = removeFromOthers($sub, $people, $found);
        }
        else {
            unset($found);
        }
    }
    return $people;
}

// The quantity of each ball
$balls = array(
    'r' => 3,
    'g' => 2,
    'b' => 1,
    'k' => 16,
);
// The options available for each person
$people = array(
    array('r', 'g', 'b', 'k'),
    array('r', 'g'),
    array('r', 'b'),
    array('b', 'g'),
);

print_r($people);
$options = getOptions($people, $balls);
print_r($options);
/**
*这只是一个人可以选择的球数的总和
*从
*/
函数getNumberOfOptions($person,$balls){
$n=0;
foreach($person as$allowed\u ball){
$n+=$balls[$allowed_ball];
}
返回$n;
}
/**
*此函数用于查找balls数组中我们可以查找的所有子集
// Example 3
{
    $person1 = new Person();
    $person1->addConstraint("R")->addConstraint("G")->addConstraint("B")->addConstraint("Y");
    $person2 = new Person();
    $person2->addConstraint("R")->addConstraint("G");
    $person3 = new Person();
    $person3->addConstraint("G")->addConstraint("B");
    $person4 = new Person();
    $person4->addConstraint("R")->addConstraint("B");

    $redBalls = new Ball_resource("R", 1);
    $greenBalls = new Ball_resource("G", 1);
    $blueBalls = new Ball_resource("B", 1);
    $yellowBalls = new Ball_resource("Y", 1);   

    $simplifier = new Simplifier();
    $simplifier->addPerson($person1)->addPerson($person2)->addPerson($person3)->addPerson($person4);
    $simplifier->addBallRessource($redBalls)->addBallRessource($greenBalls)->addBallRessource($blueBalls)->addBallRessource($yellowBalls);
    $output = $simplifier->getChoices();

    print_r($output);
}
Person 1: Y
Person 2: RG
Person 3: GB
Person 4: RB
function getAllCombinations($balls) {
    // initialize by adding the empty set
    $results = array(array( ));

    foreach ($balls as $color => $number) {
        foreach ($results as $combination) {
            $results[] = array_merge(array($color), $combination);
        }
    }

    return $results;
}
/**
 * This just gets a sum of the number of balls a person can choose
 * from
 */
function getNumberOfOptions($person, $balls) {
    $n = 0;
    foreach ($person as $allowed_ball) {
        $n += $balls[$allowed_ball];
    }
    return $n;
}

/**
 * This function finds all the subsets of the balls array that we can look for
 * in the people array later to remove options from them
 */
function getBallSubsets($balls, $max_n) {
    // initialize by adding the empty set
    $results = array(array( ));

    // Remove all balls that have more options than max_n
    foreach ($balls as $color => $number) {
        if ($number > $max_n) {
            unset($balls[$color]);
        }
    }

//    $n = 0;
    foreach ($balls as $color => $number) {
        foreach ($results as $combination) {
//            $n++;
            $set = array_merge(array($color), $combination);
            if (getNumberOfOptions($set, $balls) <= $max_n) {
                $results[] = $set;
            }
        }
    }

//    echo $n; exit;  
    return $results;
}

function removeFromOthers($colors, $people, $skip_indexes) {
    foreach ($people as $p_index => $person) {
        if (array_search($p_index, $skip_indexes) === false) {
            foreach ($colors as $color) {
                $index = array_search($color, $person);
                if ($index !== false) {
                    unset($people[$p_index][$index]);
                }
            }
        }
    }
    return $people;
}

function getOptions($people, $balls) {
    $ball_subsets = getBallSubsets($balls, count($people) -1);

    foreach ($ball_subsets as $sub) {
        $num_colors = count($sub);
        $num_people = getNumberOfOptions($sub, $balls);

        // Loop through people and if we find $n people with only the elements
        // from this subset, we can remove these elements from all other people
        $found = [];
        $found_colors = [];
        foreach ($people as $p_index => $person_arr) {
            foreach ($person_arr as $color) {
                // Contains another color, so skip this one
                if (array_search($color, $sub) === false) {
                    continue 2;
                }
            }
            $found[] = $p_index;
            foreach ($person_arr as $color) {
                if (array_search($color, $found_colors) === false) {
                    $found_colors[] = $color;
                }
            }
        }
        if (count($found) === $num_people && count($found_colors) == $num_colors) {
            $people = removeFromOthers($sub, $people, $found);
        }
        else {
            unset($found);
        }
    }
    return $people;
}

// The quantity of each ball
$balls = array(
    'r' => 3,
    'g' => 2,
    'b' => 1,
    'k' => 16,
);
// The options available for each person
$people = array(
    array('r', 'g', 'b', 'k'),
    array('r', 'g'),
    array('r', 'b'),
    array('b', 'g'),
);

print_r($people);
$options = getOptions($people, $balls);
print_r($options);