Algorithm: finding the biggest element of a list - algorithm

The catch: only comparisons between elements of the list is allowed. For example, suppose we have 1,000,000 chess players, and we are assigned the task of finding the best chess player in the group. We can play one chess player against any other chess player. Now, we want to minimize the maximum number of games any player plays.
If player A beats player B, and B beats C, we can assume that A is better than C. What is the smallest n such that no player plays more than n games?
#Carl: This is not homework; it's actually a subproblem of a larger problem from SPOJ.

I would wager a guess that the answer is the binary log of the number of people.
You set up a binary tree as a tournament ladder. This means the most games anyone plays is the height of the tree. The height of the binary tree would be log n

How do I find the biggest element of a list
If the list is ordered, then the biggest element is the first (or last) element of the list.
If the list is not ordered then:
Element biggest = list.get(0);
for (Element e : list) {
if (e.compareWith(biggest) > 0) {
biggest = e;
}
}
For example, suppose we have 1,000,000 chess players, and we are assigned the task of finding the best chess player in the group. Now, we want to minimize the maximum number of games any player plays.
With the new constraint of the last sentence ...
Answer #1: zero games played. Compare the chess player's rankings and the one with the best ranking is the objectively best player ... according to the ranking.
Answer #2: at most ceiling(log2(nos_players)) games played per player. A "knock out" / elimination tournament eliminates half the players in each round, so the number of rounds and hence the maximum number of games played by any one player is ceiling(log2(nos_players)).
The corresponding algorithm is trivially:
List players = ...
while (players.size() > 1) {
List winners = new ArrayList();
Iterator it = players.iterator();
while (it.hasNext()) {
Player p1 = it.next();
if (it.hasNext()) {
Player p2 = it.next();
int result = p1.compareTo(p2);
if (result < 0) {
winners.add(p2);
} else if (result > 0) {
winners.add(p1);
} else {
throw new Exception("draws are impossible in chess");
}
} else {
winners.add(p1); // bye
}
}
players = winners;
}
(Aside: if you also have a predetermined ranking for the players and the number of players N is at least 2 less than ceiling(log2(N)), you can arrange that the best 2 players get a bye in one round. If the best 2 players meet in the final, then everyone will have played less than ceiling(log2(N)) games ... which is an improvement on the solution where the byes are allocated randomly.)
In reality, answer #2 does not work for the game of chess because it does not take account of the fact that a significant percentage of real chess games are draws; i.e. neither player wins. Indeed, the fact that player A beat player B in one game does not mean A is a better player than B. To determine who is the better of any two players they need to play a number of games and tally the wins and losses. In short, the notion that there is a "better than" relation for chess players is TOTALLY unrealistic.
Not withstanding the points above, knock-out is NOT a practical way to organize a chess tournament. Everyone will be camped out on the tournament organizer's desk complaining about having to play games against players much better (or worse) than themselves.
The way a real chess (or similar) tournament works is that you decide on the number of rounds you want to play first. Then in a "round-robin" tournament, you select the top N players by ranking. and arrange that each player plays each other player. The player with the best win / draw score is the winner, and in the event of a tie you use (say) "sum of opponents scores" as a tie breaker. There are other styles of tournament as well that cater for more players / less rounds.

As far as I know there is no algorithm to solve your problem without any additional outside information to rank the players (such as seeding). If you could seed the players appropriately you can find the best player in less rounds than the worst case suggested by J. Wong.
Example of the results of 2 rounds of 10 players: A is the best, ceil(log 10) = 4
A > B; C > D; E > F; G > H; I > J
A > C; B > E; F > G; D > I

