I am developing a soccer tournament management system and I'm stuck with a problem. I have to show to the user round teams ranking based on different attributes. I'm right now considering the following order:
- team points ( 3*won match + 1*tie match )
- team confrontational( not sure if this is the right word but I mean matches between same teams, i.e. if two teams have the same points but one was defeated by the other, the winning one will be above the loser one )
- goal difference (goal scored-goal taken)
- goal scored
- alphabetical order
Data to build this model are retrieved through different database query and the structure I'm using right now to save them is an array of objects like this:
- teamId
- played matches
- won matches
- lost matches
- tie matches
- points
- goal scored
- goal taken
- goal difference
- teams that were defeated current one = array(teamId1,teamId2...)
- teams that defeated current one= array(teamId1,teamId2...)
- teams that had a tie with current one = array(teamId1,teamId2...)
For most sorting attributes I can use php array_multisort function and get the job done but the issue is about team confrontational. As an example let's say I have this situation:
- Team A - 3 points - won against开发者_JS百科 D
- Team B - 3 points - won against D
- Team C - 3 points - won against A
- Team D - 0 points - won against D
In this case I have that team C should stay above A since C defeated A while I should check goal difference(next attribute in the order) to determine B position compared to B and C. I'm trying to develop an algorithm able to sort this issue with any attribute configuration but had no luck so far. Anyone has any suggestion? Thank you very much and forgive me if I were not clear
what you call team confrontational is actually a new table calculated for the teams that are tying with the same points using only the games between them. This problem evolves into a particularly nasty situation when you have 3 or more teams tied in points, in which case you cannot sort the position table using one to one comparisons (which is what all sorting algorithms do) as you need to know the other teams tied to create your sub-table. You would need to create a function that given a list of matches and a list of teams generates your position table for those teams, sorted by caring only for points. Before returning the position table check if there are groups with the same points and replace that section of the table with the result of a recursive call with all the matches but only the tied group teams. Be sure to stop the recursion when the whole table is tied or else you get infinite loop, in that case apply global goal diff, goals scored and alphabetical.
PD: I tried solving this problem before and gave up after realizing I was dealing with non comparable sets. If you actually implement it I would be very happy to check out the code.
@ilcavero is exactly correct.
I implemented something similar to this. My code assumes the "confrontation" sort resolving ties can't be run unless (a) all players have played each other and (b) one player wins all the matches or one player loses all the matches between the tied teams.
This if statement avoids problems like this:
- Alice beat Bob
- Bob beat Carl
- Carl beat Alice
Alice would need to be ahead of Bob, and behind Carl. Bob needs to be ahead of Carl and behind Alice but Alice needs to be behind Carl. It breaks. It also avoids penalizing or rewarding someone for a match they didn't play.
The code does solve ties that look like this:
- Alice beat Bob
- Alice beat Carl
- Carl beat Bob
We can first remove Alice, who beat the other players, and place her above the tied teams:
- Alice
- Bob & Carl
Since Bob lost to Carl, we can remove him from the tie and place him in the position below the tie (which at this point would be just Carl). Resolving to:
- Alice
- Carl
- Bob
I keep the rankings in a structure like so when I am sorting ($this->sortedTeams):
[
1 => [$id => $team, $id => $team, $id = $team], // 3 teams tied for first position.
4 => [$id => $team], // One team in 4th position.
5 => [$id => $team, $id => $team] // 2 teams tied in 5th position.
]
Here is the code in my TeamSorter class that deals with head to head comparisons:
private function headToHead() {
foreach ($this->findTies() as $position)
$this->headToHeadThisPosition($position);
ksort($this->sortedTeams);
}
private function headToHeadThisPosition($position) {
// Teams that are tied.
$teams = $this->sortedTeams[$position];
// Every team must play every other team, or head to head doesn't work.
if (!$this->tiedTeamsHaveAllPlayedEachother($teams)) return;
// If we have a winner, it assumes $position, and other(s) assume
// $positon + 1, moving away from first place.
if ($winner_nid = $this->tiedPositionHasWinner($teams))
$position = $this->removeFromTie('winner', $winner_nid, $position);
// If we have a loser, it assumes $position + 1 (moving away from first
// place), other(s) stay at $position.
if ($loser_nid = $this->tiedPositionHasLoser($teams))
$this->removeFromTie('loser', $loser_nid, $position);
// Recursively call this function if we might resolve a tie at $position
// before going on to the next position.
if ($this->recursionNeeded($position))
$this->headToHeadThisPosition($position);
}
private function recursionNeeded($position) {
$teams = $this->sortedTeams[$position];
return
count($this->sortedTeams[$position]) > 1
&&
$this->tiedTeamsHaveAllPlayedEachother($this->sortedTeams[$position])
&&
$this->tiedPositionHasWinner($this->sortedTeams[$position]);
}
精彩评论