Generate counts of randomized numbers in C# - c#

I am trying to create a column to count how much a number is generated. Here is an example of the output I would like:
Number 1 has been generated 5 times.
Number 2 has been generated 2 times.
etc.
Here is my current code:
Random r = new Random();
int plus = 0;
for (int a = 0; a < 10; a++)
{
plus++;
Console.Write("Week {0}: ", plus );
for (int i = 0; i < 7; i++)
{
Console.Write(r.Next(1, 11));
Console.Write(", ");
}
Console.WriteLine();
}

Use a Dictionary<int,int>, where the key is the random number and the value is the count:
var count = new Dictionary<int, int>();
for (int i = 0; i < 7; i++)
{
var rndValue = r.Next(1, 11);
if (count.ContainsKey(rndValue))
count[rndValue]++;
else
count.Add(rndValue, 1);
Console.Write(rndValue);
Console.Write(", ");
}
foreach (var c in count)
Console.WriteLine("Number {0} has been generated {1} time(s).", c.Key, c.Value);
If you want to print the results for any numbers that are generated 0 times, you'll have to add some additional code to make sure a value of 0 is stored in the Dictionary for those values.
Something like this before the foreach statement should work for you:
for (var i = 1; i < 11; i++)
if (!count.ContainsKey(i))
count.Add(i, 0);
An alternate solution, using a single-dimensional array, as suggested in the comments. A Dictionary is pretty straight-forward, but this may be even easier to understand.
var count = new int[10];
for (int i = 0; i < 7; i++)
{
var nextRnd = r.Next(1, 11);
count[nextRnd - 1]++;
Console.Write(nextRnd);
Console.Write(", ");
}
for (var i = 0; i < count.Length; i++)
Console.WriteLine("Number {0} has been generated {1} time(s).", i + 1, count[i]);

I'd like to using a Dictionary to implement this function:
Dictionary<int,int> rand_count_list = new Dictionary<int,int>();
Random r = new Random();
int plus = 0;
for (int a = 0; a < 10; a++)
{
plus++;
Console.Write("Week {0}: ", plus);
for (int i = 0; i < 7; i++)
{
int rand = r.Next(1, 11);
if (rand_count_list.ContainsKey(rand))
rand_count_list[rand]++;
else
rand_count_list[rand] = 1;
Console.Write(rand);
Console.Write(", ");
}
Console.WriteLine();
}
foreach (int key in rand_count_list.Keys)
{
Console.Write("Number {0} has been generated {1} times. ", key, rand_count_list[key]);
}

var r = new Random();
var weeks = new List<List<int>>(
Enumerable.Range(0, 10)
.Select(w => new List<int>(Enumerable.Range(0, 7)
.Select(i => r.Next(1, 11)))));
foreach (var week in weeks)
{
Console.WriteLine("Week {0}: {1}", weeks.IndexOf(week), string.Join(",", week));
}
var allNumbers = weeks.SelectMany(n => n);
foreach (var number in allNumbers.Distinct().OrderBy(n => n))
{
Console.WriteLine("{0} was generated {1} times",
number,
allNumbers.Count(n => n == number));
}

Related

User input array and display frequency of each integer

