Tasks fail to launch properly - c#

private static int[] Sort(int[] arr)
{
int temp;
int[] result = new int[arr.Length];
Array.Copy(arr, result, arr.Length);
for (int i = 0; i < result.Length - 1; i++)
{
for (int j = i + 1; j < result.Length; j++)
{
if (result[i] > result[j])
{
temp = result[i];
result[i] = result[j];
result[j] = temp;
}
}
}
return result;
}
public static List<List<T>> ChunkBy<T>(this List<T> source, int chunkSize)
{
return source
.Select((x, i) => new { Index = i, Value = x })
.GroupBy(x => x.Index / chunkSize)
.Select(x => x.Select(v => v.Value).ToList())
.ToList();
}
...
List<List<int>> unsortedLists = list.ChunkBy(chunkSize);
int count = unsortedLists.Count;
List<List<int>> lists = new List<List<int>>();
Task<List<int>>[] tasks = new Task<List<int>>[count];
//from 0 to 9
for (int i = 0; i < count; i++)
{
tasks[i] = new Task<List<int>>(() =>
{
lists[i] = Sort(unsortedLists[i].ToArray()).ToList(); //out of range exception i = 10
return lists[i];
});
}
for(int i = 0; i < count; i++)
{
tasks[i].Start();
}
for(int i = 0; i < count; i++)
{
lists[i] = await tasks[i]; //array out of range
}
I'm facing extremely weird error. I have slightly simplified my code and provided the important part.
I'm creating a bunch of tasks, starting them, and then it fails instantly at 3rd loop. For some reason it uses old iterator that ended on 9, and increases it to 10, creating exception. I have no idea what to do, at the moment it looks like error of the environment.

You should never use the task constructor. In this case, use Task.Run instead.
Your other problem is that the for loop in C# only defines a single loop variable, and lambdas close over variables, not values. This is why you're seeing an i that is 10.
The simplest solution is to do away with the for loops completely:
var tasks = Enumerable.Range(0, count)
.Select(i => Task.Run(() =>
{
lists[i] = Sort(unsortedLists[i].ToArray()).ToList();
return lists[i];
}))
.ToList();
var results = await Task.WhenAll(tasks);

Related

What is wrong with this approach in comparing two rows in mastermind?

