Sorting an array by 2 values [duplicate] - php

This question already has answers here:
Sort multidimensional array by multiple columns
(8 answers)
Closed 1 year ago.
Hello everyone I am a bit confused about sorting an array by 2 values but I know I'm getting close however I cannot get my second sort correct. I'll try to explain the below information:
The $testData array has each player's data as followed in sequence: Games played, Score, Name (FYI the score is mostly negative but sometimes in the positive because this is for ranking our golf league)
I attached my php code below I think the issue lies within the SORT flags in the array_multisort but I tried them all. Thanks for anyone's help for this simple looking issue.
<?php
$testData[]=array(8,-3,"Mike");
$testData[]=array(5,-5,"Bonnie");
$testData[]=array(1,-3,"Zack");
$testData[]=array(6,-3,"Tony");
$testData[]=array(2,-3,"Danny");
$testData[]=array(5,-4,"Bruce");
$testData[]=array(2,-5,"Ellen");
$testData[]=array(5,-4,"Christine");
$testData[]=array(4,-3,"Kenny");
$testData[]=array(3,-5,"Jason");
$testData[]=array(7,-7,"Chris");
$testData[]=array(8,-4,"Steve");
$testData[]=array(7,-6,"Joe");
$testData[]=array(2,-4,"Rodger");
$testData[]=array(7,-2,"Clair");
$testData[]=array(1,-2,"Dean");
foreach($testData as $data)
{
$gamesPlayed[]=$data[0][0];
$score[]=$data[0][1];
}
array_multisort($gamesPlayed,SORT_NUMERIC,$score,SORT_NUMERIC,$testData);
rsort($testData);
print_r(json_encode($testData));
?>
I would like to able to sort by two (2) scenarios...
Scenario 1: Sort by most games played to least games played but if the games played is equivalent or ties another player, the lowest (most negative) scoring player will rank above the other players.
[
[8,-4,"Steve"],
[8,-3,"Mike"],
[7,-6,"Joe"],
[7,-2,"Clair"],
[7,-7,"Chris"],
[6,-3,"Tony"],
[5,-5,"Bonnie"],
[5,-4,"Christine"],
[5,-4,"Bruce"],
[4,-3,"Kenny"],
[3,-5,"Jason"],
[2,-5,"Ellen"],
[2,-4,"Rodger"],
[2,-3,"Danny"],
[1,-3,"Zack"],
[1,-2,"Dean"]
]
Scenario 2: Sort by lowest score to highest score but if the score is equivalent or ties another player, the most games played by a player will rank above the other players.
[
[7,-7,"Chris"],
[7,-6,"Joe"],
[5,-5,"Bonnie"],
[3,-5,"Jason"],
[2,-5,"Ellen"],
[8,-4,"Steve"],
[5,-4,"Bruce"],
[5,-4,"Christine"],
[2,-4,"Rodger"],
[8,-3,"Mike"],
[6,-3,"Tony"],
[4,-3,"Kenny"],
[2,-3,"Danny"],
[1,-3,"Zack"],
[7,-2,"Clair"],
[1,-2,"Dean"]
]

you can reduce your problem to one state, for the first scenario, let's just calculate how much score foreach match played (total score/match played) and the inverse for the other scenario.
This may be a very simple solution that I find usefull in some cases. but to be more accurate. I guess making it a bitmasking problem is great!
let's take the first scenario.
You will keep the first bits for Matchs played and the second ones for the score.
for example :
Anoir : 4 games : 12 points
Mark : 4 games : 11 points
let's move to Bits presentation :
Anoir : 0100 1100
Mark : 0100 0111
and by a simple comparison : it's clear Anoir will be ranked above Mark
I hope that was helpful .

Related

PHP - Group distribution with minimal previous matches

I've been struggling on the following (algorithmic?) problem for days now. I have a list of persons that I need to group evenly. Each "round" of groupings is stored so that the next time (round) I try to group people together, we group them if they were not matched together during a previous round.
Example:
John
Bob
Laura
Lucy
Michael
Mark
1st round
Group 1
John
Bob
Laura
Group 2
Lucy
Michael
Mark
Now for the 2nd round we have to avoid grouping John Bob Laura together (or at least minimize it).
I came up with the solution that works for these small edge cases where I create a pairing matrix with
[John - Bob] = 1 (number of times they got paired together in previous rounds)
[Bob - Laura] = 1
etc...
I then loop through that matrix, for each person I find the lowest number of times that person got paired with someone else and add these 2 to the list. Etc, until everybody got added to that list.
I then split that list into groups of desired number (the group size is the only parameter).
I found out that this doesn't work after a few rounds or with larger "rosters".
I'm getting close to think that this might be a NP problem because I'd have to iterate many many times to find the perfect "list".
Is there an algorithm I should look into? I'm coding this in PHP but Java or pseudo code works too.
A roster containing 36 persons.
1st round, group size = 6
2nd round, group size = 4
I should not have someone paired with someone else more than once.
With my solution, I have around 10 pairings that happened twice.