This is the problem given:
Write a program in C# that prompts the user to enter a number of integer values. The program
stores the integers in an array, counts the frequency of each integer and displays the frequency on the Console.
I am new to C# and don`t know Linq or dictionary yet.
static void Main(string[] args) {
//declare variables
int n,
y,
x;
int[] index;
int[] count;
//prompt user for the number of elements
Console.WriteLine("Please enter the number of element to be stored");
n = int.Parse(Console.ReadLine());
index = new int[n];
count = new int[n];
//Console.WriteLine(index.Length);
Console.WriteLine("input {0} number of element", n);
for (int i = 0; i < index.Length; i++) {
index[i] = int.Parse(Console.ReadLine());
for (y = 0; y < index.Length; y++)
if (index[y] == i) count[i]++;
}
for (int j = 0; j < index.Length; j++)
Console.WriteLine("element - {0} : {1}", j, index[j]);
Console.WriteLine("the frequency of all elements of the array");
for (x = 0; x < index.Length; x++)
Console.WriteLine("{0} occurs {1} times", index[x], x);
}
This the result I get if I select 3 integers of 2, 1, 2. 2 is repeated.
2 occurs 0 times.
1 occurs 1 times.
2 occurs 2 times.
You’re doing in wring way. And perform wise also it’s not efficient solution. You are run for loop inside for loop. You can use dictionary and store the count in it, so it will work in a single for loop.
static void GetFrequency(int[] arr)
{
Dictionary<int, int> mp = new Dictionary<int, int>();
// Traverse through array elements and count frequencies
for (int i = 0; i < arr.Length; i++)
{
if (mp.ContainsKey(arr[i]))
{
var val = mp[arr[i]];
mp.Remove(arr[i]);
mp.Add(arr[i], val + 1);
}
else
{
mp.Add(arr[i], 1);
}
}
}
Usually, when we want to query (i.e. find out frequencies) a collection (array) we use Linq:
using System.Linq;
...
var freqData = index
.GroupBy(item => item)
.Select(group => $"{group.Key} occurs {group.Count()} times.");
Console.WriteLine(string.Join(Environment.NewLine, freqData));
If you want to count frequency manually, you can use Dictionary<int, int> to have Key (item from the array)
and Value (frequency) correspondence:
Dictionary<int, int> freqData = new Dictionary<int, int>();
foreach (int item in index)
if (freqData.TryGetValue(item, out int freq))
freqData[item] = freq + 1;
else
freqData.Add(item, 1);
foreach (var pair in freqData)
Console.WriteLine($"{pair.Key} occurs {pair.Value} times.");

Count the number of occurrences in an array with random numbers without methods or list in C#

I am trying to solve an exercise in C# as follows:
Write a program that generates 20 random integers between 0 and 9 and displays the count for each number.
Use an array of ten integers, say counts, to store the counts for the number of 0s, 1s, ..., 9s.)
This is what i come up with which kind of work but i have a problem with the 0's counting 1 extra all the time.
using System.Collections.Generic;
using System.Text;
namespace ArrayExercises
{
class TaskFive
{
public static void FindNumberCount()
{
int c0=0,c1=02,c2=0,c3=0,c4=0,c5=0,c6=0,c7=0,c8=0,c9=0;
int[] arr = new int[20];
Random rand = new Random();
Console.WriteLine("Numbers generated ");
for (int i = 0; i < 19; i++)
{
arr[i] = rand.Next(0, 10);
Console.WriteLine(arr[i]);
}
foreach(int number in arr)
{
if (number == 0) { c0++; }
else if (number == 1) { c1++; }
else if (number == 2) { c2++; }
else if (number == 3) { c3++; }
else if (number == 4) { c4++; }
else if (number == 5) { c5++; }
else if (number == 6) { c6++; }
else if (number == 7) { c7++; }
else if (number == 8) { c8++; }
else if (number == 9) { c9++; }
}
Console.WriteLine
(
$"Number of 0's: {c0} \n" +
$"Number of 1's: {c1} \n" +
$"Number of 2's: {c2} \n" +
$"Number of 3's: {c3} \n" +
$"Number of 4's: {c4} \n" +
$"Number of 5's: {c5} \n" +
$"Number of 6's: {c6} \n" +
$"Number of 7's: {c7} \n" +
$"Number of 8's: {c8} \n" +
$"Number of 9's: {c9}"
);
}
}
}
Thanks in advance :)
You could shorten it like this
public static void FindNumberCount()
{
int[] count = new int[10];
Random rand = new Random();
int[] arr = new int[20];
for (int i = 0; i < arr.Length; i++)
{
arr[i] = rand.Next(0, 10);
Console.WriteLine(arr[i]);
count[arr[i]]++;
}
for (int i = 0; i < count.Length; i++)
{
Console.WriteLine($"Number of {i}'s: {count[i]}");
}
}
If you want draw 20 numbers you should for (int i = 0; i < 20; i++) not 19.
int[] counts = new int[10];
int[] numbers = new int[20];
var random = new Random();
for (int i = 0; i < numbers.Length; i++)
{
// Generate random numbers
numbers[i] = random.Next(0, 9);
// Increment the count of the generated number
counts[numbers[i]]++;
}
the for loop you use loops only 19 times.
You must change the "i < 19" to "i < 20".
In your program, the for loop leaves the last int in the array at it's default value (0),
this also explains, why you always have one more zero.
Hope this helped.
The issues is in this line
for (int i = 0; i < 19; i++)
You initialized an array with 20 int and set only value to 19 of them.
If you don't set the value int defaults to Zero and hence you always get one extra zero
Change your code as below
for (int i = 0; i <= 19; i++)
The halting condition of the first for loop should be i<20. Then your program should work.
This is how I would solve it:
static void Main(string[] args)
{
Random random = new Random();
//Fill array with random numbers
int[] array = new int[20];
for (int i = 0; i < array.Length; i++)
array[i] = random.Next(0, 10);
//Count how many times a number occurs
int[] numberCounts = new int[10];
for (int i = 0; i < array.Length; i++)
numberCounts[array[i]]++;
//Print the count of the numbers
for(int i = 0; i < numberCounts.Length; i++)
Console.WriteLine("Number of " + i + "'s: " + numberCounts[i]);
//Keep the console open
Console.ReadLine();
}