Instead of building an Abstract Data Structure such as a binary tree and resolving a tournament, you could re-interpret your goal in a different light:
Eliminate all the elements on the list that are not the largest
You will find that doing this may be much more algorithmically expedient than building a tree and seeding a "tournament".
I can demonstrate that eliminating all elements on a list that are not the largest can be done with a worst-case scenario of log n calls/comparisons per element.
Work on a copy of your original list if possible.
Pair off consecutive elements and remove from the list the lower-valued of the two. Ignore the unpaired element, if there is one.
This can be done by iterating from 0 <= i < int(n/2) and comparing indices 2i and 2i+1.
i.e., for n=7, int(n/2) = 3, i = 0,1,2; compare indices 0 and 1, 2 and 3, 4 and 5.
There should be a total of int(n/2) indices eliminated. Subtract that count from n. Then, repeat 1 until there is only one index remaining. This will be your largest.
Here is an implementation in Ruby:
def find_largest(list)
n = list.size
working_list = list.clone()
while n > 1
temp_list = Array.new()
for i in (0...n/2) # remember to cast n/2 to integer if not automatic
if working_list[2*i] > working_list[2*i+1]
new_list.push(working_list[2*i])
else
new_list.push(working_list[2*i+1])
end
end
working_list = temp_list
n -= n/2 # remember to cast n/2 to integer if not automatic
end
return working_list[0]
end

Related

Recurrence relation for variation of Nim Game

I am struggling to get the optimal subtructure to solve the problem i.e. the recurrence that has to be followed and upon which Dynamic Programming Solution can be build for optimizing the Time Complexity.
Suppose A and B have 2 kinds of Stones. There are A stones of the first type and B of the second
type. They decide to play a game according to rules such in each turn one of the following valid moves can be done:
Pick some Stones of first type
Pick some Stones of Second type
Pick equal number of Stone of both type
They have to pick atleast one Stone in each turn. Whoever makes the last move wins the game. Both play optimally. However while telling the rules of the competition, Alice cheated a bit to ensure that she wins the game by deciding who will take the first move.
Given the Stones, determine if Alice takes the first move or not.
You can work backwards from a winning position.
If all remaining stones are of either the first of the second type, or if there are an equal amount of stones from each type, the player whose turn it is wins - by picking all remaining stones.
In any other position, the player whose turn it is can reduce the number of stones of either the first or second type by one or more, or can reduce the number of both types of stones by one or more. If all of the resulting positions are winning, this position is losing. If at least one of the resulting positions is losing, the player can choose that one, so this position is winning.
More formally, for a game with A stones of the first type and B stones of the second type, create a table N[A+1, B+1] of booleans, where true means winning and false means losing.
Then just fill the table like this:
for (a = 0; a <= A; a++) {
for (b = 0; <= B; b++) {
if (a == 0 || b == 0 || a == b) {
// case 1, always winning
N[a, b] = true;
} else {
// case 2, winning if there is a losing position reachable
if (
there is an 1 <= i <= a, such that N[a-i, b] is false
OR there is an 1 <= i <= b, such that N[a, b-i] is false
OR there is an 1 <= i <= min(a, b), such that N[a-i, b-i] is false
) {
N[a, b] = true;
} else {
N[a, b] = false;
}
}
}
}
After filling the table, Alice should start if N[A, B] is true and let Bob start otherwise.
Posting this as a separate answer because it so different from the other. I'll leave that answer too because this technique is probably not very useful for similar problems.
By running an example with 10 stones of each type by hand, I found that the only losing positions are (1, 2), (3, 5), (4, 7), (6, 10). There is a search engine for integer sequences where one can search for known formulas given the start of the sequence, called OEIS
So there is sequence A072061, which matches this one. This teaches us two things:
This is a known problem and the game is more commonly known as Wythoff's game
All losing positions are given by the formula (a(2*k), a(2*k+1)) for some integer k and a(n) = n*(1+(-1)^n)/4+floor((2*n+1-(-1)^n)*(1+sqrt(5))/8)
So to determine if Alice should start, you can compute if (A, B) is a losing position using the given formula.

Calculate the winning strategy of a subtraction game

