Linq on 2 arrays in 1 loop? - c#

Is it possible to improve the efficiency of those linq requests? I use two different loops...
Can you help me to optimize this code?
double[] x = { 2, 3, 1, 5, 7, 2, 3 };
double[] y = { 1, 2, 3, 4, 5, 6, 7 };
IEnumerable<int> range = Enumerable.Range(0, x.Length);
double[] y_sorted = (from n in range orderby x[n] select y[n]).ToArray();
double[] x_sorted = (from n in range orderby x[n] select x[n]).ToArray();
This code in python is like that if you prefer:
x_index = argsort(x)
x_sorted = [x[i] for i in x_index]
y_sorted = [y[i] for i in x_index]
you will notice that, in this python code, i use only one sort. that's not the case of this c# code.
we should get at the end:
x_sorted = { 1, 2, 2, 3, 3, 5, 7 }
y_sorted = { 3, 1, 6, 2, 7, 4, 5 }
Fred
Edit:
I use the program of Diadistis (after a small correction)
So here we go:
Array.Sort(x, y) (0.05) is the fastest way following (0.18) by
int[] x_index = Enumerable.Range(0, x.Length).OrderBy(i => x[i]).ToArray();
double[] x_sorted = x_index.Select(i => x[i]).ToArray();
double[] y_sorted = x_index.Select(i => y[i]).ToArray();
The other solutions are quite equivalent (~0.35) in time consumption on my pc.
If someone have an interesting idea, I will profile it and update this post.

It's ok but I'd prefer a simpler syntax :
double[] x = { 2, 3, 1, 5, 7, 2, 3 };
double[] y = { 2, 3, 1, 5, 7, 2, 3 };
double[] x_sorted = x.OrderBy(d => d).ToArray();
double[] y_sorted = y.OrderBy(d => d).ToArray();
Edit:
Argh... I failed to spot that this was an associative array sort.
double[] x = { 2, 3, 1, 5, 7, 2, 3 };
double[] y = { 1, 2, 3, 4, 5, 6, 7 };
double[] y_sorted = y.Clone() as double[];
double[] x_sorted = x.Clone() as double[];
Array.Sort(x_sorted, y_sorted);
Edit 2 1/2
And some performance tests :
public class Program
{
delegate void SortMethod(double[] x, double[] y);
private const int ARRAY_SIZE = 3000000;
private static Random RandomNumberGenerator = new Random();
private static double[] x = GenerateTestData(ARRAY_SIZE);
private static double[] y = GenerateTestData(ARRAY_SIZE);
private static double[] GenerateTestData(int count)
{
var data = new double[count];
for (var i = 0; i < count; i++)
{
data[i] = RandomNumberGenerator.NextDouble();
}
return data;
}
private static void SortMethod1(double[] x, double[] y)
{
Array.Sort(x, y);
}
private static void SortMethod2(double[] x, double[] y)
{
IEnumerable<int> range = Enumerable.Range(0, x.Length);
x = (from n in range orderby x[n] select y[n]).ToArray();
y = (from n in range orderby x[n] select x[n]).ToArray();
}
private static void SortMethod3(double[] x, double[] y)
{
int[] x_index =
Enumerable.Range(0, x.Length).OrderBy(i => x[i]).ToArray();
x = x_index.Select(i => x[i]).ToArray();
y = x_index.Select(i => y[i]).ToArray();
}
private static void SortMethod4(double[] x, double[] y)
{
int[] range =
Enumerable.Range(0, x.Length).OrderBy(i => x[i]).ToArray();
var q = (
from n in range
orderby x[n]
select new { First = x[n], Second = y[n] }).ToArray();
x = q.Select(t => t.First).ToArray();
y = q.Select(t => t.Second).ToArray();
}
private static void SortMethodPerformanceTest(SortMethod sortMethod)
{
double[] y_sorted = y.Clone() as double[];
double[] x_sorted = x.Clone() as double[];
var sw = new Stopwatch();
sw.Start();
sortMethod.Invoke(x_sorted, y_sorted);
sw.Stop();
Console.WriteLine(
string.Format(
"{0} : {1}",
sortMethod.Method.Name,
sw.Elapsed));
}
static void Main(string[] args)
{
Console.WriteLine("For array length : " + ARRAY_SIZE);
Console.WriteLine("------------------------------");
SortMethodPerformanceTest(SortMethod1);
SortMethodPerformanceTest(SortMethod2);
SortMethodPerformanceTest(SortMethod3);
SortMethodPerformanceTest(SortMethod4);
Console.WriteLine("Press any key to continue...");
Console.ReadKey();
}
}
And the results :
For array length : 3000000
------------------------------
SortMethod1 : 00:00:00.6088503 // Array.Sort(Array, Array)
SortMethod2 : 00:00:07.9583779 // Original
SortMethod3 : 00:00:04.5023336 // dtb's Linq Alternative
SortMethod4 : 00:00:06.6115911 // Christian's Linq Alternative

