copy items from a list on one condition - c#

I am trying to copy items from a list to another list on one condition.
I have three lists. First list contains for example 10 lists of points, second list contains the total distance (cost or fitness) of each list (10 lists -> 10 total distances).
Here a picture:
first list contains 10 lists (each list contains points) - second list 'fitness'
Third list is empty and should be filled with items on one condition. First I added up all values in the second list.
Example with the numbers above: totalFitness = 4847 + 5153 + 5577 + 5324...
The condition to add list of points out of the first List to the third list is:
for example ----------> (Fitness[0] / totalFitness) <= ratio.
But it is not working, here you can see the code I tried:
class RunGA
{
public static List<List<Point3d>> createGenerations(List<List<Point3d>> firstGeneration, List<int> firstFitness, int generationSize)
{
List<List<Point3d>> currentGeneration = new List<List<Point3d>>();
int totalFitness;
int actualFitness;
totalFitness = firstFitness[0] + firstFitness[1];
double ratio = 1 / 10;
for(int k = 2; k < firstFitness.Count; k++)
{
actualFitness = firstFitness[k];
totalFitness += actualFitness;
}
for(int i = 0; i < firstFitness.Count; i++)
{
double selected = firstFitness[i] / totalFitness;
if(selected < ratio)
{
currentGeneration.Add(firstGeneration[i]);
}
}
return currentGeneration;
}
}
The third list is still empty. If I change the condition to: if(selected <= ratio)
then the whole list of points in the first list are being copied to the third list. However what I want to copy is: the list of points which has the 'best' fitness.
What is it that I'm doing wrong? I have absolutely no clue and I tried already a few changes, but it is still not working. I would appreciate it if you can consider that I'm a beginner.

I found another solution for this problem.
I still have these data:
List1:
ListOfPoints = a
ListOfPoints = b
ListOfPoints = c
ListOfPoints = d
List2:
Fitness of a
Fitness of b
Fitness of c
Fitness of d
What I wanted to achieve was: Take those ListOfPoints, which has the best Fitness and put them into a List3. All the rest ListOfPoints, put them into another List4.
This is the solution I thought of:
put List1 as Keys and List2 as Values into a Dictionary and sort it via LINQ. Now transfer the sorted Keys into a List3. With a for- Loop put the first half of the sorted List into List4 and the second half into List5.
Here is my Code:
List<List<Point3d>> currentGeneration = handoverPopulation.ToList();
List<double> currentFitness = handoverFitness.ToList();
Dictionary<List<Point3d>, double> dict = new Dictionary<List<Point3d>, double>();
foreach(List<Point3d> key in currentGeneration)
{
foreach(double valuee in currentFitness)
{
if(!dict.ContainsKey(key))
{
if(!dict.ContainsValue(valuee))
{dict.Add(key, valuee);}
}
}
}
var item = from pair in dict orderby pair.Value ascending select pair;
List<List<Point3d>> currentGenerationSorted = new List<List<Point3d>>();
currentGenerationSorted = item.Select(kvp => kvp.Key).ToList();
List<List<Point3d>> newGeneration = new List<List<Point3d>>();
List<List<Point3d>> newGenerationExtra = new List<List<Point3d>>();
int p = currentGenerationSorted.Count / 2;
for(int i = 0; i < p; i++)
{newGeneration.Add(currentGenerationSorted[i]);}
for(int j = p; j < currentGenerationSorted.Count; j++)
{newGenerationExtra.Add(currentGenerationSorted[j]);}
Hope this helps others who face the same problem.

Related

How to create unique pairs of numbers