Created a function with two ints and two lists.
This approach still double counts.
Example: row 1: 1,2,2,3
row 2: 1,4,2,5
It shows wrongPosition: 1 and samePlace: 2 instead of samePlace: 2 and wrongPosition:0.
Also, do you have any suggestion as to how to optimise this code?
private void CompareRows()
{
if (_resaultPawns.Length != _actualRowPawns.Length)
{
Debug.LogError("Arrays have different length");
return;
}
int swrongPosition = 0;
int samePlace = 0;
List<int> alreadyCounted = new List<int>();
List<int> alreadyCountedColors = new List<int>();
// Check for same position with same color
for (int i = 0; i < _resaultPawns.Length; i++)
{
if (_resaultPawns[i].GetComponent<MeshRenderer>().material.color == _actualRowPawns[i].GetComponent<MeshRenderer>().material.color)
{
if (!alreadyCounted.Contains(i))
{
samePlace++;
alreadyCounted.Add(i);
}
}
}
// Check for wrong position with same color
for (int i = 0; i < _resaultPawns.Length; i++)
{
for (int j = 0; j < _actualRowPawns.Length; j++)
{
if (_resaultPawns[i].GetComponent<MeshRenderer>().material.color == _actualRowPawns[j].GetComponent<MeshRenderer>().material.color && !alreadyCounted.Contains(i) && !alreadyCountedColors.Contains(j))
{
if (!alreadyCounted.Contains(i) && !alreadyCountedColors.Contains(j))
{
swrongPosition++;
alreadyCountedColors.Add(j);
alreadyCounted.Add(i);
}
}
}
}
}
See https://dotnetfiddle.net/91GVVe
I looked at this from optimizations/maintainability first and refactoring the code a bit seems to already have solved the issue as well ^^
var samePlace = new List<int>(4);
var wrongPosition = new List<int>(4);
for (var i = 0; i < _resaultPawns.Length; i++)
{
if (_resaultPawns[i].GetComponent<MeshRenderer>().material.color == _actualRowPawns[i].GetComponent<MeshRenderer>().material.color)
{
// Check for 'alreadyCounted.Contains' is redundan at this stage
// No additional counter needed - simply add items and use the list Count later on
samePlace.Add(i);
}
}
for (var i = 0; i < _resaultPawns.Length; i++)
{
// Can directly skip as already counted
if (samePlace.Contains(i)) continue;
for (var j = 0; j < _actualRowPawns.Length; j++)
{
// can directly skip as would have been upper loop match
// slightly chaper though than using the 'samePlace.Contains(j)' below
if (i == j) continue;
// skip as well to not re-use already exact matches
if (samePlace.Contains(j)) continue;
// skip to not use twice
if (wrongPosition.Contains(j)) continue;
if (_resaultPawns[i].GetComponent<MeshRenderer>().material.color == _actualRowPawns[j].GetComponent<MeshRenderer>().material.color)
{
wrongPosition.Add(j);
break;
}
}
}
var wrongPositionCount = wrongPosition.Count;
var samePlaceCount = samePlace.Count;
I guess the main bit I added was
if (samePlace.Contains(j)) continue;
=> you also want to ignore if a j index was already seen before by the first loop as it is already an exact match.
In your example
1,2,2,3
1,4,2,5
even though the index 3 (=value 2) was already counted as an exact match it was used again as an alternative position for index 1, a case you didn't check for as it is neither in your alreadyCounted nor in alreadyCountedColour
It seems pretty simple to just do this:
var hidden = new[] { 1, 2, 2, 3 };
var first = new[] { 1, 4, 2, 5 };
int sp = 0;
int wp = 0;
for (int i = 0; i < 4; i++)
{
if (first[i] == hidden[i])
sp++;
else
for (int j = 0; j < 4; j++)
if (first[i] == hidden[j] && first[j] != hidden[j])
{
wp++;
break;
}
}
Console.WriteLine($"Same Place: {sp}, Wrong Place: {wp}");
That outputs Same Place: 2, Wrong Place: 0.
It looks like a more complicated approach is needed to cover all cases:
var hidden = new[] { 1, 2, 2, 3 };
var first = new[] { 1, 4, 2, 5 };
var paired =
first
.Zip(hidden, (f, h) => (first: f, hidden: h))
.ToLookup(x => x.first == x.hidden);
int sp = paired[true].Count();
var unmatched_hidden = paired[false].ToLookup(x => x.first);
var unmatched_first = paired[false].ToLookup(x => x.hidden);
IEnumerable<int> query =
from uf in unmatched_first
from x in unmatched_first[uf.Key].Zip(unmatched_hidden[uf.Key])
select uf.Key;
int wp = query.Count();
Console.WriteLine($"Same Place: {sp}, Wrong Place: {wp}");
That seems to work now.
I really like InBetween's answer. I did a bit of refactoring to make it use standard operators.
private static (int SamePlace, int WrongPlace) CompareRows<T>
(IEnumerable<T> guess, IEnumerable<T> master) where T : IEquatable<T>
{
var guessWithIndex = guess.Select((g, i) => (value: g, i)).ToArray();
var masterWithIndex = master.Select((m, i) => (value: m, i)).ToArray();
var samePlaces = masterWithIndex.Intersect(guessWithIndex);
var guessNotSamePlaces = guessWithIndex.Except(samePlaces).Select(x => x.value);
var masterNotSamePlaces = masterWithIndex.Except(samePlaces).Select(x => x.value);
var wrongPlaces = guessNotSamePlaces.Intersect(masterNotSamePlaces);
return (samePlaces.Count(), wrongPlaces.Count());
}
Linq solution just for the fun of it... instructive (allthough overkill) as it shows use of generics, tuples and how you can tweak built-in linq operations with custom comparers.
private static (int Guessed, int Misplaced) CompareRows<T>
(IEnumerable<T> guesses, IEnumerable<T> master)
where T: IEquatable<T>
{
var guessesWithIndex = guesses.Select((g, i) => (g, i));
var masterWithIndex = master.Select((m, i) => (m, i));
var guessed = masterWithIndex.Intersect(guessesWithIndex);
var potentiallyMisplacedGuesses = guessesWithIndex.Except(guessed);
var masterNotGuessed = masterWithIndex.Except(guessed);
var misplaced = potentiallyMisplacedGuesses.Intersect(
masterNotGuessed, new OnlyFirstValueComparer<T, int>());
return (guessed.Count(), misplaced.Count());
}
class OnlyFirstValueComparer<T, K> : IEqualityComparer<(T, K)> where T: IEquatable<T>
{
public bool Equals((T, K) x, (T, K) y)
{
return x.Item1.Equals(y.Item1);
}
public int GetHashCode((T, K) obj)
{
return obj.Item1.GetHashCode();
}
}

