【问题标题】:Split PHP array into two equal sets where the sum of value are equal将 PHP 数组拆分为两个相等的集合,其中值的总和相等
【发布时间】:2017-03-09 10:34:43
【问题描述】:

我有以下球员和他们的观点。我想将他们分成两个相等的团队,其中点的总和应该尽可能相等。

例如输出应该是:

队伍 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
        )

)

【问题讨论】:

  • 首先使用mysqli_fetch_assoc() 而不是mysqli_fetch_array(),然后您至少会以 assoc 数组而不是 assoc 和数字数组的形式获取数据一次
  • 玩家人数也需要相等吗?您可以尝试按点对数组进行排序,并从最高点到最低点逐一添加玩家。 usort 可以帮助您。 usort - manualfunction sortByPoints($a,$b){ return strcmp($a->Points,$b->Points); } usort($array,'sortByPoints');
  • 您如何检索数据?如果您是从集合或数据库中查询,那么我认为您的第一步应该是改进查询以获得更可行的结果。大多数界面都可以让您按玩家查询并总结积分。这意味着您可以轻松地建立一个关联。只有玩家 ID 和总数的数组。然后一个简单的排序会将所有得分相似的人放在最接近的位置。
  • 感谢大家的帮助和建议。我修改了查询以按点排序。然后做了一个 fetch assoc 并将数组分成两部分,奇数和偶数。

标签: php arrays sorting sum


【解决方案1】:

所以这个问题是一个分区优化问题,猜猜看,它是 NP 完全的!所以你真的应该指定你需要最佳结果还是可接受结果,因为它在算法和计算时间方面有很大的不同。如果您的数据集非常小,则可以足够快地计算出最佳结果,但如果您的团队很大,这可能会很痛苦。

今天我感觉很精确(而且我不喜欢模糊性和启发式哈哈),所以我给你一个算法来计算最佳分割。它遍历所有可能的团队组合,并为每个团队计算权重(分)差异,返回可能差异最小的团队。

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

享受

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分钟:)

【讨论】:

  • 感谢您抽出宝贵时间发表评论。
猜你喜欢
  • 2020-09-13
  • 1970-01-01
  • 1970-01-01
  • 2019-11-30
  • 1970-01-01
  • 2012-10-18
  • 1970-01-01
  • 2015-12-19
  • 2017-04-15
相关资源
最近更新 更多