Problem:
Given 100 stones, two players alternate to take stones out. One can take any number from 1 to 15; however, one cannot take any number that was already taken. If in the end of the game, there is k stones left, but 1 through k have all been previously taken, one can take k stones. The one who takes the last stone wins. How can the first player always win?
My Idea
Use recursion (or dynamic programming). Base case 1, where player 1 has a winning strategy.
Reducing: for n stones left, if palyer 1 takes m1 stones, he has to ensure that for all options player 2 has (m2), he has a winning strategy. Thus the problem is reduced to (n - m1 - m2).
Follow Up Question:
If one uses DP, the potential number of tables to be filled is large (2^15), since the available options left depend on the history, which has 2^15 possibilities.
How can you optimize?
Assuming that the set of numbers remaining can be represented as R, the highest number remaining after your selection can be represented by RH and the lowest number remaining can be RL, the trick is to use your second-to-last move to raise the number to <100-RH, but >100-RH-RL. That forces your opponent to take a number that will put you in winning range.
The final range of winning, with the total number that you create with your second-to-last move, is:
N < 100-RH
N > 100-RH-RL
By observation I noted that RH can be as high as 15 and as low as 8. RL can be as low as 1 and as high as 13. From this range I evaluated the equations.
N < 100-[8:15] => N < [92:85]
N > 100-[8:15]-[1:13] => N > [92:85] - [1:13] => N > [91:72]
Other considerations can narrow this gap. RL, for instance, is only 13 in an edge circumstance that always results in a loss for Player A, so the true range is between 72 and 91. There is a similar issue with RH and the low end of it, so the final ranges and calculations are:
N < 100-[9:15] => N < [91:85]
N > 100-[9:15]-[1:12] => N > [91:85] - [1:12] => N > [90:73]
[90:73] < N < [91:85]
Before this, however, the possibilities explode. Remember, this is AFTER you choose your second-to-last number, not before. At this point they are forced to choose a number that will allow you to win.
Note that 90 is not a valid choice to win with, even though it might exist. Thus, the maximum it can be is 89. The real range of N is:
[88:73] < N < [90:85]
It is, however, possible to calculate the range of the number that you're using to put your opponent in a no-win situation. In the situation you find yourself in, the lowest number or the highest number might be the one you chose, so if RHc is the highest number you can pick and RLc is the lowest number you can pick, then
RHc = [9:15]
RLc = [1:12]
With this information, I can begin constructing a relative algorithm starting from the end of the game.
N*p* - RHp - RLp < Np < N*p* - RHp, where p = iteration and *p* = iteration + 1
RHp = [8+p:15]
RLp = [1:13-p]
p = -1 is your winning move
p = 0 is your opponent's helpless move
p = 1 is your set-up move
Np is the sum of that round.
Thus, solving the algorithm for your set-up move, p=1, you get:
N*p* - [9:15] - [1:12] < Np < N*p* - [9:15]
100 <= N*p* <= 114
I'm still working out the math for this, so expect adjustments. If you see an error, please let me know and I'll adjust appropriately.
Here is a simple, brute force Python code:
# stoneCount: number of stones to start the game with
# possibleMoves: which numbers of stones may be removed? (*sorted* list of integers)
# return value: signals if winning can be forced by first player;
# if True, the winning move is attached
def isWinningPosition(stoneCount, possibleMoves):
if stoneCount == 0:
return False
if len(possibleMoves) == 0:
raise ValueError("no moves left")
if stoneCount in possibleMoves or stoneCount < possibleMoves[0]:
return True,stoneCount
for move in possibleMoves:
if move > stoneCount:
break
remainingMoves = [m for m in possibleMoves if m != move]
winning = isWinningPosition(stoneCount - move, remainingMoves)
if winning == False:
return True,move
return False
For the given problem size this function returns in less than 20 seconds on an Intel i7:
>>> isWinningPosition(100, range(1,16))
False
(So the first play cannot force a win in this situation. Whatever move he makes, it will result in a winning position for the second player.)
Of course, there is a lot of room for run time optimization. In the above implementation many situations are reached and recomputed again and again (e.g. when the first play takes one stone and the second player takes two stones this will put the first player into the same situation as when the number of stones taken by each player are reversed). So the first (major) improvement is to memorize already computed situations. Then one could go for more efficient data structures (e.g. encoding the list of possible moves as bit pattern).

