If I had $1000(variable) and I want to split that amount up and give it to 20(variable) people, but rather than give it evenly to each person, I want to give more to the 1st person, and the 2nd person, etc.
So the 20th person gets the least, and the 5th person gets the 5th most.
How would I achieve that?
Thanks
Edit:
Formula:
int people = 20;
float prize = 1000;
float k = (2 * prize) / ((people) * (people - 1));
float sum = 0;
for (int i = 1; i < people; ++i)
{
var personsPrize = i * k;
sum += personsPrize;
Console.WriteLine(personsPrize);
}
Console.WriteLine("sum = " + sum);
Formula is correct, needed a little touch.
- Don't cast float into int, data loss!
When going within the for go from the first person to the n-1
int people = 20;
float prize = 1000;
float k = (2 * prize) / ((people) * (people - 1));
float sum = 0;
for (int i = 1; i < people; ++i)
{
var personsPrize = i * k;
sum += personsPrize;
Console.WriteLine(personsPrize);
}
Console.WriteLine("sum = " + sum);
Adding answer to Ensure prize pool doesn't award tied participants less than participants who scored worse
Two things
Incorrect, when assigning prices start from first person to the n-1 one person does't get prize, correct is start from first person to the n
Valid basePrize noted with K is
k = (2 * prize) / ((people) * (people + 1))
Now the answer the other question:
int totalPeople = 20;
float prize = 5000;
List<int> peopleScores = new List<int>();
Random r = new Random();
for (int i = 0; i < totalPeople; ++i)
{
peopleScores.Add(r.Next(0, 100));
}
var totalPeopleWithScore = peopleScores.Where(x => x > 0).Count();
var groupedScores = peopleScores
.Where(x => x > 0)
.ToList()
.GroupBy(x => x)
.Select(grp => new
{
score = grp.Key,
peopleScores = grp.ToList()
})
.OrderBy(g => g.score)
.ToList();
var groupCount = groupedScores.Select(x => new { count = x.peopleScores.Count() }).Select(z => z.count).Count();
float basePrizeRate = 2 * prize / totalPeopleWithScore / (totalPeopleWithScore + 1);
Console.WriteLine("Base Price rate: " + basePrizeRate);
float sum = 0;
var leaderboardPosition = 0;
var totalWinners = 0;
foreach (var positionScore in groupedScores)
{
var countWinner = positionScore.peopleScores.Count();
Console.WriteLine();
Console.WriteLine($"On leaderboard position : {groupedScores.Count() - leaderboardPosition} are {countWinner} winners with score: {positionScore.score}");
float positionPrizePool = 0;
for (int i = 1; i <= positionScore.peopleScores.Count(); ++i)
{
totalWinners++;
positionPrizePool += totalWinners * basePrizeRate;
}
Console.WriteLine("Prize Pool " + positionPrizePool);
var personPoolPrize = positionPrizePool / positionScore.peopleScores.Count();
foreach (var x in positionScore.peopleScores)
{
Console.WriteLine($"Winner {totalWinners} won: {personPoolPrize}");
sum += personPoolPrize;
}
leaderboardPosition++;
}
Console.WriteLine();
Console.WriteLine("Total Prize: " + sum);
Console.WriteLine("Total Winners: " + totalPeopleWithScore);
You owe me a beer, actually a chug of beer!!
Using Gauss distribution (N*(N+1))/2 you cannot have a slight margin over the competitors ranks, prizes have linear increase https://mathbitsnotebook.com/Algebra2/Sequences/SSGauss.html.
What you need is something that increases exponentially or with a fixed ration, I want to solve your problem with a mathematical approach, so I will use a geometric progression https://mathbitsnotebook.com/Algebra2/Sequences/SSGeometric.html
I won't add any other answer after this one, you have to build your own prize pool system!!
For Geometric Distribution formula is:
rankPrize = ((1 - distributionFactor) / (1 - distributionFactor ^
winners) * distributionFactor ^ (rank - 1)) * prize
where
distributionFactor - is between 0 and 1
Play around with distribution Factor and chose the correct value for your system, I've chosen 0.8 because the gap between prizes is not that big!
Here we go, fiddle link: https://dotnetfiddle.net/qmJnYd to play around and implementation:
static void GaussDistributionPricePool()
{
Console.WriteLine("________________________________________________________");
Console.WriteLine("Begin Gauss distribution price pool");
int totalPeople = 20;
float prize = 5000;
List<int> peopleScores = new List<int>();
Random r = new Random();
for (int i = 0; i < totalPeople; ++i)
{
peopleScores.Add(r.Next(0, 100));
}
var totalPeopleWithScore = peopleScores.Where(x => x > 0).Count();
var groupedScores = peopleScores
.Where(x => x > 0)
.ToList()
.GroupBy(x => x)
.Select(grp => new
{
score = grp.Key,
peopleScores = grp.ToList()
})
.OrderBy(g => g.score)
.ToList();
var groupCount = groupedScores.Select(x => new { count = x.peopleScores.Count() }).Select(z => z.count).Count();
// Gauss formula. (N*(N+1))/2
// https://mathbitsnotebook.com/Algebra2/Sequences/SSGauss.html
float basePrizeRate = 2 * prize / totalPeopleWithScore / (totalPeopleWithScore + 1);
Console.WriteLine("Base Price rate: " + basePrizeRate);
float sum = 0;
var leaderboardRank = 0;
var totalWinners = 0;
foreach (var positionScore in groupedScores)
{
var countWinner = positionScore.peopleScores.Count();
Console.WriteLine();
Console.WriteLine("On leaderboard rank : " + (groupedScores.Count() - leaderboardRank) + " are " + countWinner + " winners with score: " + positionScore.score);
float positionPrizePool = 0;
for (int i = 1; i <= positionScore.peopleScores.Count(); ++i)
{
totalWinners++;
positionPrizePool += totalWinners * basePrizeRate;
}
Console.WriteLine("Prize Pool " + positionPrizePool);
var personPoolPrize = positionPrizePool / positionScore.peopleScores.Count();
foreach (var x in positionScore.peopleScores)
{
Console.WriteLine("Winner " + totalWinners + " won: " + personPoolPrize);
sum += personPoolPrize;
}
leaderboardRank++;
}
Console.WriteLine();
Console.WriteLine("Total Prize: " + sum);
Console.WriteLine("Total Winners: " + totalPeopleWithScore);
Console.WriteLine("End Gauss distribution price pool");
Console.WriteLine("________________________________________________________");
}