Remove Nulls from string[,]

I have a string array defined in c# as
string[,] options = new string[100,3];
Throughout the code it gets populated with data but not always filled.
So if I have 80 parts of it filled and 20 parts of it not filled. The 20 parts have nulls in them or 60 nulls at the end. Is there an easy way to resize the array so that after filling it the array is the same as
String[,] options = new string[80,3];
It would have to be resized based on the position of the first set of 3 nulls it found.
If this was a jagged array I would have done
options = options.Where(x => x != null).ToArray();
The method is quite long, because it has to check every row twice...
public static string[,] RemoveEmptyRows(string[,] strs)
{
int length1 = strs.GetLength(0);
int length2 = strs.GetLength(1);
// First we count the non-emtpy rows
int nonEmpty = 0;
for (int i = 0; i < length1; i++)
{
for (int j = 0; j < length2; j++)
{
if (strs[i, j] != null)
{
nonEmpty++;
break;
}
}
}
// Then we create an array of the right size
string[,] strs2 = new string[nonEmpty, length2];
for (int i1 = 0, i2 = 0; i2 < nonEmpty; i1++)
{
for (int j = 0; j < length2; j++)
{
if (strs[i1, j] != null)
{
// If the i1 row is not empty, we copy it
for (int k = 0; k < length2; k++)
{
strs2[i2, k] = strs[i1, k];
}
i2++;
break;
}
}
}
return strs2;
}
Use it like:
string[,] options = new string[100, 3];
options[1, 0] = "Foo";
options[3, 1] = "Bar";
options[90, 2] = "fiz";
options = RemoveEmptyRows(options);
As suggested by Alexei, there is another way of doing this:
public static string[,] RemoveEmptyRows2(string[,] strs)
{
int length1 = strs.GetLength(0);
int length2 = strs.GetLength(1);
// First we put somewhere a list of the indexes of the non-emtpy rows
var nonEmpty = new List<int>();
for (int i = 0; i < length1; i++)
{
for (int j = 0; j < length2; j++)
{
if (strs[i, j] != null)
{
nonEmpty.Add(i);
break;
}
}
}
// Then we create an array of the right size
string[,] strs2 = new string[nonEmpty.Count, length2];
// And we copy the rows from strs to strs2, using the nonEmpty
// list of indexes
for (int i1 = 0; i1 < nonEmpty.Count; i1++)
{
int i2 = nonEmpty[i1];
for (int j = 0; j < length2; j++)
{
strs2[i1, j] = strs[i2, j];
}
}
return strs2;
}
This one, in the tradeoff memory vs time, chooses time. It is probably faster, because it doesn't have to check every row twice, but it uses more memory, because it puts somewhere a list of the non-empty indexes.
I went for all rows until you find an row with all null values:
Needs some clean up and will obviously remove non-null rows that occur after the first all null row. The requirement wasn't too clear here
EDIT: Just seen the comment clarifying requirement to remove all null rows - I've tweaked the below to avoid downvotes but a more comprehensive answer is already accepted (and is more efficient) :)
void Main()
{
string[,] options = new string[100,3];
options[0,0] = "bleb";
options[1,1] = "bleb";
options[2,0] = "bleb";
options[2,1] = "bleb";
options[3,2] = "bleb";
options[4,1] = "bleb";
string[,] trimmed = TrimNullRows(options);
Console.WriteLine(trimmed);
}
public string[,] TrimNullRows(string[,] options)
{
IList<string[]> nonNullRows = new List<string[]>();
for (int x = 0; x < options.GetLength(0); x++)
{
bool allNull = true;
var row = new string[options.GetLength(1)];
for (int y = 0; y < options.GetLength(1); y++)
{
row[y] = options[x,y];
allNull &= options[x,y] == null;
}
if (!allNull)
{
nonNullRows.Add(row);
}
}
var optionsTrimmed = new string[nonNullRows.Count, options.GetLength(1)];
for (int i=0;i<nonNullRows.Count;i++)
{
for (int j=0;j<options.GetLength(1);j++)
{
optionsTrimmed[i, j] = nonNullRows[i][j];
}
}
return optionsTrimmed;
}
You can also get yourself some helpers to convert between jagged and multi-dimensional representations. This is pretty silly, of course, but for arrays as small as the ones you're showing (and also, very sparse arrays), it'll be fine.
void Main()
{
string[,] options = new string[100,3];
options[3, 1] = "Hi";
options[5, 0] = "Dan";
var results =
options
.JagIt()
.Where(i => i.Any(j => j != null))
.UnjagIt();
results.Dump();
}
static class Extensions
{
public static IEnumerable<IEnumerable<T>> JagIt<T>(this T[,] array)
{
for (var i = 0; i < array.GetLength(0); i++)
yield return GetRow(array, i);
}
public static IEnumerable<T> GetRow<T>(this T[,] array, int rowIndex)
{
for (var j = 0; j < array.GetLength(1); j++)
yield return array[rowIndex, j];
}
public static T[,] UnjagIt<T>(this IEnumerable<IEnumerable<T>> jagged)
{
var rows = jagged.Count();
if (rows == 0) return new T[0, 0];
var columns = jagged.Max(i => i.Count());
var array = new T[rows, columns];
var row = 0;
var column = 0;
foreach (var r in jagged)
{
column = 0;
foreach (var c in r)
{
array[row, column++] = c;
}
row++;
}
return array;
}
}
The JagIt method is pretty simple of course - we'll just iterate over the rows, and yield the individual items. This gives us an enumerable of enumerables, which we can use in LINQ quite easily. If desired, you could transform those into arrays, of course (say, Select(i => i.ToArray()).ToArray()).
The UnjagIt method is a bit more talkative, because we need to create the target array with the correct dimensions first. And there's no unyield instruction to simplify that :D
This is pretty inefficient, of course, but that isn't necessarily a problem. You could save yourself some of the iterations by keeping the inner enumerable an array, for example - that will save us having to iterate over all the inner items.
I'm mostly keeping this as the memory-cheap, CPU-intensive alternative to #xanatos' memory-intensive, CPU-cheap (relatively).
Of course, the main bonus is that it can be used to treat any multi-dimensional arrays as jagged arrays, and convert them back again. General solutions usually aren't the most efficient :D
Yet another variant with linq
static string[,] RemoveNotNullRow(string[,] o)
{
var rowLen = o.GetLength(1);
var notNullRowIndex = (from oo in o.Cast<string>().Select((x, idx) => new { idx, x })
group oo.x by oo.idx / rowLen into g
where g.Any(f => f != null)
select g.Key).ToArray();
var res = new string[notNullRowIndex.Length, rowLen];
for (int i = 0; i < notNullRowIndex.Length; i++)
{
Array.Copy(o, notNullRowIndex[i] * rowLen, res, i * rowLen, rowLen);
}
return res;
}