Rounds needed in elimination tournament

I was just wondering if anyone has a formula to evaluate the number of round in a competition in single elimination based on :
- the number of teams involved (can be >2)
- the number of qualified teams for the next round.
This is easy for 1v1 or 1v1v1v1 with 2 teams qualified per match each rounds but I am stuck for 1v1v1 with 1 or 2 qualified teams...
Thanks !
As requested, Examples :
let's start with 32 players who plays COD on 4 at a time on a "free for all" mode (each player is alone against the others).
1st round : 32 players distributed in 8 matches of 4 players
Here we want to know how many rounds it will take to go to a final match with 4 players if we choose to keep the 1 or 2 or even 3 bests players in each matches.
1st case : if we keep the 2 best of each matchs :
2nd round : 16 players distributed in 4 matches of 4 players
2nd case : if we keep only the first
2nd round : 8 players distributed in 2 matches of 4 players
(In the second case we can foresee a problem because at the next turn we will have only 2 players left which is not enough to fill a game)
The idea is to find a mathematical formula that give me the necessary number of round with the following variables :
* NumberOfPlayers : the quantity of initial participants
* NumberOfPlayersInAGame : the quantity of participants in a game
* NumberOfPlayersQualifiedInAGame : the quantity of participants qualified for the next round in after a game
Enjoy :D
For 1 winner in a k-tournament (e.g. k = 3 means 1v1v1) and N players, where N element k^x and x is a positive integer it takes g games (java code):
public int countGames(int players, int k) {
if (players <= 1) {
return 0;
} else {
players = players / k;
// the number of remaining players per round is the same as the number of games
return players + countGames(players, k);
}
}
Or mathematically:
g = sum(i from 1 to logk(N), k^(i-1)) ( logK(N) = ln(N) / ln(k) )
In a single elimination tournament each match eliminates one entrant , by definition. Therefore if there are N entrants and the tournament completes by producing M winners there must, by definition of a single elimination tournament, have been N-M matches completed. It is usually the case that M is identically 1, and therefore N-1 matches must have taken place.
Similarly each round can be analyzed, and if a match is composed of two entrants playing to determine a single winner, then a round with 2*N entrants and N survivors must have had 2*N - N = N matches. If there were 2*N + 1 entrants to the round, with one entrant having a bye, then there are N+1 survivors of the round and again 2*N+1 - (N+1) = N matches must have been played.
By working backward from the required single winner, noting the constraint that for each round each entrant plays only one game, thus eliminating only one contestant, then the maximum number of entrants supported by a single elimination tournament of K rounds is 2^k.

How to improve this Dynamic programming solution(Optimisation in algorithm)

