Related
I am trying to write a function that the returns one of the following multipliers randomly selected but following the frequency requirement. What below table defines is that for 1 Million calls to this function the 1500 will be returned once, 500 twice and so on.
|---------------------|------------------------------|
| Multiplier | Frequency Per Million |
|---------------------|------------------------------|
| 1500 | 1 |
|---------------------|------------------------------|
| 500 | 2 |
|---------------------|------------------------------|
| 200 | 50 |
|---------------------|------------------------------|
| 50 | 100 |
|---------------------|------------------------------|
| 25 | 20,000 |
|---------------------|------------------------------|
| 5 | 75,000 |
|---------------------|------------------------------|
| 3 | 414,326 |
|---------------------|------------------------------|
| 2 | 490521 |
|---------------------|------------------------------|
Wondering what would be the best way to approach implementing this.
First, let's declare the model:
static Dictionary<int, int> multipliers = new Dictionary<int, int>() {
{1500, 1},
{ 500, 2},
{ 200, 50},
{ 50, 100},
{ 25, 20_000},
{ 5, 75_000},
{ 3, 414_326},
{ 2, 490_521},
};
Then you can easily choose random multiplier:
// Easiest, but not thread safe
static Random random = new Random();
...
private static int RandomMultiplier() {
int r = random.Next(multipliers.Values.Sum());
s = 0;
foreach (var pair in multipliers.OrderByDescending(p => p.Key))
if (r < (s += pair.Value))
return pair.Key;
throw new InvalidOperationException($"{r} must not reach this line");
}
...
int multiplier = RandomMultiplier();
If the frequency and value that needs to be returned are set, then nothing complicated is needed. You just need to adjust for the previous numbers being handled in the if blocks by adding the frequency of the previous numbers.
private int GetRandomMultiplier()
{
var random = new Random();
var next = random.Next(1000000);
if (next < 1)
{
return 1500;
}
else if (next < 3)
{
return 500;
}
else if (next < 53)
{
return 200;
}
else if (next < 153)
{
return 50;
}
else if (next < 20153)
{
return 25;
}
else if (next < 95153)
{
return 5;
}
else if (next < 509479)
{
return 3;
}
return 2;
}
You don't want to create a new Random every time though, so create one once and use that.
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","","", ""});
I have a list like
| column1 | column2 |
| 1 | 72 |
| 2 | 30 |
| 3 | 27 |
| 3 | 38 |
| 4 | 72 |
As you can see, the list is already sorted on column1, my goal here is to perform an OrderByDescending on the second column only on equals columns 1. Basically, I want
| column1 | column2 |
| 1 | 72 |
| 2 | 30 |
| 3 | 38 |
| 3 | 27 |
| 4 | 72 |
I can't re-run the first OrderBy (that'll be hard to explain, we don't care, I just cant :D) so forget about
list.OrderBy(e => e.column1).ThenByDescending(e => e.column2)
In fact, I wouldn't have any problem if I could simply do a .ThenByDescending(e => e.column2) without having to do the .OrderBy (maybe I can run an "empty" OrderBy that won't change the sort ? Then I would be able to do the ThenByDescending ?)
list.OrderBy(e => e.column1).ThenByDescending(e => e.column2) is still the way to go.
The algorithm has to know it has to sort on e.column1 first, no matter if it actually changes something or not. It has to know it only has to sort e.column2 within the subset of the first sorting statement. You can't do that with 'just' sorting on column2.
list.GroupBy(i => i.column1).SelectMany(i => i.OrderByDescending(g => g.column2))
Will work with many providers, but some may not preserve the ordering in the GroupBy. In such a case:
list.AsEnumerable().GroupBy(i => i.column1).SelectMany(i => i.OrderByDescending(g => g.column2))
Will work by forcing the operation into memory (where the ordering is preserved by GroupBy), though with the disadvantage of all subsequent operations being done in-memory rather than on a DB etc.
If you really DO have a list rather then an IEnumerable, it is actually possible to do this using the overload of List.Sort() which lets you specify a subset of items to sort, along with a comparer.
What you have to do is an O(N) traversal of the list to determine where each subgroup occurs according to the already-sorted column. Then sort each subgroup of more than one item according to the secondary sort column.
There's a small amount of fiddlyness involved with selecting the keys with which to identify the subgroups and the keys with which to sort the subgroups.
Here's the implementation:
public static void SortSubgroupsBy<T>
(
List<T> items,
Func<T, T, bool> sortedColumnComparer, // Used to compare the already-sorted column.
Func<T, T, int> unsortedColumnComparer // Used to compare the unsorted column.
)
{
var unsortedComparer = Comparer<T>.Create(
(x, y) => unsortedColumnComparer(x, y));
for (int i = 0; i < items.Count; ++i)
{
int j = i + 1;
while (j < items.Count && sortedColumnComparer(items[i], items[j]))
++j;
if ((j - i) > 1)
items.Sort(i, j-i, unsortedComparer);
}
}
Here's a complete demonstration in a Console app:
using System;
using System.Collections.Generic;
namespace ConsoleApplication1
{
class Item
{
public Item(int column1, int column2)
{
Column1 = column1;
Column2 = column2;
}
public int Column1;
public int Column2;
public override string ToString()
{
return $"[{Column1}, {Column2}]";
}
}
class Program
{
static void Main()
{
List<Item> items = new List<Item>
{
new Item(1, 72),
new Item(2, 29),
new Item(2, 30),
new Item(3, 27),
new Item(3, 38),
new Item(3, 53),
new Item(4, 72),
new Item(4, 21),
new Item(4, 86),
new Item(4, 17),
new Item(5, 90)
};
SortSubgroupsBy(
items,
(x, y) => x.Column1 == y.Column1, // Compare sorted column.
(x, y) => y.Column2 - x.Column2); // Compare unsorted column.
Console.WriteLine(string.Join("\n", items));
}
public static void SortSubgroupsBy<T>
(
List<T> items,
Func<T, T, bool> sortedColumnComparer, // Used to compare the already-sorted column.
Func<T, T, int> unsortedColumnComparer // Used to compare the unsorted column.
)
{
var unsortedComparer = Comparer<T>.Create(
(x, y) => unsortedColumnComparer(x, y));
for (int i = 0; i < items.Count; ++i)
{
int j = i + 1;
while (j < items.Count && sortedColumnComparer(items[i], items[j]))
++j;
if ((j - i) > 1)
items.Sort(i, j-i, unsortedComparer);
}
}
}
}
I have a list a simple Player object, as follows
Name | Team | Position | Total
Tom Brady | Team 1 | QB | 200
Adrian Peterson | Team 1 | RB | 250
Calvin Johnson | Team 2 | WR | 260
LeVon Bell | Team 2 | RB | 220
Peyton Manning | Team 3 | QB | 220
Arian Foster | Team 3 | RB | 220
This is a simple sample, in reality there are about 200 records. What I want to do is to get all possible combinations of players per team, and sum their total, so the end product would be as follows
Possibilities
Teams | Players | Total
Team 1 | Tom Brady, Adrian Peterson | 450
Team 2 | Calvin Johnson, LeVon Bell | 480
Team 3 | Peyton Manning, Arian Foster | 440
Basically I am looking for trade possibilities, so I need to get combinations of players per team. The largest possible combination I am looking for is 5 players per team, where I would have the Players and their points combined in a new object. Right now I can get there with below.
var playerList = players.GroupBy(p => p.Team)
.Select(g => new
{
Team = g.Key,
g
}).ToList();
List<Possibilities> possibilities = new List<Possibilities>();
foreach (var a in playerList)
{
List<Possibilities> onePlayer = (from b in a.g
select new Possibilities
{
Players = b.Name,
Total = b.Total,
Team = a.Team
}).ToList();
List<Possibilities> twoPlayer = (from b in a.g
from c in a.g
select new Possibilities
{
Players = b.Name + ", " + c.Name,
Total = b.Total + c.Total,
Team = a.Team
}).ToList();
And this gives me all combinations of 1,2,3 players per team, but I want to add 4 and 5. This also does not remove duplicate combinations (Player 1, Player 2 and Player 2,Player1). Is there a cleaner way to do this?
You can generate all combinations of a limited set of items (where the number of items is <= 31) by counting using a binary number. Each set bit in the binary number represents an item being present in the combination.
For example, counting (in binary) for a set of 3 items:
000, 001, 010, 011, 100, 101, 110, 111
A 1 in the binary number indicates that the corresponding item in the set should be included in that combination.
If you want to ensure that the combination includes number of items in a certain range, you need to count the bits set in the binary number and check if that count is in range. There's an efficient way to do that using bit twiddling (see here for examples).
Putting that together gives code like the following. Run it and check the output. Hopefully you can see how to use it with your program.
This example produces all combinations of between 2 and 3 items taken from A, B, C, D. Its output is:
A,B
A,C
B,C
A,B,C
A,D
B,D
A,B,D
C,D
A,C,D
B,C,D
The code is:
using System;
using System.Collections.Generic;
namespace ConsoleApplication1
{
public class Program
{
public static void Main()
{
var data = new [] {"A", "B", "C", "D"};
// Get all the combinations of elements from A,B,C,D with between 2 and 3 values:
var combinations = Combinations(data, 2, 3);
// Combinations() has returned an IEnumerable<IEnumerable<T>>,
// that is, a sequence of subsequences where each subsequence is one combination.
foreach (var combination in combinations)
Console.WriteLine(string.Join(",", combination));
}
public static IEnumerable<IEnumerable<T>> Combinations<T>(T[] input, int minElements, int maxElements)
{
int numCombinations = 2 << (input.Length - 1);
for (int bits = 0; bits < numCombinations; ++bits)
{
int bitCount = NumBitsSet(bits);
if (minElements <= bitCount && bitCount <= maxElements)
yield return combination(input, bits);
}
}
private static IEnumerable<T> combination<T>(T[] input, int bits)
{
for (int bit = 1, i = 0; i < input.Length; ++i, bit <<= 1)
if ((bits & bit) != 0)
yield return input[i];
}
public static int NumBitsSet(int i)
{
i = i - ((i >> 1) & 0x55555555);
i = (i & 0x33333333) + ((i >> 2) & 0x33333333);
return (((i + (i >> 4)) & 0x0F0F0F0F) * 0x01010101) >> 24;
}
}
}
Although probably very inefficient, the below might work. Of course that's just a simple example and you'll need to adjust the code to your situation.
static void Main(string[] args)
{
var nums = new[] { 1, 2, 3, 4, 5, 6 };
var combinations = new List<int[]>();
int[] current;
foreach (int i in nums)
{
combinations.Add(new[] { i });
foreach (int j in nums.Where(n => n != i))
{
current = new[] { i, j };
if (!combinations.Any(c => current.Length == c.Length && current.All(n => c.Contains(n))))
{
combinations.Add(current);
}
foreach (int k in nums.Where(n => n != i && n != j))
{
current = new[] { i, j, k };
if (!combinations.Any(c => current.Length == c.Length && current.All(n => c.Contains(n))))
{
combinations.Add(current);
}
foreach (int l in nums.Where(n => n != i && n != j && n != k))
{
current = new[] { i, j, k, l };
if (!combinations.Any(c => current.Length == c.Length && current.All(n => c.Contains(n))))
{
combinations.Add(current);
}
foreach (int m in nums.Where(n => n != i && n != j && n != k && n != l))
{
current = new[] { i, j, k, l, m };
if (!combinations.Any(c => current.Length == c.Length && current.All(n => c.Contains(n))))
{
combinations.Add(current);
}
}
}
}
}
}
foreach (var c in combinations)
{
foreach (var num in c)
{
Console.Write(num + " ");
}
Console.WriteLine();
}
Console.ReadKey();
}
I've imported a DataTable from a SQL Database using SqlDataAdapter and Fill-Method.
My datatable looks like this:
Timestamp(unix time) | Value
x | 10
x | 42
x | 643
y | 5
y | 9
y | 70
...and so on. The table contains a lot of values (1000+) but has always three rows with the same timestamp.
Now I want it to look like this:
Timestamp(unix time) | Value 1 | Value 2 | Value 3
x | 10 | 42 | 643
y | 5 | 9 | 70
How can I sort it this way?
(If there are more than three values, the programm should just insert the first three values it has found)
Thanks for any help!
Thanks for your approach! I solved it myself now.
This is how I've done it:
var grouped = from myRow in myDataTable.AsEnumerable()
group myRow by myRow.Field<int>("TIMESTAMP");
foreach (var timestamp in grouped)
{
string[] myRow = new string[5];
myRow[0] = timestamp.Key.ToString();
int i = 1;
foreach (var value in timestamp)
{
myRow[i] = value.Field<double>("VALUE").ToString();
i++;
if (i > 4)
break;
}
mySortedTable.Rows.Add(myRow);
}
I think this may also be solvable in SQL, but if you want to do it programmatically, I have tested the following in LinqPad:
void Main()
{
var list = new List<Tuple<string,int>> {
Tuple.Create("x", 10),
Tuple.Create("x", 42),
Tuple.Create("x", 643),
Tuple.Create("y", 5),
Tuple.Create("y", 9),
Tuple.Create("y", 70),
};
var result =
from grp in list.GroupBy(t => t.Item1)
let firstThree = grp.Select(t => t.Item2).Take(3).ToList()
select new {
Key = grp.Key,
Value1 = firstThree[0],
Value2 = firstThree[1],
Value3 = firstThree[2] };
foreach (var item in result)
Console.WriteLine(item);
}
It assumes that you have at least three elements, otherwise you'll get an out of range exception.
While the end result is an anonymous type, you could easily pipe the results of the operation into a DataRow instead.