Every combination of "1 item from each of N collections" - c#

I have a nested List<List<int>> data structure, and I would like to iterate over every possible combination of the inmost int elements, such as that in each combination exactly one value from each inner List<int> is used. For example, please consider the following nested list:
var listOfLists = new List<List<int>>()
{
new List<int>() { 1, 2, 3, 4, 9 },
new List<int>() { 0, 3, 4, 5 },
new List<int>() { 1, 6 }
};
The first few combinations would yield:
1 0 1 // Indices: 0 0 0
1 0 6 // Indices: 0 0 1
1 3 1 // Indices: 0 1 0
1 3 6 // Indices: 0 1 1
2 0 1 // Indices: 1 0 0
2 0 6 // Indices: 1 0 1
2 3 1 // Indices: 1 1 0
...
How could I accomplish this?
My initial approach was to make permutations of indices, but the lengths of inner List<int> lists are not necessarily equal. Another approach I can think of is multiplying the length of each inner List<int>, then using the modulo and division operators combined with Math.Floor to determine indices, but I'm not sure how exactly this could be implemented when N collections are present.

I've answered a several similar questions, which all basically use a variation of one and the same algorithm. Here is the modified version of the Looking at each combination in jagged array:
public static class Algorithms
{
public static IEnumerable<T[]> GetCombinations<T>(this IReadOnlyList<IReadOnlyList<T>> input)
{
var result = new T[input.Count];
var indices = new int[result.Length];
for (int pos = 0, index = 0; ;)
{
for (; pos < result.Length; pos++, index = 0)
{
indices[pos] = index;
result[pos] = input[pos][index];
}
yield return result;
do
{
if (pos == 0) yield break;
index = indices[--pos] + 1;
}
while (index >= input[pos].Count);
}
}
}
Note that in order to not do allocation, the above method yields one and the same array instance. This is perfect if you want just to count or process it with foreach loop or LINQ query without storing the results. For instance:
foreach (var combination in listOfLists.GetCombinations())
{
// do something with the combination
}
If you indeed need to store the results, you can always use ToList:
var allCombinations = listOfLists.GetCombinations().Select(c => c.ToList()).ToList();

How about using LINQ?
var listOfLists = new List<List<int>>()
{
new List<int>() { 1, 2, 3, 4, 9 },
new List<int>() { 0, 3, 4, 5 },
new List<int>() { 1, 6 }
};
var result = from l in listOfLists[0]
from y in listOfLists[1]
from z in listOfLists[2]
select new List<int>()
{
l,
y,
z
};
This will of course only work for this specific list as there are 3 lists in your list of lists.

Related

Get all subset sof given array with 'cost' restiction

I have a set of typed elements and price for each type
var array = new []
{
new Elem(0, Types.LowCost),
new Elem(1, Types.MediumCost),
new Elem(2, Types.MediumCost),
new Elem(3, Types.HightCost),
}
And prices: LowCost - 3, MediumCost - 5, HightCost - 9
How would you find all possible unique combinations of elements with restriction "sum of costs for all elements doesn't exceed a restriction"?
For example for MaxCost = 13 I expect
Elem(0) //cost 3
Elem(1) // 5
Elem(2) // 5
Elem(3) // 9
Elem(0), Elem(1) //cost 3+5=8
Elem(0), Elem(2) // 3+5=8
Elem(0), Elem(3) // 3+9=12
Elem(1), Elem(2) // 5+5 = 10
Elem(0), Elem(1), Elem(2) // cost 13
Given a dictionary of costs:
public Dictionary<Types, int> costs = new Dictionary<Types, int>()
{
{ Types.LowCost, 3 },
{ Types.MediumCost, 5 },
{ Types.HightCost, 9 },
};
I can do this:
var query =
from n in Enumerable.Range(0, 1 << array.Length).Skip(1)
let combination = array.Where((x, i) => ((n >> i) & 1) == 1).ToArray()
let cost = combination.Select(x => costs[x.Type]).Sum()
where cost <= 13
select String.Join(", ", combination.Select(x => x.Id));
That gives me:
0
1
0, 1
2
0, 2
1, 2
0, 1, 2
3
0, 3

How to sort a List of integer arrays of varying size?