Editting a 2D array with Linq and ForEach

I'm trying to solve this question in a Linqified way
"Finding diagonal in 2D array and replacing with 0 above it"
This is my Linqfied solution:
Random r = new Random();
//int[,] mas = new int[4, 5];
int[][] mas = new int[4][];
for (int i = 0; i < mas.Length; i++)
{
mas[i] = new int[5];
for (int j = 0; j < mas[i].Length; j++)
{
mas[i][j] = r.Next(1, 10);
Console.Write("{0}\t", mas[i][j]);
}
Console.WriteLine();
}
// my code starts here
for (int i = 0; i < mas.Length; i++)
{
mas[i] = mas[i].Select((c, ind) =>
{
if (ind > i)
c = 0;
return c;
}).ToArray();
}
It works, but I want to get rid of the for-loop, then I did:
mas.ToList().ForEach(m0 =>
{
m0 = m0.Select((c, ind) =>
{
if (ind > k)
c = 0;
return c;
}).ToArray();
k++;
});
Despite the inner elements m0 get the desired values changed to zero in debug time, the mas 2D array don't get its values changed.
You can use linq like so:
mas = mas
.Select((a, i) => a.Select((c, j) => j > i ? 0 : c).ToArray())
.ToArray();
You're changing the parameter to the method, which is a copy of the value in your arrays. Mutating that copy does not mutate the original array.
A proper LINQ-based solution to this problem would be to use LINQ operations such that the results of executing the query is your data set, rather than having the execution of the query mutate some other data set:
var random = new Random();
var query = Enumerable.Range(0, 4)
.Select(i => Enumerable.Range(0, 5)
.Select(j => j > i ? 0 : random.Next(1, 10))
.ToArray())
.ToArray();