If I read your code correctly, you're trying to sort two arrays by the elements of the first array.
The literal translation of your Python code to C# would be something like this:
int[] x_index = Enumerable.Range(0, x.Length).OrderBy(i => x[i]).ToArray();
double[] x_sorted = x_index.Select(i => x[i]).ToArray();
double[] y_sorted = x_index.Select(i => y[i]).ToArray();
Alternatively, you could zip the two arrays to an enumerable of tuples and then sort this by the first item:
var sorted = Enumerable.Zip(x, y, Tuple.Create<double, double>)
.OrderBy(t => t.Item1)
.ToArray();

This could be faster as you only sort once:
var q =
(from n in range
orderby x[n]
select new { First = x[n], Second = y[n] }).ToArray();
double[] x_sorted = q.Select(t => t.First).ToArray();
double[] y_sorted = q.Select(t => t.Second).ToArray();

You can also do following. Keeping in view that if you meant y[n] as in leppie comment
double[] x = { 2, 3, 1, 5, 7, 2, 3 };       
double[] y = { 2, 3, 1, 5, 7, 2, 3 };
Array.Sort<double>(x);
Array.Sort<double>(y);
Update
Should be as following to get correct result.
double[] x = { 2, 3, 1, 5, 7, 2, 3 };
double[] y = { 1, 2, 3, 4, 5, 6, 7 };
Array.Sort<double, double>(x, y);

Related

C# parallelize loop

I have the following c# code:
int[] ns = new int[4] { 100, 500, 1000, 5000 };
int[] ks = new int[5] { 5, 10, 15, 80, 160 };
int[] rs = new int[10] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
for(int n = 0; n<ns.Length; n++)
{
for(int k = 0; k < ks.Length; k++)
{
for (int r = 0; r < rs.Length; r++)
{
RunProg(ns[n], ks[k], rs[r]);
}
}
}
where RunProg takes a relatively substantial amount of time. I would like to parallelize this code. What is the most straight forward way to parallelize this in C#?
I have not tried any method yet. I was trying to decide between the different functionalities available in C#
A simple way to parallelize this is to produce a sequence representing all combinations of inputs, then transforming this sequence to a sequence of thread pool tasks representing the result. This final sequence can simply be passed to Task.WhenAll to await the completion of all tasks.
class Program
{
static void Main(string[] args)
{
int[] ns = new int[4] { 100, 500, 1000, 5000 };
int[] ks = new int[5] { 5, 10, 15, 80, 160 };
int[] rs = new int[10] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
var inputs = ns
.SelectMany(n => ks.Select(k => new { n, k }))
.SelectMany(x => rs.Select(r => new { x.n, x.k, r }));
var tasks = inputs.Select(x => Task.Run(() => RunProg(x.n, x.k, x.r)));
var results = Task.WhenAll(tasks).Result;
}
static int RunProg(int n, int k, int r)
{
Thread.Sleep(1000);
return n + k + r;
}
}
You can also Parallel.ForEach over the inputs collection, as mentioned in the other answers.
Parallel.ForEach(inputs, x => RunProg(x.n, x.k, x.r));
There is a Parallel.For method:
int[] ns = new int[4] { 100, 500, 1000, 5000 };
int[] ks = new int[5] { 5, 10, 15, 80, 160 };
int[] rs = new int[10] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
Parallel.For(0, ns.Length, n =>
{
Parallel.For(0, ks.Length, k =>
{
Parallel.For(0, rs.Length, r =>
{
RunProg(ns[n], ks[k], rs[r]);
});
});
});
This should work provided that the order in which the RunProg method is called is not important, and that the method itself is thread-safe.
Use Parallel.For() for the loops: https://msdn.microsoft.com/de-de/library/dd460713(v=vs.110).aspx
This works if the iterations are not dependent on each other.