I've done some extensive searching for this so if this is a duplicate please slaughter me :D
I have a List of byte arrays (List) where the arrays are of varying length. I need to sort the list by the array lengths in ascending order then by the bytes in the array (please see example).
Example:
I want to go from:
{0,1,2}
{0,4}
{0,3,2}
{0,1,3}
{0,2,4,6,1}
{0,1,1}
{0,3,4,5}
to:
{0,4}
{0,1,1}
{0,1,2}
{0,1,3}
{0,3,2}
{0,3,4,5}
{0,2,4,6,1}
It's essentially alphabetical order but with a set of numbers instead of characters (arguably the same thing), any ideas?
The only thing you need to do is implement a IComparer<T> interface and provide that to the sorting algorithm. In this case the algorithm looks like:
public ByteArrayComparer : IComparer<byte[]> {
public int Compare (byte[] ba, byte[] bb) {
int n = ba.Length; //fetch the length of the first array
int ci = n.CompareTo(bb.Length); //compare to the second
if(ci != 0) { //if not equal return the compare result
return ci;
} else { //else elementwise comparer
for(int i = 0; i < n; i++) {
if(ba[i] != bb[i]) { //if not equal element, return compare result
return ba[i].CompareTo(bb[i]);
}
}
return 0; //if all equal, return 0
}
}
}
Next you can use the List<T>.Sort method:
List<byte[]> data = new List<byte[]>();
//add arrays to data
data.Sort(new ByteArrayComparer());
//data is now sorted
The sorting algorithm requires that the comparator is valid, a comparator is valid if it satisfies the three constraints on an ordering relation:
Reflexivity: if an elements is compared with itself, return 0;
Anti-symmetric: If x is smaller than y (return something less than 0), then y is greater than x (something greater than 0);
Transitive: if x is smaller than y and y is smaller than z, then x is smaller than z.
If the comparer doesn't satisfy that relation, the sorting algorithm will fail to sort correctly, simply because your order makes no sense.
Why not simply use LINQ
MyList = MyList.OrderBy(arr=>arr.Length).ThenBy(arr =>arr.Sum()).ToList();
A working example :
List<int[]> a = new List<int[]>();
int[] t1 = { 0, 4 };
int[] t2 = { 0, 1, 2 };
int[] t3 = { 0, 1, 3 };
int[] t4 = { 0, 2, 4, 6, 1 };
int[] t5 = { 0, 1, 1 };
int[] t6 = { 0, 3, 4, 5 };
a.Add(t1);
a.Add(t2);
a.Add(t3);
a.Add(t4);
a.Add(t5);
a.Add(t6);
a = a.OrderBy(arr=>arr.Length).ThenBy(arr =>arr.Sum()).ToList();
foreach (int[] item in a)
{
foreach (int item2 in item)
{
Console.Write(" "+item2);
}
Console.WriteLine();
}
Sample output :
0 4
0 1
0 1 2
0 1 3
0 3 4 5
0 2 4 6 1
And as pointed out this could fail in scenarios like {3 4 5} , {4 5 3}

Compare two List<int>