The Problem statement:
Assurance Company of Moving (ACM) is a company of moving things for people. Recently, some schools want to move their computers to another place. So they ask ACM to help them. One school reserves K trucks for moving, and it has N computers to move. In order not to waste the trucks, the school ask ACM to use all the trucks. That is to say, there must be some computers in each truck, and there are no empty trucks. ACM wants to know how many partition shemes exists with moving N computers by K trucks, the ACM ask you to compute the number of different shemes with given N and K. You needn't care with the order. For example N=7,K=3, the the following 3 partition instances are regarded as the same one and should be counted as one sheme: "1 1 5","1 5 1","5 1 1". Each truck can carry almost unlimited computers!!
Save Time :
You have to count how many sequences a[1..k] exist such that :
1) a[i] + a[2] + .... + a[k] = N such that permutations dont matter
My O(N*K^2) solution (Cannot figure out how to improve on it)
#include<assert.h>
#include<stdio.h>
#include<algorithm>
using namespace std;
int DP[5001][5001];
void ini()
{
int i,j,k;
DP[0][0]=1;
for(k=1;k<=500;k++)
for(j=1;j<=500;j++)
for(i=1;i<=500;i++)
{
DP[i][j]+=j>=k?DP[i-1][j-k]:0;
DP[i][j]%=1988;
}
return ;
}
int main()
{
ini();
int N,K,i,j;
while(1)
{
scanf("%d%d",&N,&K);
if(N==0 && K==0)
return 0;
int i;
if(DP[K][N]==0)
{assert(0);}
printf("%d\n",DP[K][N]);
}
return 0;
}
Explanation of my solution DP[i][j] represents the number of ways I can have total j computers using i Trucks only.
The k represents the number of computers with which I am dealing with that means I am just avoiding permutations!
How can I improve it to O(N*K)?
Problem constraints
N (1<=N<=5000) and K(1<=K<=N)
Problem Link: Problem Spoj
Just say that you have K gift boxes and N chocolates.
I will start with a recursive and real easy to convert it to iterative solution.
The key to avoid repetitions is distributing chocolates in a ascending order (descending also works). So you 7 chocolates and I put 2 chocolate in the first box, I will put at least 2 in the second box. WHY? this helps in avoiding repetitions.
now onwards TCL = totalChocholatesLeft & TBL = totalBinsLeft
So S(TCL,TBL) = S(TCL-TBL,TBL) + S(TCL,TBL-1);
you have to call the above expression starting with S(n-k), k)
Why? because all boxes need at least one item so first put `1` each box.
Now you are left with only `n-k` chocolates.
That's all! that's the DP recursion.
How does it work?
So in order to remove repetitions we are maintaning the ascending order.
What is the easiest way to maintain the ascending order ?
If you put 1 chocolate in the ith box, put 1 in all boxes in front of it i+1, i++2 .....k.
So after keeping chocolate in a gift box, you have two choices :
Either you want to continue with current box :
S(TCL-TBL,TBL) covers this
or to move the next box just never consider this box again
S(TCL,TBL-1) covers this.
Equivalent DP would make have TC : O(NK)
This problem is equivalent to placing n-k identical balls (after already placing one ball in each cell to make sure it's not empty) in k identical cells.
This can be solved using the recurrence formula:
D(n,0) = 0 n > 0
D(n,k) = 0 n < 0
D(n,1) = 1 n >= 0
D(n,k) = D(n,k-1) + D(n-k,k)
Explanation:
Stop clauses:
D(n,0) - no way to put n>0 balls in 0 cells
D(n<0,k) - no way to put negative number of balls in k cells
D(n,1) - one way to put n balls in 1 cell: all in this cell
Recurrence:
We have two choices.
We either have one (or more) empty cell, so we recurse with the same problem, and one less cell: D(n,k-1)
Otherwise, we have no empty cells, so we put one ball in each cell, recurse with the same number of cells and k less balls, D(n-k,k)
The two possibilities are of disjoint sets, so the union of both sets is the summation of the two sizes, thus D(n,k) = D(n,k-1) + D(n-k,k)
The above recursive formula is easy to compute in O(1) (assuming O(1) arithmetics), if the "lower" problems are known, and the DP solution needs to fill a table of size (n+1)*(k+1), so this solution is O(nk)

Ranking algorithms to compare "Rankings"

Is there an algorithm that allows to rank items based on the difference of the position of those items in two rankings but also "weighted" with the position, e.g. one Player that goes from position 2->1 should be ranked higher than a player that went from 9->8.
Toy example, I have two lists/ranks:
Rank 1:
Player a
Player b
Player c
Player d
...
Rank 2:
Player d
Player c
Player a
Player b
...
I was thinking to "weight" the ranking difference with the average ranking (or other value), for example, if a player goes from 9->8 the value used to rank will be (9-8)/avg(8,9) = 1/8,5.
What you want seems more or less equivalent to Spearman's rank correlation in non-parametric statistics. It basically sums the squares of the amount_moved (the difference between the old rank and the new rank)
Number your list backwards. Calculate the "value" of moving between positions as the difference of the squares of these numbers.
So if you've got 10 items in your list:
2->1 would be 10^2 - 9^2 = 19
9->8 would be 3^2 - 2^2 = 5.
Hard to tell if this is exactly what you're after without knowing what kind of relative weights you're after. If this doesn't quite suit you, try raising/lowering the exponent to find something that fits.

Resources