LINQ: how to zip 2 lists and select elements where first list is distinct?

I have 2 lists/arrays of values, x and y. There are duplicates in the x list. I need to select the elements of x and y that have unique x values. Using LINQ, how would I write the query to get the elements of x and y where x is unique? (I'd like the first y for every distinct x).
Example:
x = {1, 1, 2, 3, 4, 4, 5, 6}
y = {3, 4, 5, 6, 7, 8, 9, 10}
The result I want is:
newX = {1, 2, 3, 4, 5, 6}
newY = {3, 5, 6, 7, 9, 10}
You can get the first x with index, then look up for the index for y.
var xWithIndex = x.Select((value, index) => new { Value = value, Index = index })
.GroupBy(item => item.Value)
.Select(group => group.First())
var newX = xWithIndex.Select(item => item.Value).ToList();
var newY = xWithIndex.Select(item => y[item.Index]).ToList();
Zip two lists, group by x, and select the initial item from each group, like this:
var pairs = x.Zip(y, (first, second) => new {X = first, Y = second})
.GroupBy(i => i.X)
.Select(g => g.First());
var newX = pairs.Select(p => p.X).ToList();
var newY = pairs.Select(p => p.Y).ToList();
If you are willing to add MoreLINQ this can be achieved using DistinctBy:
int[] x = { 1, 1, 2, 3, 4, 4, 5, 6 };
int[] y = { 3, 4, 5, 6, 7, 8, 9, 10 };
var result = x.Zip(y, (a,b) => new { x = a, y = b})
.DistinctBy(z => z.x).ToList();
var xresult = result.Select(z => z.x);
var yresult = result.Select(z => z.y);
// Verify the results
Console.WriteLine(String.Join(",", xresult));
Console.WriteLine(String.Join(",", yresult));

C# - How to find the most common and the least common integers in an array?

I was asked to make a Dice program with two arrays (one for each dice) and add the two results, e.g.: 2 (dice 1) + 6 (dice 2) = 8.
The program must roll the dices 100 times and show the sum each time.
I could do it so far, but the program also must show which sum is the most frequent, and which sum is the least frequent.
Like this: sum = [2, 2, 2, 2, 3, 3, 4, 4, 5, 6, 6]. Most common: 2; Least common: 5.
How can I do it?
This is how my code looks like:
static void Main(string[] args)
{
Random gerador = new Random();
int[] soma = new int[100];
int rolagem = 0;
for(int i = 0; i < soma.Length; i++)
{
rolagem = 0;
rolagem += gerador.Next(6) + 1;
rolagem += gerador.Next(6) + 1;
soma[i] = rolagem;
}
var mais = soma.GroupBy(item => item).OrderByDescending(g => g.Count()).Select(g => g.Key).First();
//NEED TO FIND OUT LEAST COMMON SUM
for (int j = 1; j < soma.Length; j++)
{
Console.Write("{0} ", soma[j]);
}
Console.WriteLine("Soma mais frequente: {0}, Soma menos frequente: {1}", mais, menos);
Console.ReadKey();
}
You're almost there, you can find the least common one similarly:
var array = new[] { 1, 1, 1, 1, 4, 2, 2, 3, 3, 3, 5, 5 };
var result = array.GroupBy(i => i).OrderBy(g => g.Count()).Select(g => g.Key).ToList();
var mostCommon = result.Last();
var leastCommon = result.First();
If you have code that rolls the dice 100 times, you are pretty close. All you need to do is frequency counters.
A roll of a pair of dice yields a number between 2 and 12, inclusive. Make an int count[13] array before entering the loop.
In the loop each time you have two numbers, say, d1 and d2, increment the count as follows:
count[d1+d2]++;
Once the loop is over, find the highest and the lowest numbers in the array between indexes 2 and 12, inclusive. The index of the highest number will be the number with the highest roll count; the index of the lowest number will be the number with the lowest roll count.
Tuple<int, int> least = new Tuple<int, int>(-1, -1), most = new Tuple<int, int>(-1, -1);
List<int> arr = new List<int> { 2, 2, 2, 2, 3, 3, 4, 4, 5, 6, 6 };
var grp = arr.GroupBy(x => x).Select(x=>x).ToList();
foreach (var item in grp)
{
if (least.Item2 == -1 || least.Item2>item.Count())
{
var x = new Tuple<int, int>(item.Key, item.Count());
least = x;
}
if (most.Item2 == -1 || most.Item2 < item.Count())
{
var x = new Tuple<int, int>(item.Key, item.Count());
most = x;
}
}
Console.WriteLine("Least : "+least.Item1+" repeated " + least.Item2+"times");
Console.WriteLine("Most : "+most.Item1 + " repeated " + most.Item2 + "times");
Or as m1kael suggested,
Tuple<int, int> least = new Tuple<int, int>(-1, -1), most = new Tuple<int, int>(-1, -1);
List<int> arr = new List<int> { 2, 2, 2, 2, 3, 3, 4, 4, 5, 6, 6 };
var grp = arr.GroupBy(x => x).OrderBy(x=>x.Count()).Select(x => x.Key).ToList();
Console.WriteLine("Least : "+ grp.First());
Console.WriteLine("Most : "+ grp.Last());
There is a small chance for more than one most or least common:
var a = Enumerable.Repeat(new Random(), 100).Select(r => r.Next(6) + r.Next(6) + 2);
var groups = a.GroupBy(i => i).GroupBy(g => g.Count(), g => g.Key).OrderBy(g => g.Key).ToList();
var mostCommon = string.Join(", ", groups.Last());
var leastCommon = string.Join(", ", groups[0]);

Sort list based on multiple conditions

I have a list of integer lists, like that:
A -> 10 10 1 1 1
B -> 10 9 9 7 6
...
I would like to sort them based on how many 10s they have, then on how many 9s, 8s, 7s, and so on untile the 1s
So in the example above A should be better than B because even if it has less total points, it has two 10s instead of only 1.
Code should be generic because I don't know how many numbers will be available for each case (sometimes 10, sometimes 5, or even only 3).
I developed something like that:
lists.OrderByDescending(a => a.Where(b => b == 10).Count()).
ThenByDescending(a => a.Where(b => b == 9).Count()).
and so on, but this is not generic...
I hope the question is clear... thank you very much!
You can create query which orders lists by count of 10s, then compose query by adding additional orderings for numbers from 9 to 1:
var query = lists.OrderByDescending(l => l.Count(x => x == 10));
for (int i = 9; i >= 1; i--)
query = query.ThenByDescending(l => l.Count(x => x == i));
For these sample lists:
var lists = new[] {
new[] { 10, 9, 9, 8, 7 },
new[] { 10, 9, 9, 7, 6 },
new[] { 10, 10, 1, 1, 1 }
};
Result will be:
[10, 10, 1, 1, 1]
[10, 9, 9, 8, 7]
[10, 9, 9, 7, 6]
It's simple, but not very efficient. If you need better performance, then consider creating custom comparer. Here is sample with comparer which uses zipped ordered sequences to check if all items in sequences are same, or get first item which is different:
public class CustomComparer : Comparer<IList<int>>
{
public override int Compare(IList<int> x, IList<int> y)
{
var comparisons = x.Zip(y, (a,b) => a.CompareTo(b));
foreach(var comparison in comparisons)
{
if (comparison != 0)
return comparison;
}
return x.Count.CompareTo(y.Count);
}
}
NOTE: If items in lists are not ordered, then you should sort them before zipping:
var comparisons =
x.OrderByDescending(i => i)
.Zip(y.OrderByDescending(i => i), (a,b) => a.CompareTo(b));
It works very simple. Consider two lists:
[10, 9, 9, 8, 7, 5]
[10, 9, 9, 7, 6]
It will create pairs of items in corresponding positions:
{10,10}, {9,9}, {9,9}, {8,7}, {7,6}
Then items in each pair will be compared one by one, until first mismatch will be found:
0, 0, 0, 1 (four comparisons only)
That means first list has more 8s than second one. Usage:
var query = lists.OrderByDescending(i => i, new CustomComparer());
Result is same.
The following comparer
public class Comparer : IComparer<IEnumerable<int>>
{
public int Compare(IEnumerable<int> a, IEnumerable<int> b)
{
var aOrdered = a.OrderByDescending(i => i).Concat(new[] { int.MinValue });
var bOrdered = b.OrderByDescending(i => i).Concat(new[] { int.MinValue });
return a.Zip(b, (i, j) => i.CompareTo(j)).FirstOrDefault(c => c != 0);
}
}
lets you order you lists of lists like so
var result = lists.OrderByDescending(i => i, new Comparer());
without iterating through each list ten times counting individual elements.
This compares the lists and returns conventional comparison result - 1, 0, or -1 is returned depending on whether one value is greater than, equal to, or less than the other.
static int CompareLists(List<int> a, List<int> b)
{
var grpA = a.GroupBy(p => p).ToDictionary(k=>k.Key,v=>v.Count());
var grpB = b.GroupBy(p => p).ToDictionary(k=>k.Key,v=>v.Count());
for (int i = 10; i >= 0; i--)
{
int countA = grpA.ContainsKey(i) ? grpA[i] : 0;
int countB = grpB.ContainsKey(i) ? grpB[i] : 0;
int comparison = countA.CompareTo(countB);
if (comparison != 0)
return comparison;
}
return 0;
}
First we convert the lists into dictionary of number->amount of occurences.
Then we iterate through numbers from 10 to 0 and compare the number of occurences. If the result is 0, then we go to another number.
If you have List<List<int>> to sort, just use list.Sort(CompareLists) as in:
List<int> d = new List<int> { 10, 6, 6 };
List<int> b = new List<int> { 10, 9, 9 };
List<int> a = new List<int> { 10, 10, 1, 1, 1 };
List<int> c = new List<int> { 10, 7, 7 };
List<int> e = new List<int> { 9, 3, 7 };
List<int> f = new List<int> { 9, 9, 7 };
List<List<int>> list = new List<List<int>>() { a, b, c, d, e, f };
list.Sort(CompareLists);

Aggregate sub-lists using LINQ

I have a list of objects (i.e. integers) and I want to aggregate sub-lists with LINQ.
For example:
Original list: [ 1, 4, 5, 3, 4, 10, 4, 12 ]
Sub-lists: [ [1,4,5,3], [4,5,3,4], [5,3,4,10], [3,4,10,4], [4,10,4,12] ]
Result (Aggregated List): [ 5, 5, 10, 10, 12 ]
I want to create the maximum of a sub-list for each element containing itself and the following n = 3 elements. Is this possible with LINQ or do I need to create my own aggregation mechanism?
Thanks in advance,Christian
public IEnumerable<IEnumerable<int>> GetSubLists(int[] collection)
{
for(int i = 0; i< collection.Length - 3; i++)
yield return collection.Skip(i).Take(4);
}
GetSubLists(original).Select(l => l.Max());
Or in one line
int[] original = {1, 4, 5, 3, 4, 10, 4, 12 };
int chunkCount = 4;
Enumerable.Range(0, original.Length - chunkCount + 1).Select(i => original.Skip(i).Take(chunkCount))
.Select(l => l.Max());
var result = sublists.Select(sl => sl.Max());
// [5,5,10,10,12]
Creating sub-lists:
List<int> original = new List<int> { 1, 4, 5, 3, 4, 10, 4, 12 };
int sublistSize = 4;
// check if original size is greater than required sublistSize
var sublists = Enumerable.Range(0, original.Count - sublistSize + 1)
.Select(i => original.GetRange(i, sublistSize));
// [[1,4,5,3],[4,5,3,4],[5,3,4,10],[3,4,10,4],[4,10,4,12]]
IEnumerable<int[]> GetLists (int[] list, int size )
{
return Enumerable.Range(0, list.Length - size + 1).Select(x => list.Skip(x).Take(size).ToArray());
}
Sample:
var list = new[] {1, 4, 5, 3, 4, 10, 4, 12};
var max = GetLists(list, 4).Select(x => x.Max()).ToArray();
The Sub-lists intermediate result can be constructed with a "Sliding Window" function.
The desired Result then is the function Max() mapped over the windows with Select().
var originalList = new [] {1, 4, 5, 3, 4, 10, 4, 12};
var sublists = originalList.Window(4); // [ [1,4,5,3], [4,5,3,4], ... ]
var result = sublists.Select(Enumerable.Max); // [ 5, 5, 10, 10, 12 ]
Efficient Window function:
public static IEnumerable<IEnumerable<T>> Window<T>(this IEnumerable<T> source,
int windowSize)
{
if(windowSize < 1) throw new ArgumentException("windowSize must positive", "windowSize");
var q = new Queue<T>(windowSize);
foreach(var e in source)
{
q.Enqueue(e);
if(q.Count < windowSize) continue; // queue not full yet
yield return q;
q.Dequeue();
}
}

Categories

Resources