I am writing a small program to compare two List. If the values are the same, I add them to list dups, if they are different, I add them to distinct. I noticed that some of my values are added and some are not, and after debugging for awhile, I am not certain what the problem is. Can someone shed a little light? Thanks.
List<int> groupA = new List<int>();
List<int> groupB = new List<int>();
List<int> dups = new List<int>();
List<int> distinct = new List<int>();
groupA.Add(2);
groupA.Add(24);
groupA.Add(5);
groupA.Add(72);
groupA.Add(276);
groupA.Add(42);
groupA.Add(92);
groupA.Add(95);
groupA.Add(266);
groupA.Add(42);
groupA.Add(92);
groupB.Add(5);
groupB.Add(42);
groupB.Add(95);
groupA.Sort();
groupB.Sort();
for (int a = 0; a < groupA.Count; a++)
{
for (int b = 0; b < groupB.Count; b++)
{
groupA[a].CompareTo(groupB[b]);
if (groupA[a] == groupB[b])
{
dups.Add(groupA[a]);
groupA.Remove(groupA[a]);
groupB.Remove(groupB[b]);
}
}
distinct.Add(groupA[a]);
}
I would use the Intersect and Except methods:
dups = groupA.Intersect(groupB).ToList();
distinct = groupA.Except(groupB).ToList();
When you remove an item from a list, you move the index of the remaining element down.
In essence, you are skipping some items using a for loop.
Try using a while loop, and manually increment the counter when you are not deleting an item.
For example, the following code is incorrect
List<int> nums = new List<int>{2, 4, 6, 7, 8, 10, 11};
for (int i = 0; i < nums.Count; i++)
{
if (nums[i] % 2 == 0)
nums.Remove(nums[i]);
}
If will return the list {4, 7, 10, 11} instead of just {7, 11}.
It will not remove the value of 4, because, when I remove the value of 2, (for i=0) the nums list goes from
//index 0 1 2 3 4 5 6
nums = {2, 4, 6, 7, 8, 10, 11}
to
//index 0 1 2 3 4 5
nums = {4, 6, 7, 8, 10, 11}
The loop finishes, the i is incremented to 1, and the next item referenced is nums[1], which is not 4 as one would intuitively expect, but 6. So in effect the value of 4 is skipped, and the check is not executed.
You should be very, very careful each time when you are modifying the collection you are iterating. For example, the foreach statement will throw an exception if you even try this. In this case you could use a while like
List<int> nums = new List<int>{2, 4, 6, 7, 8, 10, 11};
int i = 0;
while (i < nums.Count)
{
if (nums[i] % 2 == 0)
{
nums.Remove(nums[i])
}
else
{
i++; //only increment if you are not removing an item
//otherwise re-run the loop for the same value of i
}
}
of you could even fork the for, like
for (int i = 0; i < nums.Count; i++)
{
if (nums[i] % 2 == 0)
{
nums.Remove(nums[i]);
i--; //decrement the counter, so that it will stay in place
//when it is incremented at the end of the loop
}
}
Alternatively you could use linq, like this:
distinct.AddRange(groupA);
distinct.AddRange(groupB);
distinct = distinct.Distinct().ToList();
and
dups.AddRange(groupA);
dups.AddRange(groupB);
dups = dups.GroupBy(i => i)
.Where(g => g.Count() > 1)
.Select(g => g.Key)
.ToList();
Note that the LINQ code will not alter your existing groupA and groupB lists. If you just want to distinct them, you could just do
groupA = groupA.Distinct().ToList();
groupB = groupB.Distinct().ToList();
You can easily do it with Linq:
List<int> dups = groupA.Intersect(groupB).ToList();
List<int> distinct = groupA.Except(groupB).ToList();
(assuming I correctly understood what you were trying to do)
The title of the question is "Compare two List", some people who are interested in true/false only result will land to the question
use Enumerable.SequenceEqual Method
if (listA.SequenceEqual(listB))
{
// they are equal
}
You need to find the missing elements in both:
List<int> onlyInA = groupA.Except(groupB).ToList();
List<int> onlyInB = groupB.Except(groupA).ToList();
Or in a single linq:
List<int> missing = groupA.Except(groupB).Union(groupB.Except(groupA)).ToList()
Note - as with all linq, its worth pointing out that its not the most efficient way to do this. All list iterations have a cost. A longer-hand way of sorting both lists then iterating over them together would be quicker if the lists were REALLY large...

reorder List starting at given position

List:
List<int> list1 = new List<int>(){ 0, 1, 2, 3, 4, 5, 6 };
let's say we want to reorder it. The beginning should be at number "2"
// 2,3,4,5,6,0,1
or at number 5
// 5,6,0,1,2,3,4
how do you do it with C#?
the reason: Imagine that you have an index of a given number in the List (number 3, index 3). You want to get the second number from the right - it'll be 5.
Unfortunately, if the starting number is at the end of the List (numbers 5 and 6) - out of range exception will be thrown, because there's no 7 and 8!
The idea is to reorder the List!
We enter Nr. 5 - we get 0 (5,6,0).
We enter Nr. 6 - we get 1 (6,0,1), etc.
or maybe there is some other (read - better) way to solve this problem?
The better way to do it is to use the mod operator %. This gives you the remainder when you divide an int by another int. The way this works is something like this:
int nextIndex = (currentIndex + offset) % length;
So, if your current index is 5, your offset is 2 and your length is 6 then:
5 + 2 = 7
7 / 6 = 1 remainder 1 (or 7 mod 6 = 1)
therefore nextIndex = 1
A little Linq can do this pretty easily:
List<int> list1 = new List<int>(new[] { 0, 1, 2, 3, 4, 5, 6 });
var numToStart = 4;
//reorderedList will be {4,5,6,0,1,2,3}
var reorderedList = list1.Skip(numToStart).Concat(list1.Take(numToStart));
You don't need to reorder the list. You could get the number with the following function:
int GetNumber(List<int> list, int fromValue, int index)
{
return list[(list.IndexOf(fromValue) + index) % list.Count()];
}
You could call the function like this:
List<int> list1 = new List<int>(new[] { 0, 1, 2, 3, 4, 5, 6 });
int number = GetNumber(list1, 5, 2); // number = 0

