Creating a multi-layered matrix-ish Collection in C# - c#

The setup
I have a List<Room>() which I get back from a service. The list refreshes every 10 seconds, and rooms get added and removed.
class Room
{
public int ID {get;set;}
}
My job
To display these rooms on the screen, I have a Matrix-like view of variable size.
Sometimes the matrix is 3 x 3 cells, other times it is 4 x 2 or 5 x 1.
I needed a way to "remember" which slot/cell a room has been placed in, so I thought a DataTable would give me that option.
To store the cells I use a DataTable, which has 3 Columns:
"Column" (int)
"Row" (int)
"Room" (Room)
So If I have a 2 x 4 matrix, it would look like this.
Column | Row | Room
-----------------------------
0 | 0 | rooms[0]
-----------------------------
1 | 0 | rooms[1]
-----------------------------
2 | 0 | rooms[2]
-----------------------------
0 | 1 | rooms[3]
-----------------------------
1 | 2 | rooms[4]
And so forth...
Once I have this DataTable I am then able to refresh the screen, knowing that each room will be displayed at the position it was before. This can probably be achieved in a smarter way.
The problem
Now I need to enumerate the List<Room> and fill the matrix/DataTable.
If I have more rooms than cells, then I need to start at position 0,0 again (like adding a new matrix as a layer), until all rooms have been assigned a cell.
The approach so far
I have tried a few for(...) loops that look like:
int totalTiles = area.TileColumns * area.TileRows;
int totalLayers = (int)Math.Ceiling((double)area.Rooms.Count / totalTiles);
for (int i = 0; i < totalLayers; i++)
{
for (int j = 0; j < area.TileRows; j++)
{
for (int k = 0; k < area.TileColumns; k++)
{
// This is going nowhere :-(
}
}
}
In my brain
When I first came across this problem, I immediately thought: "Nothing a simple LINQ query won't fix!". And then I bricked ...
What would be the most efficient / best performing approach to fill this matrix?

Without being able to make assumptions, like will the row/columns change at runtime, I would have to say just make it completely dynamic.
class RoomStorage
{
public Room room {get;set;}
public int layer {get;set;}
public int row {get;set;}
public int col {get;set;}
}
var matrix=new List<RoomStorage>();
Then you can things like:
var newRooms=new List<Room>(); // Get from service
//Remove rooms no longer in use
var matrix=matrix.Where(m=>newRooms.Select(nr=>nr.ID).Contains(m.Room.ID));
//Find rooms we need to add (Optionally use Exclude for faster perf)
var roomsToAdd=newRooms.Where(r=>matrix.Select(m=>m.Room.ID).Contains(r.ID));
var maxLayer=matrix.Max(m=>m.layer);
var rows = ?
var cols = ?
var positions=Enumerable
.Range(0,maxLayer+1)
.SelectMany(layer=>
Enumerable
.Range(0,rows)
.SelectMany(row=>
Enumerable
.Range(0,cols)
.Select(col=>new {layer,row,col})));
Then you can use positions, left joining it to matrix for display purposes, or finding the first empty position.

Related

What is the Algorithm should i use to maximize the number of tasks (with deadlines) that i can do?