Comparing arrays without using linq

I am making a lottery game that asks the user for 10 numbers and then check it against random numbers that i have created in an array. I need to compare the two but I am not allowed to use the contains method.
I think I need to use a foreach loop to compare the arrays but really I am at a loss of what to do. I have been piecing it together from the little I know and would like to know if I am on the right track.
Is a foreach loop the correct way to compare the two arrays?
This is my code so far.
using System;
namespace lotto2
{
class Program
{
static void Main(string[] args)
{
//an array named "input" to hold the users' 10 guesses
int[] inputs = new int[10];
//an array named "lotNum" to hold 10 random numbers
int[] lotNums = new int[10];
//a for loop to loop over the inputs array. each loop will ask the user for a number
Console.WriteLine("Enter your 10 lottery numbers one at a time. The numbers must be between 1 and 25.");
for (int i = 0; i < inputs.Length; i++)
{
inputs[i] = Convert.ToInt32(Console.ReadLine());
}
//a random number generator
Random ranNum = new Random();
//loop to call the random generator 10 times and store 10 random numbers in the "lotNum" array
for (int i = 0; i < 10; i++)
{
lotNums[i] = ranNum.Next(1, 26); //chooses random numbers between 1 and 25
}
//writes out the randomly generated lotto numbers
Console.Write("\nThe lottery numbers are: ");
for (int i = 0; i < 10; i++)
{
Console.Write("{0} ", lotNums[i]);
}
//loop for checking users inputs against random generated numbers..
//foreach loop maybe?
foreach (var input in lotNums)
{
}
//print out if there are any matches, which numbers matched
//declared integer for the correct numbers the user guessed
int correct;
//end progam
Console.WriteLine("\n\nPress any key to end the program:");
Console.ReadKey();
}
}
}
Here's a program that correctly does what you want. It even ensures that you don't have duplicate lotto numbers.
void Main()
{
const int count = 10;
const int max = 25;
//an array named "input" to hold the users' 10 guesses
int[] inputs = new int[count];
//a for loop to loop over the inputs array. each loop will ask the user for a number
Console.WriteLine("Enter your {0} lottery numbers one at a time. The numbers must be between 1 and {1}.", count, max);
for (int i = 0; i < inputs.Length; i++)
{
inputs[i] = Convert.ToInt32(Console.ReadLine());
}
//a random number generator
Random ranNum = new Random();
//an array named "allNums" to hold all the random numbers
int[] allNums = new int[max];
for (int i = 0; i < allNums.Length; i++)
{
allNums[i] = i + 1;
}
//shuffle
for (int i = 0; i < allNums.Length; i++)
{
int j = ranNum.Next(0, allNums.Length);
int temporary = allNums[j];
allNums[j] = allNums[i];
allNums[i] = temporary;
}
//an array named "lotNum" to hold 10 random numbers
int[] lotNums = new int[count];
Array.Copy(allNums, lotNums, lotNums.Length);
//writes out the randomly generated lotto numbers
Console.Write("\nThe lottery numbers are: ");
for (int i = 0; i < lotNums.Length; i++)
{
Console.Write("{0} ", lotNums[i]);
}
int correct = 0;
Console.Write("\nThe correct numbers are: ");
for (int i = 0; i < lotNums.Length; i++)
{
for (int j = 0; j < inputs.Length; j++)
{
if (lotNums[i] == inputs[j])
{
Console.Write("{0} ", lotNums[i]);
correct++;
};
}
}
Console.Write("\nYou got {0} correct. ", correct);
Console.WriteLine("\n\nPress any key to end the program:");
Console.ReadLine();
}
You're on the right way.
My implementation would be:
foreach (var input in inputs)
{
for (int i = 0; i < lotNums.Length; i++){
if(input == lotNums[i]){
Console.WriteLine(lotNums[i]);
}
}
}
This will compare every number of the input array with the lottery array.
I'm printing every match, but you can set a variable to True if it finds a match or add every matching number into an array if you need it.
This is what I have tried.I hope it makes sense?
static void LottoMethod(int[] randNums,int[] userNums)
{
Console.WriteLine("Guess 10 numbers");
for(int i = 0; i <= userNums.Length-1; i++)
{
userNums[i] = Int32.Parse( Console.ReadLine());
}
Console.WriteLine("The numbers you entered: ");
foreach(int k in userNums)
{
Console.Write(k+" ");
}
//generate 10 numbers randomly
Random rnds = new Random();
for(int k = 0; k <= randNums.Length - 1; k++)
{
randNums[k] = rnds.Next(1, 26);
}
Console.WriteLine("Random Numbers");
foreach(int i in randNums)
{
Console.Write(i + " ");
}
int correctNums = 0;
//Check if random numbers correspond with entered numbers
try
{
for(int i = 0; i <= randNums.Length-1; i++)
{
for(int j = 0; j <= userNums.Length-1; j++)
{
if (randNums[i] == userNums[j])
{
correctNums++;
}
}
}
Console.WriteLine($"There are {correctNums} numbers ");
}
catch(Exception e) {
throw new Exception(e.ToString());
}
}
You have to calculate intersection of two sequences. You have three options:
Double foreach loop. This is something to avoid as it has time complexity O(m*n). It it not a problem for 10 items, but we should make programs that scale.
Using hash join. You can use HashSet for this and it would be my preferred method. But as it inherently implies using Contains, it is not the option here.
Merging sorted sequences. This would be the way to go here.
The program is rather self explanatory, it produces and intersects two random sequences.
static Random rnd = new Random((int)DateTime.Now.Ticks);
static int[] GetRandomArray(int arrSize, int minNumber, int maxNumber)
{
int[] tmpArr = new int[maxNumber - minNumber + 1];
for (int i = 0; i < tmpArr.Length; ++i)
{
tmpArr[i] = i + minNumber; // fill with 1, 2, 3, 4,...
}
int[] ret = new int[arrSize];
for (int i = 0; i < ret.Length; ++i)
{
int index = rnd.Next(tmpArr.Length - i); //choose random position
ret[i] = tmpArr[index];
tmpArr[index] = tmpArr[tmpArr.Length - 1 - i]; //fill last of the sequence into used position
}
return ret;
}
static IEnumerable<int> GetMatches(int[] a, int[] b)
{
Array.Sort(a);
Array.Sort(b);
for (int i = 0, j = 0; i < a.Length && j < b.Length;)
{
if (a[i] == b[j])
{
yield return a[i];
++i;
++j;
}
else if (a[i] > b[j])
{
++j;
}
else
{
++i;
}
}
}
static void Main(string[] args)
{
var a = GetRandomArray(5, 3, 7);
var b = GetRandomArray(10, 1, 25);
Console.WriteLine("A: " + string.Join(", ", a));
Console.WriteLine("B: " + string.Join(", ", b));
Console.WriteLine("Matches: " + string.Join(", ", GetMatches(a, b)));
Console.ReadKey();
}
The result is something like:
A: 7, 4, 6, 3, 5
B: 17, 1, 8, 14, 11, 22, 3, 20, 4, 25
Matches: 3, 4
You can think about what would happen if one or both of the sequences contain duplicities.

