PHP-如何根据条件配对数组中的项目

时间:2012-02-11 10:48:59

标签: php arrays

如何配对数组中的项目? 让我们说我有一系列战士。我想根据他们的权重来配对它们。最接近重量的战士应配对为最佳匹配。但如果他们在同一个团队中他们就不应该配对

     
  • ** ---第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

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

3 个答案:

答案 0 :(得分:5)

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

<?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()按每个元素的weight属性排序。
  4. 遍历数组并匹配基于:
    1. 战斗机1尚未匹配
    2. 战斗机2与Fighter one不在同一个团队
    3. 战斗机2尚未匹配
  5. 当找到匹配项时,将每个匹配战士的对象指针相互存储(因此它不再为空,加上你可以通过$fighterVariable->paired访问每个战士的对)
  6. 最后,打印结果。

答案 1 :(得分:2)

这只是基于评论的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) > 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;
}

我已经把所有这些都写下来了(这是挑战),测试它并在评论中留下注释:)

答案 2 :(得分:0)

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