I'm trying to create some pairs of unique numbers using pretty simple algorithm.
For some unknown reason after compiling Unity goes into an endless "not responding" state. Seems like it's stuck in a do..while loop, but I don't see any reason for that.
//Creating two lists to store random numbers
List<int> xList = new List<int>();
List<int> yList = new List<int>();
int rx, ry;
for(int i = 0; i < 10; i++)
{
// look for numbers until they are unique(while they are in lists)
do
{
rx = rand.Next(0, width);
ry = rand.Next(0, height);
}
while(xList.Contains(rx) || yList.Contains(ry));
//add them to lists
xList.Add(rx);
yList.Add(ry);
Debug.Log(rx + ", " + ry);
// some actions with these numbers
gridArray[rx,ry].isBomb = true;
gridArray[rx,ry].changeSprite(bombSprite);
}
As mentioned the issue is that once all unique numbers have been used once you are stuck in the do - while loop.
Instead you should rather simply
generate the plain index lists for all possible pairs.
I will use the Unit built-in type Vector2Int but you could do the same using your own struct/class
For each bomb to place pick a random entry from the list of pairs
Remove according random picked item from the pairs so it is not available anymore in the next go
Something like
// create the plain pair list
var pairs = new List<Vector2Int>(width * height);
for(var x = 0; x < width; x++)
{
for(var y = 0; y < height; y++)
{
pairs.Add(new Vector2Int(x,y));
}
}
// so now you have all possible permutations in one list
if(pairs.Count < BOMB_AMOUNT_TO_PLACE)
{
Debug.LogError("You are trying more bombs than there are fields in the grid!");
return;
}
// Now place your bombs one by one on a random spot in the grid
for(var i = 0; i < BOMB_AMOUNT_TO_PLACE; i++)
{
// now all you need to do is pick one random index from the possible entries
var randomIndexInPairs = Random.Range(0, pairs.Count);
var randomPair = pairs[randomIndexInPairs];
// and at the same time remove the according entry
pairs.RemoveAt(randomIndexInPairs);
// Now you have completely unique but random index pairs
var rx = randomPair.x;
var ry = randomPair.y;
gridArray[rx, ry].isBomb = true;
gridArray[rx, ry].changeSprite(bombSprite);
}
Depending on your use-case as alternative to generate the pairs list and then remove entries again you could also generate it once and then use
if(pairs.Count < BOMB_AMOUNT_TO_PLACE)
{
Debug.LogError("You are trying more bombs than there are fields in the grid!");
return;
}
var random = new System.Random();
var shuffledPairs = pairs.OrderBy(e => random.Next());
for(var i = 0; i < BOMB_AMOUNT_TO_PLACE; i++)
{
// then you can directly use
var randomPair = shuffledPairs[i];
// Now you have completely unique but random index pairs
var rx = randomPair.x;
var ry = randomPair.y;
gridArray[rx, ry].isBomb = true;
gridArray[rx, ry].changeSprite(bombSprite);
}
Although your algorithm is maybe not the best way to generate ten bombs in a grid, it should work.
The problem is that your while condition is using a OR statement, which means that if you have a bomb in the first line (in any column), it will not be able to add another bomb in that line.
Therefore you will pretty soon end up with an infinite loop because for every bomb you lock the line and column.
If you put an AND condition, you make sure the pair is unique because you lock only that cell.
Provided of course that width x height is more than ten.

Adding the calculated sum of a list to another list