MYSQL sorting content by rating logic and opinion?

I'm designing a site and don't know how to rate the system in terms of logic.
Outcome is I want an item with 4 stars with 1000 votes to be ranked higher than an item with 1 vote of 5 stars. However, I don't want an item with 1 star with 1000 votes to be ranked higher than an item with 4 stars and 200 votes.
Anyone have any ideas or advice on what to do?
I found these two questions
Sorting by weighted rating in SQL?
MySQL Rating System - Find Rating
and they have their drawbacks and in the first one I don't understand what the winner means by "You may want to denormalize this rating value into event for performance reasons if you have a lot of ratings coming in." Please share some insight? Thank you!
Here's a quick sketch-up of such a system which works by defining a bonus factor xₙ for each flag number. According to your question you want:
x₄*4*1000 > x₅*1*5
and
x₁*1*1000 < x₄*4*200
Setting the factors to for example x₁=1, x₄=2 and x₅=2 will satisfy this, but you will of course want to adjust it and add the missing factors.
He means, you should put rating-data into the event-table (and thus have redundant data) to optimize it for performance.
See the wiki for Denormalization: http://en.wikipedia.org/wiki/Denormalization
The data you have to determine the rank of items is:
average rating
number of ratings
The hard part is probably to make rules for the ranking. Like: If the average rating for an item > 4 and the number of ratings < 4 treat it like rated 3.9
For convenience, I would put this value (how to treat the items for ranking) in the item-table.

Algorithm that creates "teams" based on a numeric skill value

I am building an application that helps manage frisbee "hat tournaments". The idea is people sign up for this "hat tournament". When they sign up, the provide us with a numeric value between 1 and 6 which represents their skill level.
Currently, we are taking this huge list of people who signed up, and manually trying to create teams out of this based on the skill levels of each player. I figured, I could automate this by creating an algorithm that splits up the teams as evenly as possible.
The only data feeding into this is the array of "players" and a desired "number of teams". Generally speaking we are looking at 120 players and 8 teams.
My current thought process is to basically have a running "score" for each team. This running score is the total of all assigned players skill levels. I loop through each skill level. I go through rounds of picks once inside skill level loop. The order of the picks is recalculated each round based on the running score of a team.
This actually works fairly well, but its not perfect. For example, I had a range of 5 pts in my sample data array. I could very easily, manually swap players around and make the discrepancy no more then 1 pt between teams.. the problem is getting that done programatically.
Here is my code thus far: http://pastebin.com/LAi42Brq
Snippet of what data looks like:
[2] => Array
(
[user__id] => 181
[user__first_name] => Stephen
[user__skill_level] => 5
)
[3] => Array
(
[user__id] => 182
[user__first_name] => Phil
[user__skill_level] => 6
)
Can anyone think of a better, easier, more efficient way to do this? Many thanks in advance!!
I think you're making things too complicated. If you have T teams, sort your players according to their skill level. Choose the top T players to be captains of the teams. Then, starting with captain 1, each captain in turn chooses the player (s)he wants on the team. This will probably be the person at the top of the list of unchosen players.
This algorithm has worked in playgrounds (and, I dare say on the frisbee fields of California) for aeons and will produce results as 'fair' as any more complicated pseudo-statistical method.
A simple solution could be to first generating a team selection order, then each team would "select" one of the highest skilled player available. For the next round the order is reversed, the last team to select a player gets first pick and the first team gets the last pick. For each round you reverse the picking order.
First round picking order could be:
A - B - C - D - E
second round would then be:
E - D - C - B - A
and then
A - B - C - D - E etc.
It looks like this problem really is NP-hard, being a variant of the Multiprocessor scheduling problem.
"h00ligan"s suggestions is equivalent to the LPT algorithm.
Another heuristic strategy would be a variation of this algorithm:
First round: pick the best, second round: pair the teams with the worst (add from the end), etc.
With the example "6,5,5,3,3,1" and 2 teams this would give the teams "6,1,5" (=12) and "5,3,3" (=11). The strategy of "h00ligan" would give the teams "6,3,3" (=12) and "5,5,1" (=11).
This problem is unfortunately NP-Hard. Have a look at bin packing which is probably a good place to start and includes an algorithm you can hopefully tweak, this may or may not be useful depending on how "fair" two teams with the same score need to be.

MySQL - Specialized Order By?