Storing values in an array and outputting the highest,lowest,average. Output flaw

This was a small problem the teacher gave us at school. We were asked to make a program which keeps asking the user to input test scores until he inputs -99 to stop the program. The values are stored in an array and the highest, lowest and average scores are displayed.
The problem I have with my code is that whenever I run it it always gives me a value of 0 for lowest scores even though there are no zeros in the input. Can anyone please point out the mistake in my code?
Code:
static void Main(string[] args)
{
int[] za = new int[100];
scores(za, 0);
}
public static void scores(int[] ar, int x)
{
Console.Write("Please enter homework score [0 to 100] (-99 to exit): ");
int a = Convert.ToInt16(Console.ReadLine());
if (a != -99)
{
ar[x] = a;
x++;
scores(ar, x);
}
else
{
Console.Clear();
Console.WriteLine("Homework App");
int[] arr = new int[x];
foreach (int l in arr)
{
arr[l] = ar[l];
}
int lowest = arr[0];
for (int i = 1; i < arr.Length; i++)
{
if (lowest > arr[i]) { lowest = arr[i]; }
}
int highest = arr[0];
for (int j = 1; j < arr.Length; j++)
{
if (highest < arr[j]) { highest = arr[j]; }
}
double sum = 0;
double nums = 0;
for (int k = 0; k < arr.Length; k++)
{
sum = sum + arr[k];
nums++;
}
double average = sum / nums;
Console.WriteLine("Highest Score: {0}", highest);
Console.WriteLine("Lowest Score: {0}", lowest);
Console.WriteLine("Average Score: {0}", average);
Console.ReadLine();
}
}
When you're copying items, don't use a foreach loop and then the element stored in the array as the index that you're inserting to. Instead, use a for loop with a counter variable.
You should change this:
int[] arr = new int[x];
foreach (int l in arr)
{
// You're using the wrong value for the index here.
// l represents the value of the item stored in `arr`, which is `0`
arr[l] = ar[l];
}
To this:
int[] arr = new int[x];
for (var counter = 0; counter < x; counter++)
{
arr[counter] = ar[counter];
}

