将PHP数组拆分为两个相等的集合,其中值的总和等于

时间:2016-10-26 21:58:56

标签: php arrays sorting sum

我有以下阵列的球员和他们的积分。我想把它们分成两个相等的小组,其中点的总和应尽可能相等。

E.g输出应为:

团队a =玩家ID为505,481,510,总分为6

球队b =球员ID 504,509,513,积分总数为6

请帮助我指出如何实现这个目标的正确方向?

谢谢

Array
(
    [0] => Array
        (
            [match_id] => 664
            [0] => 664
            [player_id] => 505
            [1] => 505
            [Points] => 4
            [2] => 4
        )

    [1] => Array
        (
            [match_id] => 664
            [0] => 664
            [player_id] => 481
            [1] => 481
            [Points] => 1
            [2] => 1
        )

    [2] => Array
        (
            [match_id] => 664
            [0] => 664
            [player_id] => 510
            [1] => 510
            [Points] => 1
            [2] => 1
        )

    [3] => Array
        (
            [match_id] => 664
            [0] => 664
            [player_id] => 504
            [1] => 504
            [Points] => 1
            [2] => 1
        )

    [4] => Array
        (
            [match_id] => 664
            [0] => 664
            [player_id] => 509
            [1] => 509
            [Points] => 4
            [2] => 4
        )

    [5] => Array
        (
            [match_id] => 664
            [0] => 664
            [player_id] => 513
            [1] => 513
            [Points] => 1
            [2] => 1
        )

)

1 个答案:

答案 0 :(得分:1)

所以这个问题是一个分区优化问题,猜测是什么,是NP完全的!因此,您应该确定是否需要最佳结果或可接受的结果,因为它在算法和计算时间方面产生巨大差异。如果你有一个非常小的数据集,那么最好的结果可以足够快地计算出来,但如果你的团队很大,那可能真的很痛苦。

今天我感觉很精确(加上我不喜欢模糊和启发式lol)所以我给你一个算法来计算最好的分裂。它通过所有可能的团队组合,并为每个团队计算权重(点数)差异,让团队尽可能少地返回。

你可以通过停止,如果他找到零(最佳可能的分割)或仅枚举组合而不是排列来改进这个算法,但是渐近复杂度是相同的,所以我不会打扰。

享受

class SplitTeams {
  private $_split_weight;
  private $_split_teams;
  private $_players;

  public function __construct($players) {
    $this->_players = array();
    foreach ($players as $p) {
      $this->_players[$p['player_id']] = $p;
    }
  }

  public function getTeams() {
    $this->_list_permutations(array_keys($this->_players), array());

    $half = (int) (count($this->_split_teams) / 2);
    $team_a = array_slice($this->_split_teams, 0, $half);
    $team_b = array_slice($this->_split_teams, $half);
    return array($team_a, $team_b);
  }

  private function _calculate_diff($list) {
    $sum_team_a = 0;
    $sum_team_b = 0;
    for ($i = 0; $i < count($list); $i++) {
      if ($i < (count($list) / 2))
        $sum_team_a += $this->_players[$list[$i]]['Points'];
      else
        $sum_team_b += $this->_players[$list[$i]]['Points'];
    }

    return abs($sum_team_a - $sum_team_b);
  }

  private function _list_permutations($list, $perm) {
    if (count($list) == 0) {
      /* calculate the weight for this split */
      $w = $this->_calculate_diff($perm);
      if (($this->_split_weight === null) ||
          ($this->_split_weight > $w)) {
        /* this is a candidate solution */
        $this->_split_weight = $w;
        $this->_split_teams = $perm;
      }

      print "PERM: " . implode("; ", $perm) . " - weight $w\n";

      return;
    }

    for ($i = 0; $i < count($list); $i++) {
      // slice array
      $sublist = $list;
      $a = array_splice($sublist, $i, 1);
      $this->_list_permutations($sublist, array_merge($perm, $a));
    }
  }
}

$Data = array(
  array('player_id' => 505, 'Points' => 4),
  array('player_id' => 481, 'Points' => 1),
  array('player_id' => 509, 'Points' => 3),
  array('player_id' => 510, 'Points' => 1),
  array('player_id' => 504, 'Points' => 1),
  array('player_id' => 513, 'Points' => 2));

$s = new SplitTeams($Data);
$teams = $s->getTeams();
print_r($teams);

输出:

Array
(
    [0] => Array
        (
            [0] => 505
            [1] => 481
            [2] => 510
        )

    [1] => Array
        (
            [0] => 509
            [1] => 504
            [2] => 513
        )

)

更新其实我在开玩笑,8位玩家需要11秒,9位玩家需要1分钟,10位玩家需要10分钟。)