Is there a way to specify a sorting procedure for ORDER BY, or some kind of custom logic? What I need is to check some other data for the column being ordered, which is also in the row. For example if one column has a higher value than another, but a certain condition isn't met, it's sorted as lower. Right now I pull all the data in the column, sort it in PHP with usort(), and then paginate it, but this is a pretty bad performance hog. I would really like to move it into MySQL, is it possible? If so, how? :P
Thanks in advance!
Example of problem on the website here - the records get sorted on win percentage, but players who have 1 game played turn out on top with 100 % win. I'd like to set a threshold on games and then sort them lower, even though their win percentage is higher.
You can order by multiple expressions:
ORDER BY games_played < 10, wins / losses DESC
The first expression sorts all those players who have played 10 or more games above all the players that have playes fewer than 10 games. The second expression sorts by win/loss ratio. The second expression is only used to tie-break rows that were equal for the first expression. This means that a player who has played 10 games will always appear above a player who has played only 9 games regardless of their win/loss ratios.

Tournament bracket

Not sure of the best way to go about this?
I want to create a tournament bracket of 2,4,8,16,32, etc teams.
The winner of the first two will play winner of the next 2 etc.
All the way until there is a winner.
Like this
Can anyone help me?
OK so more information.
Initially I want to come up with a way to create the tournament with the 2,4,8,16,etc.
Then when I have all the users in place, if they are 16 players, there are 8 fixtures.
At this point I will send the fixture to the database.
When all the players that won are through to the next round, i would want another sql query again for the 2 winners that meet.
Can you understand what i mean?
I did something like this a few years ago. This was quite a while ago and I'm not sure I'd do it the same way (it doesn't really scale to double-elimintation or the like) How you output it might be a different question. I resorted to tables as it was in 2002-2003. There are certainly better techniques today.
The amount of rounds in the tournament is log2(players) + 1, as long as players is one of the numbers you specified above. Using this information you can calculate how many rounds there are. The last round contains the final winner.
I stored the player information something like this (tweek this for best practices)
Tournament
Name
Size
Players
Tournament
Name
Position (0 to tournament.size - 1)
Rounds
Tournament
Round
Position (max halves for each round)
Winner (player position)
Note in all my queries below, I don't include the "Tournament = [tournament]" to identify the tournament. They all need it.
It's rather simple to query this with one query and to split it out as needed for the different rounds. You could do something like this to get the next opponent (assuming there is one). For round 1, you'd simply need to get the next/previous player based on if it was even or odd:
SELECT * FROM Players WHERE Position = PlayerPosition + 1
SELECT * FROM Players WHERE Position = PlayerPosition - 1
For the next round, if the user's last Round.Position was even, you'll need to make suer that the next position up has a winner:
SELECT Player FROM Rounds WHERE Position = [playerRoundPosition] - 1
If not, the next player isn't decided, or there's a gap (don't allow gaps!)
If the users last Round.Position was odd, you'll need make sure there's a user below them AND that there's a winner below them, otherwise they should automatically be promoted to the next round (as there is no one to play)
SELECT COUNT(*) FROM Players WHERE Position > [Player.Position]
SELECT Player FROM Rounds WHERE Position = [playerRoundPosition] + 1
On a final note, I'm pretty sure you could use something like the following to reduce the queries you write by using something like:
SELECT Player FROM Rounds WHERE Position + Position % 2 = [playerRoundPosition]
SELECT Player FROM Rounds WHERE Position - Position % 2 = [playerRoundPosition]
Update:
Looking over my original post, I find that the Rounds table was a little ambigous. In reality, it should be named matches. A match is a competition between two players with a winner. The final table should look more like this (only the name changed):
Matches
Tournament
Round
Position (max halves for each round)
Winner (player position)
Hopefully that makes it a bit more clear. When the two players go up against each other (in a match), you store that information in this Matches table. This particular implementation depends on the position of the Match to know which players participated.
I started numbering the rounds at 1 because that was more clear in my implementation. You may choose 0 (or even do something completely different like go backwords), if you choose.
In the first round, match 1 means players 1 and 2 participated. In match 2, the players 3-4 participated. Essentially the first round is simply players position and position + 1 participated. You could also store this information in the rounds table if you need more access to it. Every time I used this data in the program, I needed all the round and player information anyways.
After the first round, you look at the last round of matches. In round 2, match 1, the winners from matches 1 and 2 participate. Round 2, match 2, the winners from match 3 and 4 participate. It should look pretty familiar, except that it uses the match table after round 1. I'm sure there's a more efficent way to do this repetitive task, I just never got enough time to refactor that code (it was refactored, just not that much).
Use arrays and remove the losing teams from the main array. (But keep 'em on a separate array, for reference and reuse purposes).

Categories