I'm facing the following problem: The have one grid and each grid cell has a position. One example of such grid could be the following one:
_________________________________________
| | | | | |
| (0,0) | (1,0) | (2,0) | (3,0) | (4,0) |
|_______|_______|_______|_______|_______|
| | | | | |
| (0,1) | (1,1) | (2,1) | (3,1) | (4,1) |
|_______|_______|_______|_______|_______|
| | | | | |
| (0,2) | (1,2) | (2,2) | (3,2) | (4,2) |
|_______|_______|_______|_______|_______|
| | | | | |
| (0,3) | (1,3) | (2,3) | (3,3) | (4,3) |
|_______|_______|_______|_______|_______|
| | | | | |
| (0,4) | (1,4) | (2,4) | (3,4) | (4,4) |
|_______|_______|_______|_______|_______|
I need to create a function that, given a number N, creates groups of quadratic NxN amounts of that number at most. For instance, for N = 2, each group would contain at most a 2x2 cells, group 1 would contain [(0, 0) (1, 0) (0, 1) (1, 1)], group 2 [(2, 0) (3, 0) (2, 1) (3, 1)], group 3 would just contain [(4, 0) (4, 1)] and so on.
I'm using C#, so this is conceptually a Group by operation, I decided to use LINQ Group By function, however it requires a lambda expression that must calculate a number that must be the same for each group. Therefore I'm looking at an expression that, for my problem with n = 2, must return the same number for [(0, 0) (1, 0) (0, 1) (1, 1)], another number for [(2, 0) (3, 0) (2, 1) (3, 1)], a different one for [(4, 0) (4, 1)], etc...
Which kind of expression could fulfil that property?
Thank you
In order to use a 'Group By'-operation, you need to define a key to group on.
In this case, if it's about a grid, the only possible key I could think of is the result of a calculation based on the index of the row/column in the grid.
I find it a bit difficult to explain the calculation I chose in plain text so I hope the example below does the talk for me.
Does this answer or help with your question?
Test data / settings
var grid = new List<List<string>>();
grid.Add(new List<string>(new[] { "0,0", "1,0", "2,0", "3,0", "4,0" }));
grid.Add(new List<string>(new[] { "0,1", "1,1", "2,1", "3,1", "4,1" }));
grid.Add(new List<string>(new[] { "0,2", "1,2", "2,2", "3,2", "4,2" }));
grid.Add(new List<string>(new[] { "0,3", "1,3", "2,3", "3,3", "4,3" }));
grid.Add(new List<string>(new[] { "0,4", "1,4", "2,4", "3,4", "4,4" }));
// Quadratic group factor.
int n = 2;
Solution 1 - Lambda expression
var result_1 = grid
// Create quadratic groups by calculating the combined index of the row+column with the quadratic group factor.
.SelectMany(r =>
r.GroupBy(c =>
(int)Math.Floor((double)grid.IndexOf(r) / (double)n)
+ "_" +
(int)Math.Floor((double)r.IndexOf(c) / (double)n)
)
)
// Combine all same keys together in one group.
.GroupBy(g => g.Key)
// Get all results per group.
.Select(gg => gg.SelectMany(g => g).ToList())
// ToList() because it's easier to inspect the value of the result while debugging.
.ToList();
// Short version:
var result_2 = grid
.SelectMany(r =>
r.GroupBy(c =>
(int)Math.Floor((double)grid.IndexOf(r) / (double)n) + "_" + (int)Math.Floor((double)r.IndexOf(c) / (double)n)
)
)
.GroupBy(g => g.Key)
.Select(gg => gg.SelectMany(g => g).ToList())
.ToList();
Solution 2 - Oldschool loop, probably easier/better to understand.
var result_3 = new List<List<string>>();
// Range (amount of both 'rows' and 'columns' since it's a grid).
int range = (int)Math.Ceiling((double)grid.Count / (double)n);
// Loop through 'rows'.
for(var y = 0; y < range; y++)
{
int skipRowsAmount = (y * n);
int takeRowsAmount = n;
// Get all 'rows' to split in groups.
var rows = grid.Skip(skipRowsAmount).Take(takeRowsAmount).ToList();
// Loop through 'columns'.
for (var x = 0; x < range; x++)
{
int skipColumnsAmount = (x * n);
int takeColumnsAmount = n;
// Get all 'columns' from all 'rows' to split in groups.
var quadraticColumns = rows.SelectMany(l => l.Skip(skipColumnsAmount).Take(takeColumnsAmount)).ToList();
// Add the quadratic columns group to the result.
result_3.Add(quadraticColumns);
}
}
Edit - Code to change group key from string to integer
.SelectMany(r =>
r.GroupBy(c =>
(((int)Math.Floor((double)grid.IndexOf(r) / (double)n)) * ((int)Math.Ceiling((double)grid.Count / (double)n)))
+
(int)Math.Floor((double)r.IndexOf(c) / (double)n)
)
)
Related
Writing Othello with a twist: Only storing the black, white and the legal moves in Piece type lists.
public class Piece
{
//coordinates of the pieces on the board
//X=0 Y=0 is the SouthWest corner of the board
public int X, Y;
//references to other pieces, 0 is SouthWest, 1 is West, ... 7 is South
public Piece[] neighbours = new Piece[8];
public Piece(int xLocation, int yLocation) {
this.X = xLocation;
this.Y = yLocation;
for (int i = 0; i < 8; i++)
{
this.neighbours[i] = null;
}
}
}
The problem is a missing snippet from:
{
List<Piece> legalMoves = new List<Piece>();
Piece element;
for (int i = 0; i < enemy.Count; i++)
{
for (int j = 0; j < 8; j++)
{
int xOffset = 0;
int yOffset = 0;
if (j % 4 != 3)
{
if (j < 3)
{
xOffset = -1;
}
else
{
xOffset = 1;
}
}
if (j % 4 != 1)
{
if (j > 1 && j < 5)
{
yOffset = 1;
}
else
{
yOffset = -1;
}
}
int newX = enemy[i].X + xOffset;
int newY = enemy[i].Y + yOffset;
if (enemy[i].neighbours[j] == null && newX >= 0 && newX <= 7 && newY >= 0 && newY <= 7)
{
int direction = (j + 4) % 8;
element = enemy[i].neighbours[direction];
while (element != null && !enemy.Contains(element))
{
element = enemy[i].neighbours[direction];
}
if (element != null)
{
legalMoves.Add(new Piece(newX, newY));
legalMoves[legalMoves.Count - 1].neighbours[direction] = enemy[i];
}
}
}
}
//missing snippet here
return legalMoves;
}
The missing snippet needs to transform the LegalMoves before return like this:
LegalMoves example without snippet:
X | Y | neighbours[0] | neighbours[1] | neighbours[2] | neighbours[3] |...
----------------------------------------------------------------------
0 | 0 | null | null | PieceRef | null |...
0 | 1 | null | null | PieceRef | null |...
0 | 1 | null | PieceRef | null | null |...
1 | 0 | null | null | null | PieceRef |...
0 | 1 | PieceRef | null | null | null |...
1 | 0 | PieceRef | null | null | null |...
LegalMoves example with snippet:
X | Y | neighbours[0] | neighbours[1] | neighbours[2] | neighbours[3] |...
----------------------------------------------------------------------
0 | 0 | null | null | PieceRef | null |...
0 | 1 | PieceRef | PieceRef | PieceRef | null |...
1 | 0 | PieceRef | null | null | PieceRef |...
More precisely:
The LegalMoveList without the snippet is creating a list, where multiple elements have the same X and Y value, but with different neigbours array.
The code above the missing snippet adds only those elements, which fullfill certain criterias:
-In the neighbours array only one element is Piece referency, the other seven is null.
-Where the X and Y is the same in multiple elements the location of the Piece reference is not the same. e.g.: If element1 and element2 have the same X and Y value, and element1.neighbours[0] is a Piece reference, then element2.neighbours[0] is null.
The task for the missing snippet would be to unify those elements.
Every X and Y pair needs to be unique. If it was not unique before the snippet, then one element must "inherit" the not-null values of the neighbours array (This way those criterias come in handy, do not need to concern about the "collision").
LINQ solutions is much appreciated, but if not possible, then a plain code is good as well.
Please write if the explanation is not proper.
So you have a sequence of Pieces, called LegalMoves, where some Pieces might have the same location (= values for x and y).
You want to combine the Pieces with the same location into one Piece according to the following rule
Every X and Y pair needs to be unique. If it was not unique before the snippet, then one element must "inherit" the not-null values of the neighbours array (This way those criterias come in handy, do not need to concern about the "collision").
In other words: from a group of Pieces, that all have the same location, you want to make one Piece. For every index 0..7, you want the zero or one non-null Neighbour with this index.
So if you have 25 Pieces in a group, then at utmost one of them will have a non-null value for Neighbours[4], and at utmost one of them will have a non-null value for Neighbours[0], etc.
Whenever you want to make groups of items that have something in common, consider to use one of the overloads of Enumerable.GroupBy. If you want to make one element from each Group, use an overload that has a parameter resultSelector
So you want to make groups of Pieces that have the same location:
var result = legalMoves.GroupBy(legalMove => new
{
X = legalMove.X,
Y = legalMove.Y,
},
// TODO parameter resultSelector
In words: we have an object, called legalMoves, which is a sequence of Pieces. from every legalMove in the sequence of legalMoves, make one key. This key is an object with two properties: the X and the Y of the legalMove. Make groups of Pieces that have the same value for this key.
Parameter resultSelector is a function that has two input parameters: the key, we just created and all legalMoves that have this value for key. Every legalMove is a Piece. The return value of the function is one Piece.
Something like this:
Piece CreateResult(TKey key, IEnumerable<Piece> legalMovesWithThisKey) {...}
As a Lambda we write it as:
(key, legalMovesWithThisKey => new Piece {...}
So what do we want with this new Piece:
Location is the Location of all elements in the group: so the X and Y of the Key
Neighbours is an array of 8 Pieces, where every Piece is the first element in the Neighbours group that has a non-null value for the Piece index.
The last sentence is a difficult way to say that Neighbours[0] is the one and only Neighbours[0] value that is not null, or null if all values are null. Similar for Neighbours1, [2] etc.
(key, legalMovesWithThisKey => new Piece
{
X = key.X,
Y = key.Y,
Neighbours = Enumerable.Range(0, 8)
.Select(index => legalMovesWithThisKey
.select(legalMove => legalMove.Neighbours[index])
.Where(neighbour => neighbour != null)
.FirstOrDefault())
.ToArray();
}
For Neighbours: enumerate the 8 indexes from 0..7, and for every index, use all legalMovesWithThisKey to get the neighbour with this index. Get the first one that is not null, or null if there is no first one.
So for index 3, Get the Neighbours[3] for every legalMove in this group, and take the first one that has a non-null value. Because you guaranteed that there wouldn't be more than one, the order is not important.
By the way: Your Piece has no method to access the neighbours. I took the liberty to add public get / set properties for X Y Neighbours. To make efficient use of LINQ methods, it is wise to create objects with default constructor and to have proper get / set
Try using a datatable :
DataTable dt = new DataTable();
dt.Columns.Add("X", typeof(int));
dt.Columns.Add("Y", typeof(int));
dt.Columns.Add("neighbours0", typeof(string));
dt.Columns.Add("neighbours1", typeof(string));
dt.Columns.Add("neighbours2", typeof(string));
dt.Columns.Add("neighbours3", typeof(string));
dt.Rows.Add(new object[] {0,0, "","","PieceReg", ""});
dt.Rows.Add(new object[] {0,1, "","","PieceReg", ""});
dt.Rows.Add(new object[] {0,1, "","PieceReg", "", ""});
dt.Rows.Add(new object[] {1,0, "","","", "PieceReg"});
dt.Rows.Add(new object[] {0,1, "PieceReg","","", ""});
dt.Rows.Add(new object[] {1,0, "PieceReg","","", ""});
What I have
A list of objects with Id, DateStart and DateFinish.
[
{
Id: 1234567890,
DateStart: new DateTime(),
DateFinish: new DateTime(),
},
...
]
What I need to do
I need to validate if none of the dates overlap each other.
I'm not sure if overlap is passing the right meaning here, so here is some examples:
Invalid Entry
[
{
Id: 1,
DateStart: new DateTime().AddHours(1),
DateFinish: new DateTime().AddHours(3),
},
{
Id: 2,
DateStart: new DateTime().AddHours(2),
DateFinish: new DateTime().AddHours(4),
}
]
This list have an overlap because the time of id 2 is in the middle of id 1
A table to show better:
-------------------------------------------------------------
| 1 | 2 | 3 | 4 |
| DateStart1 | | DateFinish1 | |
| | DateStart2 | | DateFinish2 |
-------------------------------------------------------------
*overlap* *overlap*
Other Invalid Examples
-------------------------------------------------------------
| 1 | 2 | 3 | 4 |
| DateStart1 | | | DateFinish1 |
| | DateStart2 | DateFinish2 | |
-------------------------------------------------------------
*overlap* *overlap*
-------------------------------------------------------------
| 1 | 2 | 3 | 4 |
| DateStart1 | | | DateFinish1 | // This would be a full overlap
| DateStart2 | | | DateFinish2 | // And it's also Invalid
-------------------------------------------------------------
*overlap* *overlap*
-------------------------------------------------------------
| 1 | 2 | 3 | 4 |
| | DateStart1 | | DateFinish1 | // Same as first example
| DateStart2 | | DateFinish2 | | // But "inverted"
-------------------------------------------------------------
*overlap* *overlap*
Valid Entry
[
{
Id: 1,
DateStart: new DateTime().AddHours(1),
DateFinish: new DateTime().AddHours(2),
},
{
Id: 2,
DateStart: new DateTime().AddHours(2),
DateFinish: new DateTime().AddHours(4),
}
]
A table to show better:
-------------------------------------------------------------
| 1 | 2 | 3 | 4 |
| DateStart1 | DateFinish1 | | |
| | DateStart2 | | DateFinish2 |
-------------------------------------------------------------
*not overlap*
And you can also have DateStart and DateFinish that are the same value, which means it can start and end at the same time.
-------------------------------------------------------------
| 1 | 2 | 3 | 4 |
| DateStart1 | | | |
| DateFinish1 | | | |
| DateStart2 | | | DateFinish2 |
-------------------------------------------------------------
*not overlap*
What I have done so far:
I'm making a foreach loop, where item is each element, and using a where with the following expression:
myList.Any(
x => x.Id == item.Id
&&
(
(
item.DateStart <= x.DateStart
&&
item.DateFinish > x.DateStart
&&
item.DateFinish <= x.DateFinish
)
||
(
item.DateStart >= x.DateStart
&&
item.DateStart < x.DateFinish
&&
item.DateFinish > x.DateFinish
)
||
(
item.DateStart <= x.DateStart
&&
item.DateFinish >= x.DateFinish
)
)
)
My Question
Is this expression correct? I have tried it with a lot of data and it seems to be wrong sometimes.
I need to be certain that it will cover all edge cases.
If there is a better way of writing all this logic, it would help to, because this code looks to ugly and hard to understand for other people.
I would use the following code:
static bool IsOverlapping(IEnumerable<Range> list)
{
Range previousRange = null;
foreach (var currentRange in list.OrderBy(x => x.DateStart).ThenBy(x => x.DateFinish))
{
if (currentRange.DateStart > currentRange.DateFinish)
return true;
if (previousRange?.DateFinish > currentRange.DateStart)
return true;
previousRange = currentRange;
}
return false;
}
A quick and dirty version. Not very performant as is on large sets. But can be improved on.
https://dotnetfiddle.net/Widget/PEn2Lm
static void DetectOverlap(List<Range> l)
{
foreach(var r in l)
{
var overlap = l.Any(x => x.Id != r.Id
&& ((r.Start == x.Start && r.End == x.End)
|| (r.Start >= x.Start && r.Start < x.End)
|| (r.End > x.Start && r.End <= x.End)));
if(overlap)
{
Console.WriteLine("Overlap detected");
throw new Exception("Overlapping range detected");
}
}
Console.WriteLine("Clean ranges");
}
I tried to mirror your cases, but I'd suggest writing unit tests to full test all of your scenarios.
List<FooBar> bars = new List<FooBar>()
{
new FooBar() //end date is inside 3
{
Start = new DateTime(2001,12,1),
End = new DateTime(2002,5,15),
Id = 1
},
new FooBar() //fine
{
Start = new DateTime(2005,12,1),
End = new DateTime(2006,5,15),
Id = 2
},
new FooBar() //start date is inside 1
{
Start = new DateTime(2002,4,1),
End = new DateTime(2003,5,15),
Id = 3
},
new FooBar() //this one is fine
{
Start = new DateTime(2006,5,15),
End = new DateTime(2007,5,15),
Id = 4
},
new FooBar() //also fine
{
Start = new DateTime(2001,12,1),
End = new DateTime(2001,12,1),
Id = 5
},
};
And then a, to me at least, slightly easier to read / skim code snippet which seems to work perfectly:
var inside = bars.Where(w =>
bars.Where(outer => ((outer.Start < w.Start && outer.End > w.Start)
|| (outer.Start < w.End && outer.End > w.End)
|| (outer.Start == w.Start && outer.End == w.End)) && outer.Id != w.Id).Any()).ToList();
inside.ForEach(e => {
Console.WriteLine($"{e.Id}");
});
For actual use, I'd also test for Any or First, and not ToList, but this gives me the Ids for the console to check.
As for why I used this logic, it might prove faulty but my assumptions:
An overlapped start date is between another input's start and end,
or an item's end date is between another input's start and end dates,
or a start and end date matches another input's values exactly.
Additional tests (as with your provided code) I expect false positives due to the use of <= and >=.
For example, changing the method to include
bars.Where(outer => ((outer.Start <= w.Start && outer.End >= w.Start)
gives me false positives on 4 and 5
I have been trying to implement an AI for the computer using minimax with alpha-beta pruning, but I m facing an unidentifiable bug. The algorithm should calculate all the possible moves of its own and the other player too, but it isn't playing back the way it should.
Here is my minimax code :
public int minimax(int[] board, char symbol, int alpha, int beta, int depth = 2)
{
int win = util.checkwin(board);
int nsymbol = (symbol == 'X' ? 1 : 2);
int mult = (symbol == compside ? 1 : -1);
if (win != -1)
{
if (win == nsymbol)
return mult;
else if (win != 0)
return (mult * -1);
else
return 0;
}
if (depth == 0)
return 0;
int[] newboard = new int[9];
Array.Copy(board, newboard, 9);
int score, i, pos = -1;
ArrayList emptyboard = new ArrayList();
emptyboard = util.filterboard(newboard);
for (i = 0; i < emptyboard.Count; i++)
{
if (i > 0)
newboard[(int)emptyboard[i - 1]] = 0;
newboard[(int)emptyboard[i]] = nsymbol;
score = minimax(newboard, util.changeside(symbol), alpha, beta, depth - 1);
if (mult == 1)
{
if (score > alpha)
{
alpha = score;
pos = (int)emptyboard[i];
}
if (alpha >= beta)
break;
}
else
{
if (score < beta)
beta = score;
if (alpha >= beta)
break;
}
}
if (depth == origdepth)
return pos;
if (mult == 1)
return alpha;
else
return beta;
}
The details of undefined functions:
util.checkwin(int[] board) = checks the board for a possible won or drawn outboard or an incomplete board, and returns the winner as 1 or 2 (player X or O), 0 for a draw, and -1 for an incomplete board.
util.filterboard(int[] newboard) = returns an arraylist containing all the positions of empty locations in board given.
util.changeside(char symbol) = simply flips X to O and O to X and returns the result.
I have tried with the depth as 2 which means it will calculate the next 2 moves (if it is winning and if the opponent can win). But the results weren't what I expected. and it is also trying to play on a filled location occasionally.
Here is an output(depth = 2):
Turn: X
| |
1 | 2 | 3
__|___|__
| |
4 | 5 | 6
__|___|__
| |
7 | 8 | 9
| |
Enter Your Choice:
Turn: O
| |
1 | 2 | 3
__|___|__
| |
X | 5 | 6
__|___|__
| |
7 | 8 | 9
| |
Enter Your Choice: 5
Turn: X
| |
1 | 2 | 3
__|___|__
| |
X | O | 6
__|___|__
| |
7 | 8 | 9
| |
Enter Your Choice:
Turn: O
| |
1 | X | 3
__|___|__
| |
X | O | 6
__|___|__
| |
7 | 8 | 9
| |
Enter Your Choice: 1
Turn: X
| |
O | X | 3
__|___|__
| |
X | O | 6
__|___|__
| |
7 | 8 | 9
| |
Enter Your Choice:
Turn: O
| |
O | X | 3
__|___|__
| |
X | O | 6
__|___|__
| |
7 | X | 9
| |
Enter Your Choice: 9
| |
O | X | 3
__|___|__
| |
X | O | 6
__|___|__
| |
7 | X | O
| |
O Wins
But it still fails to recognize my winning move.
All the other functions have been tested when played user against a user and they are all working fine. I would appreciate some help.
I am happy to provide my full code, if necessary and anything else required.
A couple of observations.
1) The if (depth == 0) return 0; should be changed to something like
if (depth == 0) return EvaluatePosition();,
because currently your algorithm will return 0 (score, corresponding to a draw) whenever it reaches depth zero (while the actual position at zero depth might not be equal - for instance, one of the sides can have huge advantage). EvaluatePosition() function should reflect the current board position (it should say something like "X has an advantage", "O is losing", "The position is more or less equal" etc, represented as a number). Note, that this will matter only if depth == 0 condition is triggered, otherwise it is irrelevant.
2) Do you really need this emptyboard stuff? You can iterate over all squares of the newboard and once you find an empty square, copy the original board, make the move on this empty square and call minimax with the copied and updated board. In pseudocode it will look something like this:
for square in board.squares:
if square is empty:
board_copy = Copy(board)
board_copy.MakeMove(square)
score = minimax(board_copy, /*other arguments*/)
/*the rest of minimax function*/
3) The if (alpha >= beta) break; piece is present in both branches (for mult == 1 and mult != 1), so you can put it after the if-else block to reduce code repetition.
4) Check if your algorithm is correct without alpha-beta pruning. The outcomes of plain minimax and alpha-beta pruning minimax should be the same, but plain minimax is easier to understand, code and debug. After your plain minimax is working properly, add enhancements like alpha-beta pruning and others.
I have a gave, which, when it's finished, has a table of players and their scores.
On the other hand i have a virtual pot of money that i want to distribute among these winners. I'm looking for a SQL query or piece of C# code to do so.
The descending sorted table looks like this:
UserId | Name | Score | Position | % of winnings | abs. winnings $
00579 | John | 754 | 1 | ? | 500 $
98983 | Sam | 733 | 2 | ? | ?
29837 | Rick | 654 | 3 | ? | ? <- there are 2 3rd places
21123 | Hank | 654 | 3 | ? | ? <- there are 2 3rd places
99821 | Buck | 521 | 5 | ? | ? <- there is no 4th, because of the 2 3rd places
92831 | Joe | 439 | 6 | ? | ? <- there are 2 6rd places
99281 | Jack | 439 | 6 | ? | ? <- there are 2 6rd places
12345 | Hal | 412 | 8 | ? | ?
98112 | Mick | 381 | 9 | ? | ?
and so on, until position 50
98484 | Sue | 142 | 50 | ? | 5 $
Be aware of the double 3rd and 6th places.
Now i want to distribute the total amount of (virtual) money ($ 10,000) among the first 50 positions. (It would be nice if the positions to distribute among (which is now 50) can be a variable).
The max and min amount (for nr 1 and nr 50) are fixed at 500 and 5.
Does anyone have a good idea for a SQL query or piece of C# code to fill the columns with % of winnings and absolute winnings $ correctly?
I prefer to have a distribution that looks a bit logarithmic like this: (which makes that the higher positions get relatively more than the lower ones).
.
|.
| .
| .
| .
| .
| .
| .
| .
| .
I haven't done SQL since 1994, but I like C# :-). The following might suit, adjust parameters of DistributeWinPot.DistributeWinPot(...) as required:
private class DistributeWinPot {
private static double[] GetWinAmounts(int[] psns, double TotWinAmounts, double HighWeight, double LowWeight) {
double[] retval = new double[psns.Length];
double fac = -Math.Log(HighWeight / LowWeight) / (psns.Length - 1), sum = 0;
for (int i = 0; i < psns.Length; i++) {
sum += retval[i] = (i == 0 || psns[i] > psns[i - 1] ? HighWeight * Math.Exp(fac * (i - 1)) : retval[i - 1]);
}
double scaling = TotWinAmounts / sum;
for (int i = 0; i < psns.Length; i++) {
retval[i] *= scaling;
}
return retval;
}
public static void main(string[] args) {
// set up dummy data, positions in an int array
int[] psns = new int[50];
for (int i = 0; i < psns.Length; i++) {
psns[i] = i+1;
}
psns[3] = 3;
psns[6] = 6;
double[] WinAmounts = GetWinAmounts(psns, 10000, 500, 5);
for (int i = 0; i < psns.Length; i++) {
System.Diagnostics.Trace.WriteLine((i + 1) + "," + psns[i] + "," + string.Format("{0:F2}", WinAmounts[i]));
}
}
}
Output from that code was:
1,1,894.70
2,2,814.44
3,3,741.38
4,3,741.38
5,5,614.34
6,6,559.24
7,6,559.24
8,8,463.41
9,9,421.84
10,10,384.00
11,11,349.55
12,12,318.20
13,13,289.65
14,14,263.67
15,15,240.02
16,16,218.49
17,17,198.89
18,18,181.05
19,19,164.81
20,20,150.03
21,21,136.57
22,22,124.32
23,23,113.17
24,24,103.02
25,25,93.77
26,26,85.36
27,27,77.71
28,28,70.74
29,29,64.39
30,30,58.61
31,31,53.36
32,32,48.57
33,33,44.21
34,34,40.25
35,35,36.64
36,36,33.35
37,37,30.36
38,38,27.64
39,39,25.16
40,40,22.90
41,41,20.85
42,42,18.98
43,43,17.27
44,44,15.72
45,45,14.31
46,46,13.03
47,47,11.86
48,48,10.80
49,49,9.83
50,50,8.95
Then how about this?
Select userid, log(score),
10000 * log(score) /
(Select Sum(log(score))
From TableName
Where score >=
(Select Min(score)
from (Select top 50 score
From TableName
Order By score desc) z))
From TableName
Order By score desc
I need help to understand how a function is working;: it is a recursive function with yield return but I can't figure out how it works. It is used calculate a cumulative density function (approximate) over a set of data.
Thanks a lot to everyone.
/// Approximates the cumulative density through a recursive procedure
/// estimating counts of regions at different resolutions.
/// </summary>
/// <param name="data">Source collection of integer values</param>
/// <param name="maximum">The largest integer in the resulting cdf (it has to be a power of 2...</param>
/// <returns>A list of counts, where entry i is the number of records less than i</returns>
public static IEnumerable<int> FUNCT(IEnumerable<int> data, int max)
{
if (max == 1)
{
yield return data.Count();
}
else
{
var t = data.Where(x => x < max / 2);
var f = data.Where(x => x > max / 2);
foreach (var value in FUNCT(t, max / 2))
yield return value;
var count = t.Count();
f = f.Select(x => x - max / 2);
foreach (var value in FUNCT(f, max / 2))
yield return value + count;
}
}
In essence, IEnumerable functions that use yield return function slightly differently from traditional recursive functions. As a base case, suppose you have:
IEnumerable<int> F(int n)
{
if (n == 1)
{
yield return 1;
yield return 2;
// implied yield return break;
}
// Enter loop 1
foreach (var v in F(n - 1))
yield return v;
// End loop 1
int sum = 5;
// Enter loop 2
foreach (var v in F(n - 1))
yield return v + sum;
// End loop 2
// implied yield return break;
}
void Main()
{
foreach (var v in F(2))
Console.Write(v);
// implied return
}
F takes the basic orm of the original FUNCT. If we call F(2), then walking through the yields:
F(2)
| F(1)
| | yield return 1
| yield return 1
Console.Write(1);
| | yield return 2
| yield return 2
Console.Write(2)
| | RETURNS
| sum = 5;
| F(1)
| | yield return 1
| yield return 1 + 5
Console.Write(6)
| | yield return 2
| yield return 2 + 5
Console.Write(7)
| | RETURNS
| RETURNS
RETURNS
And 1267 is printed. Note that the yield return statement yields control to the caller, but that the next iteration causes the function to continue where it had previously yielded.
The CDF method does adds some additional complexity, but not much. The recursion splits the collection into two pieces, and computes the CDF of each piece, until max=1. Then the function counts the number of elements and yields it, with each yield propogating recursively to the enclosing loop.
To walk through FUNCT, suppose you run with data=[0,1,0,1,2,3,2,1] and max=4. Then running through the method, using the same Main function above as a driver, yields:
FUNCT([0,1,0,1,2,3,2,1], 4)
| max/2 = 2
| t = [0,1,0,1,1]
| f = [3] // (note: per my comment to the original question,
| // should be [2,3,2] to get true CDF. The 2s are
| // ignored since the method uses > max/2 rather than
| // >= max/2.)
| FUNCT(t,max/2) = FUNCT([0,1,0,1,1], 2)
| | max/2 = 1
| | t = [0,0]
| | f = [] // or [1,1,1]
| | FUNCT(t, max/2) = FUNCT([0,0], 1)
| | | max = 1
| | | yield return data.count = [0,0].count = 2
| | yield return 2
| yield return 2
Console.Write(2)
| | | RETURNS
| | count = t.count = 2
| | F(f, max/2) = FUNCT([], 1)
| | | max = 1
| | | yield return data.count = [].count = 0
| | yield return 0 + count = 2
| yield return 2
Console.Write(2)
| | | RETURNS
| | RETURNS
| count = t.Count() = 5
| f = f - max/2 = f - 2 = [1]
| FUNCT(f, max/2) = FUNCT([1], 2)
| | max = 2
| | max/2 = 1
| | t = []
| | f = [] // or [1]
| | FUNCT(t, max/2) = funct([], 1)
| | | max = 1
| | | yield return data.count = [].count = 0
| | yield return 0
| yield return 0 + count = 5
Console.Write(5)
| | | RETURNS
| | count = t.count = [].count = 0
| | f = f - max/2 = []
| | F(f, max/2) = funct([], 1)
| | | max = 1
| | | yield return data.count = [].count = 0
| | yield return 0 + count = 0 + 0 = 0
| yield return 0 + count = 0 + 5 = 5
Console.Write(5)
| | RETURNS
| RETURNS
RETURNS
So this returns the values (2,2,5,5). (using >= would yield the values (2,5,7,8) -- note that these are the exact values of a scaled CDF for non-negative integral data, rather than an approximation).
Interesting question. Assuming you understand how yield works, the comments on the function (in your question) are very helpful. I've commented the code as I understand it which might help:
public static IEnumerable<int> FUNCT(IEnumerable<int> data, int max)
{
if (max == 1)
{
// Effectively the end of the recursion.
yield return data.Count();
}
else
{
// Split the data into two sets
var t = data.Where(x => x < max / 2);
var f = data.Where(x => x > max / 2);
// In the set of smaller numbers, recurse to split it again
foreach (var value in FUNCT(t, max / 2))
yield return value;
// For the set of smaller numbers, get the count.
var count = t.Count();
// Shift the larger numbers so they are in the smaller half.
// This allows the recursive function to reach an end.
f = f.Select(x => x - max / 2);
// Recurse but add the count of smaller numbers. We already know there
// are at least 'count' values which are less than max / 2.
// Recurse to find out how many more there are.
foreach (var value in FUNCT(f, max / 2))
yield return value + count;
}
}