I have a list of int .. How can i sum all the numbers in the list and get the result ?
List<int> FineListy = new List<int>();
Your code has a number of issues.
List<int> FineListy = new List<int>();
for (int i = 0; i < fineList.Count(); i++)
{
if (fineList[i] > 0)
{
FineListy.Add((fineList[i] += fineList[i]));
}
}
Firstly: C# naming conventions are such that local variables should start with lowercase letters and be camelCased. I recommend naming your variables totalsList and fineList respectively. For the sake of simplicity, I will use your current naming conventions below.
Next, you're doing FineListy.Add(fineList[i] += fineList[i]); which is the same as:
fineList[i] = fineList[i] * 2;
FineListy.Add(fineList[i]);
And you're doing it in a loop, so you will simply get a list of all items multiplied by 2.
Now you could fix this like so:
int total = 0;
for (int i = 0; i < fineList.Count; ++i)
{
if (fineList[i] > 0)
{
total += fineList[i];
}
}
FineListy.Add(total);
But you can use LINQ to do the same in a single line (I've split it across multiple lines to make it easier to read):
var total = fineList
.Where(v => v > 0)
.Sum();
FineListy.Add(total);
Or simply:
FineListy.Add(fineList.Where(v => v > 0).Sum());
If you have a list of int, why not use sum function??
int sum = FineListy.Sum();
This will add up all the numbers and give you the expected result.Now i see you do an If check to see if the number is not 0.So,create a new list then and pass the numbers to the list only if it's greater than 0
List<int> NewList = new List<int>;
foreach (var number in IntegerList)
{
if (number > 0)
{
NewList.Add(number);
}
}
Finally get the sum total :
int sum = NewList.Sum();
Or one-line LINQ solution :
var result = fineList.Where(a => a > 0).Sum();
NewList.Add(result );
Yes, it doubles, because that's what you do here :
FineListy.Add((fineList[i] += fineList[i]));
You say : "Add me fineList[i] + fineList[i] to my resulting collection FineListy"
So you got all elements doubled.
If you want to add values you don't need list just a variable to store it and LINQ .Sum() method
P.S.
I mean either one of these two (as others suggested):
FineListy.Add(fineList.Sum());
Or
int sum = fineList.Sum();

How to performantly get every (unordered) pair of different collection elements if random access is not available

Example: I have the collection {1, 2, 3, 4}.
I want to get all (unordered) pairs of different elements, which are:
{1,2}, {1,3}, {1,4}, {2,3}, {2,4}, {3,4}.
If I have an IList, I can do it like this:
IList<MyType> list = ... // fill the list elements
for (int i = 0; i < list.Count - 1; i++)
for (int j = i+1; j < list.Count; j++)
{
... // now we have the pair of list[i] and list[j]
}
For a LinkedList I also know how to do it; it is almost the same except that instead of indexes i and j, we have two LinkedListNodes fst and snd. Whenever we call fst.Next, we will then set snd = fst.Next.
For a list of n elements, the above approach takes (n-1)*n/2 iteration steps. (For i = 0 we have j = 1 ... n-1, so n-1 steps. For i = 1 we have j = 2 ... n-1, so n-2 steps. And so on. This sums up to (n-1) + (n-2) + ... + 3 + 2 + 1 = (n-1)*n/2 steps.)
Is there a way to do it with any ICollection? I thought an IEnumerator could do the trick, but it seems there is no way to tell an IEnumerator "Move to that reference!" like I can do with LinkedListNodes.
EDIT:
I am NOT looking for this solution:
foreach (MyType a in list)
foreach (MyType b in list)
{
if (a == b)
continue;
... // now we have the pair of a and b
}
For a collection of n elements, this approach takes n^2 iteration steps which is a bit more than double than the above approach, so it is clearly not as performant.
EDIT:
Turning the collection into a List beforehand seems to be the easiest way, and the performance penalty is negligible for large n, because it only adds n steps to the whole interation (the new list must be filled by the n elements). So I'll just stick with it.
IEnumerable is a forward-only iterator; ICollection does not have indexed access. What you could do is get the enumerable into a buffer during the first iteration, and afterwards use the nested for loops.
var enumerable = Enumerable.Range(0, 10);
var buffer = new List<int>();
using (var enumerator = enumerable.GetEnumerator())
{
if (enumerator.MoveNext())
{
buffer.Add(enumerator.Current);
while (enumerator.MoveNext())
{
var current = enumerator.Current;
buffer.Add(current);
Handle(buffer[0], current);
}
}
}
for (int i = 1; i < buffer.Count - 1; i++)
for (int j = i + 1; j < buffer.Count; j++)
Handle(buffer[i], buffer[j]);
Alternatively, if you don't care about going through the items one more time, you could just use enumerable.ToArray() and then use the nested for on that array.
Edit: Based on the discussion in comments and the edited question it seems that copying the enumerable in a list is the best course of action.
Old answer:
It seems to me that you need to put the items into some kind of hashtable data structure.
Here is a LINQ based solution that uses GroupBy
var items = new List<int> { 1, 2, 3, 2, 3 };
var groupedItems = from i in items
group i by i into g
select g;
foreach (var group in groupedItems)
{
//group.Key stores the key of the grouping
foreach (var item in group) //group contains items with the same key
{
//Do something
Console.WriteLine(item);
}
}
Here each group contains items with the same key (in this example because the item is an int the item is the key but you can group by whatever expression you want)
Alternatively you can group stuff on your own using a dictionary
var items = new List<int> { 1, 2, 3, 2, 3 };
var itemsDictionary = new Dictionary<int, List<string>>();
foreach (int i in items)
{
List<string> repeatedValues;
if(itemsDictionary.TryGetValue(i, out repeatedValues))
{
repeatedValues.Add(i.ToString());
}
else
{
itemsDictionary.Add(i, new List<string> { i.ToString() });
}
}
foreach (KeyValuePair<int, List<string>> kvp in itemsDictionary)
{
//do whatever is needed kvp.Value
}
In this example I've used a string to emulate a key of int and a value of different type. I think both these solutions are NlogN
Alternatively - sort the collection in a list - all equal elements will be placed one after another. This will be NlogN solution.

How can I mix elements from two list and store them is another list using C#?

I basically have two lists: list1 and list2 which each of them contains bitmap images as elements.
My question is how I can select bitmap elements from both list randomly and mix them together and store them is another list “list3”.
List<Bitmap> list1 = new List<Bitmap>();
List<Bitmap> list2 = new List<Bitmap>();
(List3 has elements which are mixing of list1 and list2 randomly and the size of the two lists are varying depending on the number of produced images)
This answer focuses on Randomly Interleaving two lists with minimal bias.
It is important to use an appropriate algorithm and is a significant bias when using rnd.Next(2) == 0 when interleaving between two unequal-length lists. This bias is very obvious when interleaving a list of 2 elements and a list of 20 elements - the elements in the shorter list will be clustered near the front of the result. While such a bias is not always readily seen, it exists between any two unequal-length lists without taking the "weights" of the lists into account.
Thus, instead of using rnd.Next(2) == 0 to pick the source list, an unbiased implementation should pick fairly between all the remaining elements.
if (randInt(remaining(l1) + remaining(l2)) < remaining(l1)) {
// take from list 1
// (also implies list 1 has elements; as rand < 0 never matches)
} else {
// take from list 2
// (also implies list 2 has elements)
}
An implementation might look like this:
IEnumerable<T> RandomInterleave<T>(IEnumerable<T> a, IEnumerable<T> b) {
var rnd = new Random();
int aRem = a.Count();
int bRem = b.Count();
while (aRem > 0 || bRem > 0) {
var i = rnd.Next(aRem + bRem);
if (i < aRem) {
yield return a.First();
a = a.Skip(1);
aRem--;
} else {
yield return b.First();
b = b.Skip(1);
bRem--;
}
}
}
var list3 = RandomInterleave(list1, list2).ToList();
And again, an example for you.
List<int> list1 = new List<int>();
List<int> list2 = new List<int>();
List<int> list3 = new List<int>();
// putting some values into the lists to mix them
for (int i=0; i<50; i++) list1.Add(1);
for (int i=0; i<40; i++) list2.Add(2);
var rnd = new Random();
int listIndex1 = 0;
int listIndex2 = 0;
while (listIndex1 < list1.Count || listIndex2 < list2.Count)
{
if (rnd.Next(2) == 0 || listIndex2 >= list2.Count)
{
list3.Add(list1[listIndex1++]);
}
else
{
list3.Add(list2[listIndex2++]);
}
}
foreach (var mixed in list3) { Console.WriteLine(mixed); }
Output:
1
1
2
2
2
2
1
2
2
1
1
2
2
2
1
...

How to increment the index of a list for saving the next number in next index of list

I want to save the result of zarb function which repeats for 1000 times in a list with size 1000. Then I must to increase the index of the list for every calculation to avoid to save the next calculation at the same index of previous one. How can I do that?
var results = new List<float>(1000);
for (int z = 0; z < 1000; z++)
{
results.Add(zarb(sc,z));
//increase the index of resukts
}
foreach (var resultwithindex in results.Select((r, index) => new { result = r, Index = index }).OrderByDescending(r => r.result).Take(20))
{
MessageBox.Show(string.Format("{0}: {1}", resultwithindex.Index, resultwithindex.result));
}
Zarb function
public float zarb(int userid, int itemid)
{
float[] u_f = a[userid];
float[] i_f = b[itemid];
for (int i = 0; i < u_f.Length; i++)
{
result += u_f[i] * i_f[i];
}
return result;
}
No you don't. The Add method (surprisingly) adds an item into the list. It doesn't replace anything. You should read MSDN documentation for List<T>. Also, don't be afraid of trying and seeing the results before asking—you'll save time.
Maybe I don't understand the question, but you do not need index for list. The add method will deal with the it.

Categories

Resources