Sorting many TValue arrays with one TKey array

I have an outer array of N inner arrays of size M. I want to sort each inner array according to another array K exactly in the same way as a built-in Array.Sort<TKey, TValue> Method (TKey[], TValue[], IComparer<TKey>) .NET method does.
The method modifies the Key array after sorting, so I can use it to sort only single inner array. To sort many arrays, I copy the Key array to another KeyBuffer array for each inner array, reusing the KeyBuffer on each sorting step and avoiding allocation and GC. Is that the most efficient way if the typical N is 10K-100K and M < 1000? Given the low size of M the copying and sorting should be done in CPU cache, - which is the fastest that I can get?
My concern is that by doing so, I am sorting the buffer and discarding the results (N-1) times, which is a kind of waste. Also I am doing actual sorting N times, but after the first sorting I already know a map of old indexes to new indexes and I could somehow reuse that mapping for other (N-1) steps.
How would you avoid unnecessary sorting and apply known mapping from the first step to other steps?
Here is the code how I do it now. The question is if it is possible to do it more efficiently.
using System;
using System.Collections.Generic;
namespace MultiSorting {
class Program {
static void Main(string[] args) {
var N = 10;
var M = 5;
var outer = new List<string[]>(N);
for (var i = 0; i < N; i++) {
string[] inner = { "a" + i, "d" + i, "c" + i, "b" + i, "e" + i };
outer.Add(inner);
}
int[] keys = { 1, 4, 3, 2, 5 };
var keysBuffer = new int[M];
for (int i = 0; i < N; i++) {
Array.Copy(keys, keysBuffer, M);
// doing sort N times, but we know the map
// old_index -> new_index from the first sorting
// plus we sort keysBuffer N times but use the result only one time
Array.Sort(keysBuffer, outer[i]);
}
keys = keysBuffer;
foreach (var key in keys) {
Console.Write(key + " "); // 1, 2, 3, 4, 5
}
Console.WriteLine("");
for (var i = 0; i < N; i++) {
foreach (var item in outer[i]) {
Console.Write(item + " "); // a{i}, b{i}, c{i}, d{i}, e{i}
}
Console.WriteLine("");
}
Console.ReadLine();
}
}
Just played with this and implemented mapping reuse directly in a for loop. I didn't expect that a simple loop instead of native built-in methods could speed up things, probably because I had underestimated algorithmic costs of sorting vs costs of array looping and I used to relax when a profiler said the job was mostly done inside .NET methods...
Naive is the code from the question, ReuseMap is what is described in words in the question, Linq is from the answer by #L.B. ...InPlace modifies input, ...Copy doesn't.
Results with N = 2000, M = 500, 10 runs, in milliseconds:
NaiveInPlace: 1005
ReuseMapInPlace: 129 (Log2(500) = 9.0, speed-up = 7.8x)
NaiveCopy: 1181
ReuseMapCopy: 304
LinqCopy: 3284
The entire test is below:
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
namespace MultiSorting {
class Program {
static void Main() {
const int n = 2;
const int m = 10;
var keys = GenerateKeys(m);
foreach (var key in keys) {
Console.Write(key + " ");
}
Console.WriteLine("");
var keysBuffer = new int[keys.Length];
Array.Copy(keys, keysBuffer, keys.Length);
Array.Sort(keysBuffer);
foreach (var key in keysBuffer) {
Console.Write(key + " ");
}
Console.WriteLine("");
// warm up, check that output is the same
List<string[]> outer = MultiSortNaiveInPlace(keys, GenerateOuter(n, m));
PrintResults(outer);
outer = MultiSortNaiveCopy(keys, GenerateOuter(n, m));
PrintResults(outer);
outer = MultiSortReuseMapInPlace(keys, GenerateOuter(n, m));
PrintResults(outer);
outer = MultiSortReuseMapCopy(keys, GenerateOuter(n, m));
PrintResults(outer);
outer = MultiSortLinqCopy(keys, GenerateOuter(n, m));
PrintResults(outer);
// tests
keys = GenerateKeys(500);
NaiveInPlace(2000, 500, keys);
ReuseMapInPlace(2000, 500, keys);
NaiveCopy(2000, 500, keys);
ReuseMapCopy(2000, 500, keys);
LinqCopy(2000, 500, keys);
Console.ReadLine();
}
private static void NaiveInPlace(int n, int m, int[] keys) {
const int rounds = 10;
var source = new List<List<string[]>>(rounds);
for (int i = 0; i < rounds; i++) {
source.Add(GenerateOuter(n, m));
}
GC.Collect();
var sw = Stopwatch.StartNew();
for (int i = 0; i < rounds; i++) {
source[i] = MultiSortNaiveInPlace(keys, source[i]);
}
sw.Stop();
Console.WriteLine("NaiveInPlace: " + sw.ElapsedMilliseconds);
}
private static void ReuseMapInPlace(int n, int m, int[] keys) {
const int rounds = 10;
var source = new List<List<string[]>>(rounds);
for (int i = 0; i < rounds; i++) {
source.Add(GenerateOuter(n, m));
}
GC.Collect();
var sw = Stopwatch.StartNew();
for (int i = 0; i < rounds; i++) {
source[i] = MultiSortReuseMapInPlace(keys, source[i]);
}
sw.Stop();
Console.WriteLine("ReuseMapInPlace: " + sw.ElapsedMilliseconds);
}
private static void NaiveCopy(int n, int m, int[] keys) {
const int rounds = 10;
var source = new List<List<string[]>>(rounds);
for (int i = 0; i < rounds; i++) {
source.Add(GenerateOuter(n, m));
}
GC.Collect();
var sw = Stopwatch.StartNew();
for (int i = 0; i < rounds; i++) {
source[i] = MultiSortNaiveCopy(keys, source[i]);
}
sw.Stop();
Console.WriteLine("NaiveCopy: " + sw.ElapsedMilliseconds);
}
private static void ReuseMapCopy(int n, int m, int[] keys) {
const int rounds = 10;
var source = new List<List<string[]>>(rounds);
for (int i = 0; i < rounds; i++) {
source.Add(GenerateOuter(n, m));
}
GC.Collect();
var sw = Stopwatch.StartNew();
for (int i = 0; i < rounds; i++) {
source[i] = MultiSortReuseMapCopy(keys, source[i]);
}
sw.Stop();
Console.WriteLine("ReuseMapCopy: " + sw.ElapsedMilliseconds);
}
private static void LinqCopy(int n, int m, int[] keys) {
const int rounds = 10;
var source = new List<List<string[]>>(rounds);
for (int i = 0; i < rounds; i++) {
source.Add(GenerateOuter(n, m));
}
GC.Collect();
var sw = Stopwatch.StartNew();
for (int i = 0; i < rounds; i++) {
source[i] = MultiSortLinqCopy(keys, source[i]);
}
sw.Stop();
Console.WriteLine("LinqCopy: " + sw.ElapsedMilliseconds);
}
private static void PrintResults(List<string[]> outer) {
for (var i = 0; i < outer.Count; i++) {
foreach (var item in outer[i]) {
Console.Write(item + " "); // a{i}, b{i}, c{i}, d{i}, e{i}
}
Console.WriteLine("");
}
}
private static int[] GenerateKeys(int m) {
var keys = new int[m];
for (int i = 0; i < m; i++) { keys[i] = i; }
var rnd = new Random();
keys = keys.OrderBy(x => rnd.Next()).ToArray();
return keys;
}
private static List<string[]> GenerateOuter(int n, int m) {
var outer = new List<string[]>(n);
for (var o = 0; o < n; o++) {
var inner = new string[m];
for (int i = 0; i < m; i++) { inner[i] = "R" + o + "C" + i; }
outer.Add(inner);
}
return outer;
}
private static List<string[]> MultiSortNaiveInPlace(int[] keys, List<string[]> outer) {
var keysBuffer = new int[keys.Length];
foreach (var inner in outer) {
Array.Copy(keys, keysBuffer, keys.Length);
// doing sort N times, but we know the map
// old_index -> new_index from the first sorting
// plus we sort keysBuffer N times but use the result only one time
Array.Sort(keysBuffer, inner);
}
return outer;
}
private static List<string[]> MultiSortNaiveCopy(int[] keys, List<string[]> outer) {
var result = new List<string[]>(outer.Count);
var keysBuffer = new int[keys.Length];
for (var n = 0; n < outer.Count(); n++) {
var inner = outer[n];
var newInner = new string[keys.Length];
Array.Copy(keys, keysBuffer, keys.Length);
Array.Copy(inner, newInner, keys.Length);
// doing sort N times, but we know the map
// old_index -> new_index from the first sorting
// plus we sort keysBuffer N times but use the result only one time
Array.Sort(keysBuffer, newInner);
result.Add(newInner);
}
return result;
}
private static List<string[]> MultiSortReuseMapInPlace(int[] keys, List<string[]> outer) {
var itemsBuffer = new string[keys.Length];
var keysBuffer = new int[keys.Length];
Array.Copy(keys, keysBuffer, keysBuffer.Length);
var map = new int[keysBuffer.Length];
for (int m = 0; m < keysBuffer.Length; m++) {
map[m] = m;
}
Array.Sort(keysBuffer, map);
for (var n = 0; n < outer.Count(); n++) {
var inner = outer[n];
for (int m = 0; m < map.Length; m++) {
itemsBuffer[m] = inner[map[m]];
}
Array.Copy(itemsBuffer, outer[n], inner.Length);
}
return outer;
}
private static List<string[]> MultiSortReuseMapCopy(int[] keys, List<string[]> outer) {
var keysBuffer = new int[keys.Length];
Array.Copy(keys, keysBuffer, keysBuffer.Length);
var map = new int[keysBuffer.Length];
for (int m = 0; m < keysBuffer.Length; m++) {
map[m] = m;
}
Array.Sort(keysBuffer, map);
var result = new List<string[]>(outer.Count);
for (var n = 0; n < outer.Count(); n++) {
var inner = outer[n];
var newInner = new string[keys.Length];
for (int m = 0; m < map.Length; m++) {
newInner[m] = inner[map[m]];
}
result.Add(newInner);
}
return result;
}
private static List<string[]> MultiSortLinqCopy(int[] keys, List<string[]> outer) {
var result = outer.Select(arr => arr.Select((item, inx) => new { item, key = keys[inx] })
.OrderBy(x => x.key)
.Select(x => x.item)
.ToArray()) // allocating
.ToList(); // allocating
return result;
}
}
}

Categories

Resources