【问题标题】:PHP-How To Pair Up items in Array based on conditionPHP-如何根据条件对数组中的项目进行配对
【发布时间】:2012-02-11 10:48:59
【问题描述】:

如何将数组中的项目配对? 假设我有一组 Fighters。我想根据它们的权重对它们进行配对。重量最接近的选手应作为最佳匹配配对。但如果他们在同一个团队中,则不应配对

  • **---第 1 队--**
  • 战斗机 A 重量为 60
  • 战斗机 B 重量为 65
  • **--第 2 队--**
  • 战斗机 C 重量为 62
  • 战斗机 D 重量为 60
  • **--第 3 队--**
  • 战斗机 E 重量为 64
  • 战斗机 F 重量为 66

输出:

  • 战斗机 A VS 战斗机 D
  • 战斗机 B VS 战斗机 F
  • 战斗机 C VS 战斗机 E

我一直在研究这个主题,发现了一些类似但不完全的东西: Random But Unique Pairings, with Conditions

非常感谢一些帮助。提前致谢!

【问题讨论】:

    标签: php arrays


    【解决方案1】:

    我非常喜欢你的问题,所以我制作了一个完整的强大版本。

    <?php
    
        header("Content-type: text/plain");
        error_reporting(E_ALL);
    
        /**
         * @class            Fighter
         * @property $name   string
         * @property $weight int
         * @property $team   string
         * @property $paired Fighter  Will hold the pointer to the matched Fighter
         */
        class Fighter {
            public $name;
            public $weight;
            public $team;
            public $paired = null;
    
            public function __construct($name, $weight, $team) {
                $this->name   = $name;
                $this->weight = $weight;
                $this->team   = $team;
            }
        }
    
        /**
         * @function sortFighters()
         *
         * @param $a Fighter
         * @param $b Fighter
         *
         * @return int
         */
        function sortFighters(Fighter $a, Fighter $b) {
            return $a->weight - $b->weight;
        }
    
        $fighterList = array(
            new Fighter("A", 60, "A"),
            new Fighter("B", 65, "A"),
            new Fighter("C", 62, "B"),
            new Fighter("D", 60, "B"),
            new Fighter("E", 64, "C"),
            new Fighter("F", 66, "C")
        );
        usort($fighterList, "sortFighters");
    
        foreach ($fighterList as $fighterOne) {
            if ($fighterOne->paired != null) {
                continue;
            }
            echo "Fighter $fighterOne->name vs ";
            foreach ($fighterList as $fighterTwo) {
                if ($fighterOne->team != $fighterTwo->team && $fighterTwo->paired == null) {
                    echo $fighterTwo->name . PHP_EOL;
                    $fighterOne->paired = $fighterTwo;
                    $fighterTwo->paired = $fighterOne;
                    break;
                }
            }
    
        }
    
    1. 首先,战士被保存在类中,这样可以更轻松地为它们分配属性(如果您自己还没有这样做,我敦促您这样做!)
    2. 制作一组战士,并为他们指定名称、重量和团队。
    3. 按权重对数组进行排序(使用usort()和排序函数sortFighters()按每个元素的权重属性排序。
    4. 遍历数组并匹配基于:
      1. 一号战斗机尚未匹配
      2. 战斗机 2 与战斗机 1 不在同一队
      3. 战斗机 2 尚未匹配
    5. 找到匹配后,将每个匹配的战士的对象指针相互存储(因此不再为空,另外您可以通过转到$fighterVariable-&gt;paired访问每个战士对)
    6. 最后,打印结果。

    【讨论】:

    • 不错的答案!你的比较函数可以简化为return $a-&gt;weight - $b-&gt;weight
    • 不错且有教育意义的答案 (+1),但是:我会将 sortFithers 定义更改为 function sortFighters(Fighter $a, Fighter $b)(因此它非常强大),而不是在比较功能体中您拥有 $a-&gt;weight - $b-&gt;weight1;(删除 1 ) 如果您将这两条记录:new Fighter("G", 90, "M"), new Fighter("H", 91, "M") 添加到数组的末尾,您将得到以下结果:Fighter G vs Fighter H vs 您需要实现某种反向传播或类似的东西。
    • 我忘记了对象类型提示!你是对的!我已经添加了,谢谢!
    • 哇!非常感谢你们!特别是@Truth ,还没有尝试过,但看起来真的很好。谢谢!
    • 听到一个后续问题。如何动态设置 $fighterList 的值?还是来自数据库?我真的不习惯这种编码。谢谢!
    【解决方案2】:

    这只是基于 cmets 的 Truth's 答案的扩展:

    我要做的第一件事是基本保持跟踪播放器。

    $unassignedPlayers = $fighterList;
    

    算法会起作用:准备团队列表(如果您使用数据库,请使用SELECT DISTINCTGROUP BY teams.id):

    $teams = array();
    foreach( $fighterList as $fighter){
        $teams[] = $figter->team;
    }
    $teams = array_unique( $teams);
    

    接下来,我们需要拆分战斗机数组的方法(假设我们有团队 {A,A,B,B,C,C},我们想将其拆分为 {A,A}{B,B,C,C}):

    // Don't use string type declaration, it's just ilustrating
    function splitFighters( array $input, string $team){
        $inteam = array();
        $outteam = array();
        foreach( $input as $fighter){
            if( $figter->team == $team){
               $inteam[] = $fighter;
            } else {
               $outteam[] = $fighter;
            }
        }
    
        return array( $inteam, $outteam);
    }
    

    现在我们已经有了,我们可以创建对团队成员进行排序的函数:

    function assignFighters( array &$input, array $teams, array &$output){
        // Nothing to work with?
        if( !count( $input)){
            return true;
        }
    
        // No team left and still unassigned players, that fatal error
        if( !cont( $teams)){
            throw new Exception( 'Unassigned players occurred!');
        }
    
        // Shift team
        $team = array_shift( $teams);
    
        // Split into in and out team
        list( $inteam, $outteam) = splitFighters( $input, $team);
    
        // Inteam is already empty (let's say all players were assigned before)
        // Just go deeper (where's DiCaprio?)
        if( !count( $inteam) && count( $teams)) {
            return assignFighters( $input, $teams, $output)
        }
    
        // There're unassigned and nonassignable players in this team
        // This is error and we'll have to deal with it later
        if( !count($outteam)){
            $input = $inteam; // Propagate unassigned players to main
            return false;
        }
    
        // Sort both teams by fighters weight
        // Uses Truth's comparison function
        usort($inteam, "sortFighters");
        usort($outteam, "sortFighters");
    
        // Fig = Fighter
        while( $fig1 = array_shift( $inteam)){
             // Are there any players to work with
             if( !count( $outteam)){
                 array_unshift( $inteam, $fig1);
                 $input = $inteam; // Propagate unassigned players to main
                 return false;
             }
    
             // Assign players to each other
             $fig2 = array_shift( $outteam);
             $fig1->paired = $fig2;
             $fig2->paired = $fig1;
    
             // This will propagate players to main nicely
             $output[] = $fig1;
             $output[] = $fig2;
        }
    
        // Still here? Great! $inteam is empty now
        // $outteam contains all remaining players
        $input = $outteam;
    
        return assignFighters( $input, $teams,$output);
    }
    

    到目前为止,您可以使用 Truth 的算法,但这应该有更好的权重匹配,并且更清楚地代表您的意图,但无论如何现在 $unassignedPlayers 开始工作了:

    $assignedPlayers = array();
    $state = assignFighters( $unassignedPlayers, $teams, $assignedPlayers);
    
    // Note:
    $state === !(bool)count($unassignedPlayers)
    // should evaluate as true, otherwise I'm having an error in algorithm
    

    那现在怎么办...如果您有$state === false resp。 count( $unassignedPlayers) &gt; 0 出了点问题,我们需要应用一些魔法。这种魔法将如何发挥作用:

    // Keep the trace of swapped players so we don't end up in endless loop
    $swappedPlayers = array();
    
    // Browse all unassigned players
    while( $fig1 = array_shift( $unassignedPlayers)){
        // Store as swapped
        $swappedPlayers[] = $fig1;
    
        // At first check whether there's not unassigned player in the different team
        // this shouldn't occur in first iteration (all fighters should be from one team
        // in the beginning) but this is most effective part of this method
        foreach( $unassignedPlayers as $key => $fig2){
           if( $fig2->team != $fig1->team){
                $fig1->pair = $fig2;
                $fig2->pair = $fig1;
                continue;
           }
        }
    
        // No luck, normal magic required
        list( $inteam, $outteam) = splitFighters( $assignedPlayers, $fig1->team);
    
        $fig2 = null; // I like my variables initialized, this actually quite important
    
        // Now select someone from $outteam you will want to swap fights with.
        // You may either iterate trough all players until you find best weight
        // match or select it random, or whatever, I'll go with random,
        // it's your job to implement better selection
        $i = 1000;  // Limit iterations
        while(($i--) > 1){
           $key1 = array_rand( $outteam, 1);
           if( $outteam[$key]->team == $fig1->team){
              continue; // No point in swapping fit team member
           }
    
           // No recursive swaps
           if( in_array( $outteam[$key], $swappedPlayers)){
              continue;
           }
    
           // This may speed things really up:
           // That means we'll get rid of 2 players due to foreach loop at the beggining
           // However I'm not sure how this condition will really work
           if( $outteam[$key]->pair->team == $fig1->team){
              continue;
           }
    
           // Store matched fighter
           $fig2 = $outteam[$key];
    
           // Unset pair from another fighter
           $fig2->pair->pair = null;
    
           // Find the pair in $assignedPlayers and move it to $unassignedPlayers
           $key = array_search( $fig2->pair, $assignedPlayers);
           if( $key === false){
               throw new Exception( 'Cannot find pair player');
           }
           unset( $assignedPlayers[$key]);
           $unassignedPlayers[] = $fig2->pair;
           $swappedPlayers[] = $fig2->pair;
    
           // Remove pair from self
           $fig2->pair = null;
           $swappedPlayers[] = $fig2;
           break; // hh, try forgetting this one :)
        }
    
        // This shouldn't be happening
        if( $fig2 === null){
             throw new Exception( 'Didn\'t find good match in 1000 iterations.');
        }
    
        // Ok now just make matches as go to the next iteration
        $fig1->pair = $fig2;
        $fig2->pair = $fig1;
    
        // And store those
        $assignedPlayers[] = $fig1;
        $assignedPlayers[] = $fig2;
    }
    

    我已经把所有这些都写在了我的脑海中(这是一个挑战),请对其进行测试并在 cmets 中留下笔记:)

    【讨论】:

    • 嗨@Vyktor,让我试试这个。谢谢!
    • 嘿@Vyktor,也许您可​​以将代码作为一个整体发布?我似乎无法理解这种类型的编码。对不起,因为我真的筋疲力尽而且我不习惯 OOP。谢谢!
    • 伙计,你做这一切真是太酷了!对此的支持。有一个错误:可捕获的致命错误:传递给 assignFighters() 的参数 1 必须是一个数组,给定 null,在第 127 行调用...
    • 你好@Vyktor,错误现在消失了,但它没有显示输出。
    【解决方案3】:

    按权重对数组进行排序。然后,您将拥有一对彼此接近的权重。

    【讨论】:

    • 只有这样并不能保证他们不会在同一个团队中。
    • 使用上述方法后,您可以使用新的查询来确保他们不在同一个团队中。
    • 其实已经试过了。但这还不足以满足我的要求。
    猜你喜欢
    • 2019-09-01
    • 1970-01-01
    • 2019-09-04
    • 2013-09-30
    • 2022-11-19
    • 2021-03-08
    • 1970-01-01
    • 1970-01-01
    • 2022-08-15
    相关资源
    最近更新 更多