Order an array in a specific order

I have this array of integers:-
int[] numbers = new int[] { 10, 20, 30, 40 };
I am trying to create an array which will have first element, last element, second element, second-last element and so on..
So, my resulting output will be:-
int[] result = {10,40,20,30};
This was my approach, in one loop start from first and go till the middle & in second loop start from last and get to the middle and select items accordingly, but I totally messed it up. Here is my attempted code:-
private static IEnumerable<int> OrderedArray(int[] numbers)
{
bool takeFirst = true;
if (takeFirst)
{
takeFirst = false;
for (int i = 0; i < numbers.Length / 2; i++)
{
yield return numbers[i];
}
}
else
{
takeFirst = true;
for (int j = numbers.Length; j < numbers.Length / 2; j--)
{
yield return numbers[j];
}
}
}
Need Help.
You might try this:
int[] result = numbers.Zip(numbers.Reverse(), (n1,n2) => new[] {n1, n2})
.SelectMany(x =>x)
.Take(numbers.Length)
.ToArray();
Explanation: This approach basically pairs up the elements of the original collection with the elements of its reverse ordered collection (using Zip). So you get a collection of pairs like [first, last], [second, second from last], etc.
It then flattens those collection of pairs into a single collection (using SelectMany). So the collection becomes [first, last, second, second from last,...].
Finally, we limit the number of elements to the length of the original array (n). Since we are iterating through twice as many elements (normal and reverse), it works out that iterating through n elements allow us to stop in the middle of the collection.
As a different approach, this is a modification on your existing method:
private static IEnumerable<int> OrderedArray(int[] numbers)
{
var count = (numbers.Length + 1) / 2;
for (int i = 0; i < count; i++)
{
yield return numbers[i];
int reverseIdx = numbers.Length - 1 - i;
if(i != reverseIdx)
yield return numbers[reverseIdx];
}
}
ok,
public static class Extensions
{
public static IEnumerable<T> EndToEnd<T>(this IReadOnlyList<T> source)
{
var length = source.Count;
var limit = length / 2;
for (var i = 0; i < limit; i++)
{
yield return source[i];
yield return source[length - i - 1];
}
if (length % 2 > 0)
{
yield return source[limit];
}
}
}
Which you could use like this,
var result = numbers.EndToEnd().ToArray();
more optimally,
public static class Extensions
{
public static IEnumerable<T> EndToEnd<T>(this IReadOnlyList<T> source)
{
var c = source.Count;
for (int i = 0, f = 0, l = c - 1; i < c; i++, f++, l--)
{
yield return source[f];
if (++i == c)
{
break;
}
yield return source[l];
}
}
}
no divide or modulus required.
With a simple for;
int len = numbers.Length;
int[] result = new int[len];
for (int i = 0, f = 0, l = len - 1; i < len; f++, l--)
{
result[i++] = numbers[f];
if (f != l)
result[i++] = numbers[l];
}
Based on Selman22's now deleted answer:
int[] numbers = new int[] { 10, 20, 30, 40 };
int[] result = numbers
.Select((x,idx) => idx % 2 == 0
? numbers[idx/2]
: numbers[numbers.Length - 1 -idx/2])
.ToArray();
result.Dump();
(The last line is LinqPad's way of outputting the results)
Or in less LINQy form as suggested by Jeppe Stig Nielsen
var result = new int[numbers.Length];
for (var idx = 0; idx < result.Length; idx++) {
result[idx] = idx % 2 == 0 ? numbers[idx/2] : numbers[numbers.Length - 1 -idx/2];
}
The principle is that you have two sequences, one for even elements (in the result) and one for odd. The even numbers count the first half of the array and the odds count the second half from the back.
The only modification to Selman's code is adding the /2 to the indexes to keep it counting one by one in the right half while the output index (which is what idx basically is in this case) counts on.
Came up with this
static void Main(string[] args)
{
List<int> numbers = new List<int>() { 10, 20, 30, 40, 50, 60, 70};
List<int> numbers2 = new List<int>();
int counter1 = 0;
int counter2 = numbers.Count - 1;
int remainder = numbers.Count % 2 == 0 ? 1: 0;
while (counter1-1 < counter2)
{
if (counter1 + counter2 % 2 == remainder)
{
numbers2.Add(numbers[counter1]);
counter1++;
}
else
{
numbers2.Add(numbers[counter2]);
counter2--;
}
}
string s = "";
for(int a = 0; a< numbers2.Count;a++)
s+=numbers2[a] + " ";
Console.Write(s);
Console.ReadLine();
}
This late answer steals a lot from the existing answers!
The idea is to allocate the entire result array at once (since its length is known). Then fill out all even-indexed members first, from one end of source. And finally fill out odd-numbered entries from the back end of source.
public static TElement[] EndToEnd<TElement>(this IReadOnlyList<TElement> source)
{
var count = source.Count;
var result = new TElement[count];
for (var i = 0; i < (count + 1) / 2; i++)
result[2 * i] = source[i];
for (var i = 1; i <= count / 2; i++)
result[2 * i - 1] = source[count - i];
return result;
}
Came up with this
public int[] OrderedArray(int[] numbers)
{
int[] final = new int[numbers.Length];
var limit=numbers.Length;
int last = numbers.Length - 1;
var finalCounter = 0;
for (int i = 0; finalCounter < numbers.Length; i++)
{
final[finalCounter] = numbers[i];
final[((finalCounter + 1) >= limit ? limit - 1 : (finalCounter + 1))] = numbers[last];
finalCounter += 2;
last--;
}
return final;
}

c# permutation without repetition when order is important [duplicate]

I have a list of Offers, from which I want to create "chains" (e.g. permutations) with limited chain lengths.
I've gotten as far as creating the permutations using the Kw.Combinatorics project.
However, the default behavior creates permutations in the length of the list count. I'm not sure how to limit the chain lengths to 'n'.
Here's my current code:
private static List<List<Offers>> GetPerms(List<Offers> list, int chainLength)
{
List<List<Offers>> response = new List<List<Offers>>();
foreach (var row in new Permutation(list.Count).GetRows())
{
List<Offers> innerList = new List<Offers>();
foreach (var mix in Permutation.Permute(row, list))
{
innerList.Add(mix);
}
response.Add(innerList);
innerList = new List<Offers>();
}
return response;
}
Implemented by:
List<List<AdServer.Offers>> lst = GetPerms(offers, 2);
I'm not locked in KWCombinatorics if someone has a better solution to offer.
Here's another implementation which I think should be faster than the accepted answer (and it's definitely less code).
public static IEnumerable<IEnumerable<T>> GetVariationsWithoutDuplicates<T>(IList<T> items, int length)
{
if (length == 0 || !items.Any()) return new List<List<T>> { new List<T>() };
return from item in items.Distinct()
from permutation in GetVariationsWithoutDuplicates(items.Where(i => !EqualityComparer<T>.Default.Equals(i, item)).ToList(), length - 1)
select Prepend(item, permutation);
}
public static IEnumerable<IEnumerable<T>> GetVariations<T>(IList<T> items, int length)
{
if (length == 0 || !items.Any()) return new List<List<T>> { new List<T>() };
return from item in items
from permutation in GetVariations(Remove(item, items).ToList(), length - 1)
select Prepend(item, permutation);
}
public static IEnumerable<T> Prepend<T>(T first, IEnumerable<T> rest)
{
yield return first;
foreach (var item in rest) yield return item;
}
public static IEnumerable<T> Remove<T>(T item, IEnumerable<T> from)
{
var isRemoved = false;
foreach (var i in from)
{
if (!EqualityComparer<T>.Default.Equals(item, i) || isRemoved) yield return i;
else isRemoved = true;
}
}
On my 3.1 GHz Core 2 Duo, I tested with this:
public static void Test(Func<IList<int>, int, IEnumerable<IEnumerable<int>>> getVariations)
{
var max = 11;
var timer = System.Diagnostics.Stopwatch.StartNew();
for (int i = 1; i < max; ++i)
for (int j = 1; j < i; ++j)
getVariations(MakeList(i), j).Count();
timer.Stop();
Console.WriteLine("{0,40}{1} ms", getVariations.Method.Name, timer.ElapsedMilliseconds);
}
// Make a list that repeats to guarantee we have duplicates
public static IList<int> MakeList(int size)
{
return Enumerable.Range(0, size/2).Concat(Enumerable.Range(0, size - size/2)).ToList();
}
Unoptimized
GetVariations 11894 ms
GetVariationsWithoutDuplicates 9 ms
OtherAnswerGetVariations 22485 ms
OtherAnswerGetVariationsWithDuplicates 243415 ms
With compiler optimizations
GetVariations 9667 ms
GetVariationsWithoutDuplicates 8 ms
OtherAnswerGetVariations 19739 ms
OtherAnswerGetVariationsWithDuplicates 228802 ms
You're not looking for a permutation, but for a variation. Here is a possible algorithm. I prefer iterator methods for functions that can potentially return very many elements. This way, the caller can decide if he really needs all elements:
IEnumerable<IList<T>> GetVariations<T>(IList<T> offers, int length)
{
var startIndices = new int[length];
var variationElements = new HashSet<T>(); //for duplicate detection
while (startIndices[0] < offers.Count)
{
var variation = new List<T>(length);
var valid = true;
for (int i = 0; i < length; ++i)
{
var element = offers[startIndices[i]];
if (variationElements.Contains(element))
{
valid = false;
break;
}
variation.Add(element);
variationElements.Add(element);
}
if (valid)
yield return variation;
//Count up the indices
startIndices[length - 1]++;
for (int i = length - 1; i > 0; --i)
{
if (startIndices[i] >= offers.Count)
{
startIndices[i] = 0;
startIndices[i - 1]++;
}
else
break;
}
variationElements.Clear();
}
}
The idea for this algorithm is to use a number in offers.Count base. For three offers, all digits are in the range 0-2. We then basically increment this number step by step and return the offers that reside at the specified indices. If you want to allow duplicates, you can remove the check and the HashSet<T>.
Update
Here is an optimized variant that does the duplicate check on the index level. In my tests it is a lot faster than the previous variant:
IEnumerable<IList<T>> GetVariations<T>(IList<T> offers, int length)
{
var startIndices = new int[length];
for (int i = 0; i < length; ++i)
startIndices[i] = i;
var indices = new HashSet<int>(); // for duplicate check
while (startIndices[0] < offers.Count)
{
var variation = new List<T>(length);
for (int i = 0; i < length; ++i)
{
variation.Add(offers[startIndices[i]]);
}
yield return variation;
//Count up the indices
AddOne(startIndices, length - 1, offers.Count - 1);
//duplicate check
var check = true;
while (check)
{
indices.Clear();
for (int i = 0; i <= length; ++i)
{
if (i == length)
{
check = false;
break;
}
if (indices.Contains(startIndices[i]))
{
var unchangedUpTo = AddOne(startIndices, i, offers.Count - 1);
indices.Clear();
for (int j = 0; j <= unchangedUpTo; ++j )
{
indices.Add(startIndices[j]);
}
int nextIndex = 0;
for(int j = unchangedUpTo + 1; j < length; ++j)
{
while (indices.Contains(nextIndex))
nextIndex++;
startIndices[j] = nextIndex++;
}
break;
}
indices.Add(startIndices[i]);
}
}
}
}
int AddOne(int[] indices, int position, int maxElement)
{
//returns the index of the last element that has not been changed
indices[position]++;
for (int i = position; i > 0; --i)
{
if (indices[i] > maxElement)
{
indices[i] = 0;
indices[i - 1]++;
}
else
return i;
}
return 0;
}
If I got you correct here is what you need
this will create permutations based on the specified chain limit
public static List<List<T>> GetPerms<T>(List<T> list, int chainLimit)
{
if (list.Count() == 1)
return new List<List<T>> { list };
return list
.Select((outer, outerIndex) =>
GetPerms(list.Where((inner, innerIndex) => innerIndex != outerIndex).ToList(), chainLimit)
.Select(perms => (new List<T> { outer }).Union(perms).Take(chainLimit)))
.SelectMany<IEnumerable<IEnumerable<T>>, List<T>>(sub => sub.Select<IEnumerable<T>, List<T>>(s => s.ToList()))
.Distinct(new PermComparer<T>()).ToList();
}
class PermComparer<T> : IEqualityComparer<List<T>>
{
public bool Equals(List<T> x, List<T> y)
{
return x.SequenceEqual(y);
}
public int GetHashCode(List<T> obj)
{
return (int)obj.Average(o => o.GetHashCode());
}
}
and you'll call it like this
List<List<AdServer.Offers>> lst = GetPerms<AdServer.Offers>(offers, 2);
I made this function is pretty generic so you may use it for other purpose too
eg
List<string> list = new List<string>(new[] { "apple", "banana", "orange", "cherry" });
List<List<string>> perms = GetPerms<string>(list, 2);
result

Categories

Resources