I have many tasks, each task defined by the day that I can start working on and the last day that task is still valid to do, each task done withing one day, not more, I can do one task per day.
The tasks with the deadlines as described in the below table.
| task | valid from | valid until |
|------|------------|-------------|
| t01 | 1 | 3 |
| t02 | 2 | 2 |
| t03 | 1 | 1 |
| t04 | 2 | 3 |
| t05 | 2 | 3 |
the number of tasks may be a huge number.
I want to know which algorithm I can use to solve this problem to maximize the number of tasks that I can do.
Update
base on the comments I wrote this code it is working but still hasn't good performance with a huge number of tasks.
public static int countTodoTasks(int[] validFrom, int[] validUnitil)
{
var tasks = new List<TaskTodo>();
for (int i = 0; i < validFrom.Length; i++)
{
tasks.Add(new TaskTodo { ValidFrom = validFrom[i], ValidUntil = validUnitil[i] });
}
tasks = tasks.OrderBy(x => x.ValidUntil).ToList();
var lDay = 0;
var schedule = new Dictionary<int, TaskTodo>();
while (tasks.Count > 0)
{
lDay = findBigestMinimumOf(lDay, tasks[0].ValidFrom, tasks[0].ValidUntil);
if (lDay != -1)
{
schedule[lDay] = tasks[0];
}
tasks.RemoveAt(0);
tasks.RemoveAll(x => lDay >= x.ValidUntil);
}
return schedule.Count;
}
static int findBigestMinimumOf(int x, int start, int end)
{
if (start > x)
{
return start;
}
if ((x == start && start == end) || x == end || x > end)
{
return -1;
}
return x + 1;
}
If the tasks have the same duration, then use a greedy algorithm as described above.
If it's too slow, use indexes (= hashing) and incremental calculation to speed it up if you need to scale out.
Indexing: during setup, iterate through all tasks to create map (=dictionary?) that maps each due date to a list of tasks. Better yet, use a NavigableMap (TreeMap), so you can ask for tail iterator (all tasks starting from a specific due date, in order). The greedy algorithm can then use that to scale better (think a better bigO notation).
Incremental calculation: only calculate the delta's for each task you're considering.
If the tasks have different duration, a greedy algorithm (aka construction heuristic) won't give you the optimal solution. Then it's NP-hard. After the Construction Heuristic (= greedy algorithm), run a Local Search (such as Tabu Search). Libraries such as OptaPlanner (Java, not C# unfortunately - look for alternatives there) can do both for you.
Also note there are multiple greedy algo's (First Fit, Fit Fit Decreasing, ...)
I suppose you can apply greedy algorithm for you purpose in this way.
Select minimal "valid from", minday.
Add to Xcandidates, all candidates with "valid from" = minday.
If no Xcandidates go to 1.
Select the interval, x, from Xcandidates, with earliest "valid until".
Remove x, inserting it in your schedule.
Remove all Xcandidates with "valid until" = minday.
Increment minday and go to 2.

Accessing and extracting Data Frame Results - R.NET

I run a R script from C# and get the results in a dataframe as :
var OUTPUT = engine.GetSymbol("detail.dt").AsDataFrame();
OUTPUT dataframe has let's say 3 columns as:
NAME Month Rate
Rob 1 100
Rob 2 150
Rob 3 500
Ned 1 200
Ned 2 500
Sansa 1 500
Sansa 2 1000
I can extract individual column values as :
var Name = OUTPUT[0].AsEnumerable().ToList();
var Month = OUTPUT[1].AsNumeric().ToList();
var Rate = OUTPUT[2].AsNumeric().ToList();
My question is instead of extracting column by column values, I basically want to extract Month and Rate if user asks for "Rob" or "Sansa".
How do I extract Month and Rate values for a givenName?
Is there a better and faster way?
I would loop through the data.frame. Name appears to be duplicated in your data, but here's how you can get one of them:
string userInput = "Rob";
int myMonth = 0;
int myRate = 0;
for (int i = 0; i < OUTPUT.RowCount; ++i)
{
if (OUTPUT[i, 0].ToString() == userInput) {
myMonth = Convert.ToInt32(OUTPUT[i, 1].ToString());
myRate = Convert.ToInt32(OUTPUT[i, 2].ToString());
break;
}
}
If you need all of them, get rid of the break statement, and pile them into a list or whatever data structure that suits you.

Genetic Algorithm to store Items in 3 trucks, based on weight and value

I am currently Coding a Genetic algorithm to solve a problem that could be considered similar to the knapsack problem but the differentiating factor is that I am storing items over multiple "trucks", and their value/importance is based on a 1 = Most important, 3 = Least important.
How i am doing this is with an array of binary integers, 0 = not on the truck, 1 = on the truck.
For the most part, the program seems to be doing what it needs to do. Except when I am comparing the chromosomes to find the best three individual chromosomes for each generation. Individual being that no chromosomes can contain the same item, As the item can only be on one truck at a time.
This is the function I am using to compare the chromosomes:
private int[] getBestSolution(int count)
{
int[] Genes = new int[3];
int min1 = Global.p.population[0].fitness;
int min2 = Global.p.population[0].fitness;
int min3 = Global.p.population[0].fitness;
int ref1 = 0;
int ref2 = 0;
int ref3 = 0;
bool areSame = true;
//searches for the first "Fittest" chromosome and stores to ref1
for (int i = 0; i < Global.population; i++)
{
if (min1 > Global.p.population[i].fitness)
{
min1 = Global.p.population[i].fitness;
ref1 = i;
}
}
//searches for 2nd fittest, while also checking they are different
for (int i = 0; i < Global.population; i++)
{
areSame = arrayCompare(Global.p.population[ref1].chromosome, Global.p.population[i].chromosome);
if(areSame == true)
{
continue;
}
if (min2 > Global.p.population[i].fitness)
{
min2 = Global.p.population[i].fitness;
ref2 = i;
}
}
//Searches for 3rd fittest while checking that it is different from the first two
for (int i = 0; i < Global.population; i++)
{
areSame = arrayCompare(Global.p.population[ref1].chromosome, Global.p.population[i].chromosome);
if (areSame == true)
{
continue;
}
areSame = arrayCompare(Global.p.population[ref2].chromosome, Global.p.population[i].chromosome);
if(areSame == true)
{
continue;
}
if (min3 > Global.p.population[i].fitness)
{
min3 = Global.p.population[i].fitness;
ref3 = i;
}
}
//stores the reference of each chromosome and return
Genes[0] = ref1;
Genes[1] = ref2;
Genes[2] = ref3;
return Genes;
}
This is the function i am using to compare the chromosome to the Previously selected chromosome to ensure they do not containt the same value.
public bool arrayCompare(int[] a, int[] b)
{
for (int i = 0; i < a.Length; i++)
{
if ((a[i] == 1) && b[i] == 1)
{
return true;
}
}
return false;
}
I have narrowed it down to these two fuctions causing the problem by using breakpoints at different points of the code and checking what the values are vs what they are expected to be.
Finally, to the problem itself. The getBestSolution() produces the first "fittest" value perfectly. The second and third values stay as what they were initialized as "population[0].fitness" and that is displayed.
I attempted to change the breeding structure, the selection criteria for breeding as I believed that if there were no chromosomes containing different values, it would stay as initialized. I have also tested using larger populations for this same reason, so i believe that their must be a problem with my code.
Any help is much appreciated.
1. A note about your code.
You should initialize min1, min2, and min3, to a value higher than the maximum that fitness can be, because in the case Global.p.population[0].fitness is the best fitness in the population, you'll never find the second and third bests, because in this case no individual will have a fitness lower than Global.p.population[0].fitness.
2. Probabilities about the genes your are using.
I may be wrong but here is my theory on the problem you described.
In a random distribution, for 1 gene, there is a total of 4 possible combinations of the genes' values for two individuals. On these 4 combinations, there are 3 where the two individuals don't have in common this gene set to 1.
Individual 1 Individual 2 Match criterion Probability
Combination 1: 0 0 Yes 25% |
Combination 2: 0 1 Yes 25% | 75%
Combination 3: 1 0 Yes 25% |
Combination 4: 1 1 No 25%
It means that for individuals that have only one gene, you have 75% chances to find two individuals that match the criterion. In other words, if you compare the genes of 100 individuals, you will find in average 75 pairs of individuals that match the criterion. But there will be some populations where you won't find them, and the most of populations where you will find them.
As the number of genes grow, the percentage decrease. Each time you add 1 gene to the chromosome, you have to multiply by 75/100 the percentage of individuals matching the criterion. Each time you add 2 genes, you have to multiply the percentage by 56,25/100.
Number of genes percentage of individuals matching the criterion
1 75,00%
2 56,25% = 75,00 * 75,00 / 100
4 31,64% = 56,25 * 56,25 / 100
6 17,79% = 31,64 * 56,25 / 100
8 10,01% = 17,79 * 56,25 / 100
[...]
16 1%
It means that for a chromosome of 16 genes, and a population of 100, you will find in average 1 pair of individuals matching the criterion. The percentage is even smaller if you want 3 individuals matching the criterion. For 6 genes, the probability to find these 3 individuals should be arround 1,5%.
So the problem is that in a population, there isn't enough pairs of individuals that are matching the criterion.
3. Another way to design the algorithm.
In your algorithm, you seem to consider an individual as a truck. I would design the algorithm in another way.
I would consider a gene as the truck number for an item, and if needed, 0 to indicate the item has no assigned truck:
gene[0] = 1 (item 0 is in truck 1)
gene[1] = 0 (item 1 is not in any truck)
gene[2] = 3 (item 2 is in truck 3)
gene[3] = 2 (item 3 is in truck 2)
gene[4] = 1 (item 4 is in truck 1)
With this coding of the information, the second and third best individuals can have one item in the same truck than the best. For example:
first best individual:
----------------------
truck1: item1 item2
truck2: item3 item4
truck3: item5 item6 item7
not in any truck: item0
second best individual:
-----------------------
truck1: item1 item3
truck2: item2 item5
truck3: item4 item6
not in any truck: item0 item7
In this example, item1 is always in truck1, and item6 is always in truck3.
So it is possible to select the second and third best individuals without comparing the genes, just checking the fitness.

Best Algorithm to find intersection between 2 Intervals

I have a table in the database called Control:
Table structure:
Id | Name | MinValue (decimal) | MaxValue(decimal)
I have some restrictions on that table, one of it's restrictions is : no intersections.
Example : if the table has some values as follows:
row1 : 1 | Test1 | 1.3 | 2.5 //valid
row2 : 2 | Test2 | 3.3 | 4.5 // valid
row3 : 3 | Test3 | 5 | 6 // valid
Now if I want to add a new record, it must not intersect with any other row
Example:
row4 : 4 | Test4 | 5.1 | 10 //not valid since slot from 5 to 6 is reserved
row5 : 5 | Test5 | 1.0 | 1.4 // not valid since slot from 1.3 to 2.5 is reserved
I'm using this code, and it worked perfectly, but I wonder if there is a better solution and more efficient :
var allRows = db.Control.ToList();
var minValue = control.MinimumValue;
var maxValue = control.MaximumValue;
bool flag = true;
foreach(var row in allRows)
{
for(var i = minValue; i <= maxValue && flag ; i = decimal.Add( i , (decimal) 0.01))
{
if(i >= row.MinimumValue && i <= row.MaximumValue)
{
flag = false;
min = row.MinimumValue;
max = row.MaximumValue;
break;
}
}
}
if (flag)
{
//add
}
else
{
//intersection
}
Any suggestions ?
I think this is a O(LogN) issue...
Keep segments Ordered by their Start Value.
in a valid list s[i].end < s[i+1].start for any i
when inserting new segment, find it's position (the one that which start is closest (but lesser) than your new segment) call it i
if((seg[i-1].end < new.start) && (seg[i+1].start > new.end))
//OK to insert
else
// intersect
Let's assume this is the object you're trying to add :
var control = new Control()
{
Name = 'name',
MinValue = 5,
MaxValue = 6
};
You can do the following:
var biggerThanMinValue = db.Control.Count(x => x.MinValue >= control.MinValue) != 0;
var biggerThanMaxValue = db.Control.Count(x => x.MaxValue >= control.MaxValue) != 0;
if (!biggerThanMinValue && !biggerThanMinValue)
{
db.Control.Add(control); // or whatever your add operation is
}
By doing so you:
do NOT load the whole table in-memory -> performance gain terms of time, traffic and memory
let the database use it's data structures/algorithms to verify that the item can be added (the db should be able to optimize this request) -> another performance gain (cpu + time)
have clearer backend code
have less code to test
Edit: I suppose you could also ask the database to sort your table by min/max value and then make some validation (1 or 2 ifs), but the first approach is better, imo.

Merge two reports to create a four column single report

This is related to a GameBetting related Project.
We have Two simple Lists of following Class.
class Gameresults
{
int userid,
double amount
}
The following two reports are printed on paper.
A) Game Winners
uid amount
10 -
14 -
15 -
B) Game Losers
uid amount
11 -
12 -
13 -
16 -
Since the columns in such report leaves space across
the width of a Paper, we have to merge both the reports and
prepare the following report
Game Winners Game Losers
uid Amount uid Amount | uid Amount uid Amount
10 - 15 - 11 - 13 -
14 - 12 - 16 -
The above report has two columns for each report.
The Row length of WinnerReport is totalrecords/2 , therefore two records in first
column and 1 record in next column
So first the right column is filled, rest goes to the left column
Same for LosersReport
the Rowlength of Winner vs Losers reports is not important.
They however must be equal (+/- 1) with respect to record count in their
respective Lists
I made a Class to put all the records in one row, as we use printer for output of the report
class MergeRow
{
int uidWinCol1; // userid,amount of winner on column 1
double amtWinCol1;
int uidWinCol2; // userid.amount of winner on column 2
double amtWinCol2;
int uidLosCol1;
double amtLosCol1;
int uidLosCol2;
double amtLosCol2;
}
I need advice for the part on how to merge both lists, i presume such
a method is possible only in Linq, but any pointer or link will be helpful.
thank you
You can split the winnersList and losersList, each into 2 halves. Thus you would have 4 sublists.
Now you can use FirstOrDefault on each sublist to get an instance ofMergeRow. In case one of the sublists become empty before the others, use DefaultIfEmpty, with a placeholder item.
The code would look like:
var winnersList = new List<Gameresults>();
var losersList = new List<Gameresults>();
//Populate the winnersList and losersList
var winnersList1 = winnersList.Take(winnersList.Count/2).ToList();
var winnersList2 = winnersList;
var losersList1 = losersList.Take(losersList.Count/2).ToList();
var losersList2 = losersList;
var allLists = new List<List<Gameresults>> {winnersList1, winnersList2, losersList1, losersList2};
var mergeRows = new List<MergeRow>();
while (allLists.Any(l => l.Count > 0))
{
var resultsInOneRow = allLists.Select(l => l.DefaultIfEmpty(new Gameresults()).FirstOrDefault()).ToList();
mergeRows.Add(GetMergeRow(resultsInOneRow));
}
Your GetMergeRow() method would look like:
private MergeRow GetMergeRow(List<Gameresults> recordsToMerge)
{
var mergeRow = new MergeRow();
mergeRow.uidWinCol1 = recordsToMerge[0].userid;
mergeRow.amtWinCol1 = recordsToMerge[0].amount;
//... and so on
return mergeRow;
}

Categories

Resources