Find Same patterns in lists

Assume we have below lists:
List<int> Journey1 = new List<int>() { 1, 2, 3, 4, 5 };
List<int> Journey2 = new List<int>() { 2, 3, 4, 6, 7, 3, 4 };
List<int> Journey3 = new List<int>() { 6, 7, 1 };
List<int> Journey4 = new List<int>() { 3, 1, 4 };
And the patterns are:
2, 3, 4 -> Journey1, Journey2;
6, 7 -> Journey2, Journey3;
1 -> Journey2, Journey3, Journey4;
5 -> Journey1;
3, 4 -> Journey2;
3 -> Journey4;
4 -> Journey4;
We have 5000 lists, and each has around 200 items, so the patterns can have between 1-200 items and can be seen in 1-5000 lists.
Therefore I need very fast way of pattern matching.
Without precomputation and with a naive on-the-fly search:
var matchedJourneys = journeys.Where(x => ContainsPattern(x, mypattern));
bool ContainsPattern(List<int> list, List<int> pattern)
{
for(int i = 0; i < list.Count - (pattern.Count - 1); i++)
{
var match = true;
for(int j = 0; j < pattern.Count; j++)
if(list[i + j] != pattern[j])
{
match = false;
break;
}
if(match) return true;
}
return false;
}
This will execute at max 200 million equals checks for your 'numbers'. But since checks are not expected to be executed for whole patterns, that could be (just a guess) ~5 million equals operations if checking all the lists. That's a few hundred milliseconds.
It all depends on what is 'very fast' for you. If that's too slow, you will need a much much more complicated approach ...
I am not sure what you want as output. I just made a Try.
I suggest that you make a list of lists, instead of declaring individual list variables.
List<List<int>> journeys = new List<List<int>>();
journeys.Add(new List<int>() { 1, 2, 3, 4, 5 });
journeys.Add(new List<int>() { 2, 3, 4, 6, 7, 3, 4 });
journeys.Add(new List<int>() { 6, 7, 1 });
journeys.Add(new List<int>() { 3, 1, 4 });
I assumed that the numbers range from 0 to 255. With this query
var result = Enumerable.Range(0, 256)
.Select(number => new
{
number,
listIndexes = journeys
.Select((list, index) => new { index, list })
.Where(a => a.list.Contains(number))
.Select(a => a.index)
.ToList()
})
.Where(b => b.listIndexes.Count > 0)
.ToList();
and this test loop
foreach (var item in result) {
Console.Write("Number {0} occurs in list # ", item.number);
foreach (var index in item.listIndexes) {
Console.Write("{0} ", index);
}
Console.WriteLine();
}
you will get this result
Number 1 occurs in list # 0 2 3
Number 2 occurs in list # 0 1
Number 3 occurs in list # 0 1 3
Number 4 occurs in list # 0 1 3
Number 5 occurs in list # 0
Number 6 occurs in list # 1 2
Number 7 occurs in list # 1 2
Where the lists are numbered starting at zero.
For brute force approach you can try to use polynomial hash-functions to speed up sub-section matches. Still insane number of comparisons required, but at least match could be almost constant irrespective of sub-sequence length.
In your case there are opportunities to benefit from pattern preprocessing as well as text preprocessing (http://en.wikipedia.org/wiki/String_searching_algorithm).
For instance, constructing a trie for all subsequences in a list will allow to query this list for a given pattern in time proportional to the pattern length.

Categories

Resources