C# Array / List performance difference - c#

Consider the following methods:
static Stopwatch ArrayTest(int size)
{
var arr = new int[size];
Stopwatch stw = new Stopwatch();
stw.Start();
for (int i = 0; i < size; i++)
{
arr[i] = i / div;
}
var rnd = new Random(1);
var sz2 = size / div;
for (int i = 0; i < sz2; i++)
{
var sz = size - i;
var ix = rnd.Next(sz);
Array.Copy(arr, ix + 1, arr, ix, size - ix - 1);
arr[sz - 1] = 0;
}
double sum = 0.0;
for (int i = 0; i < size - sz2; i++)
{
sum += arr[i];
}
stw.Stop();
Console.Write(" Array: {0}", sum);
return stw;
}
static Stopwatch ListTest(int size)
{
var lst = new List<int>();
Stopwatch stw = new Stopwatch();
stw.Start();
for (int i = 0; i < size; i++)
{
lst.Add(i / div);
}
var rnd = new Random(1);
var sz2 = size / div;
for (int i = 0; i < sz2; i++)
{
var ix = rnd.Next(lst.Count);
lst.RemoveAt(ix);
}
double sum = 0.0;
for (int i = 0; i < lst.Count; i++)
{
sum += lst[i];
}
stw.Stop();
Console.Write(" List: {0}", sum);
return stw;
}
div = 2 and size = 200000.
Running this (complied at Release) produces the following:
Array: 5012641699 12.8367529 s
List: 5012641699 6.1027289 s
According to http://referencesource.microsoft.com/#mscorlib List.RemoveAt is implemented like this:
// Removes the element at the given index. The size of the list is
// decreased by one.
//
public void RemoveAt(int index) {
if ((uint)index >= (uint)_size) {
ThrowHelper.ThrowArgumentOutOfRangeException();
}
Contract.EndContractBlock();
_size--;
if (index < _size) {
Array.Copy(_items, index + 1, _items, index, _size - index);
}
_items[_size] = default(T);
_version++;
}
So I wonder why the ArrayTest requires twice the time that ListTest does. It seems to me that they are both doing pretty much the same thing, in fact I'd expect ArrayTest to be faster due to less overhead. Perhaps I have missed something obvious?
Update
Let me explain the example. The idea was to measure the performance under conditions of random deletes (while still retaining indexed access). The first part of each method initializes an array/list where each element in the array is 1/2 the index (integer division). The next part randomly removes 1/2 the elements. The last loop simply sums the values remaining in the array/list which is used as a checksum (to compare results). Profiling the code shows that in ArrayTest the Array.Copy uses the most time and in ListTest the lst.RemoveAt uses the most time.
I will attempt to replicate the behavior in a simpler example and update it here.

The problem seems to be with the line:
Array.Copy(arr, ix + 1, arr, ix, size - ix - 1);
in method ArrayTest. It should read
Array.Copy(arr, ix + 1, arr, ix, sz - ix - 1);
since the variable size is not changed when the array is resized, instead sz, the actual size, is calculated in each iteration.

There is a flaw in your test program. You've made it overly convoluted and thus hard to prove on paper. The only way to figure out what your code is doing is by debugging, hand-calculating everything.
So instead I made a simpler test.
void Main()
{
const int LENGTH = 200000;
Stopwatch sw = Stopwatch.StartNew();
var a = new int[LENGTH];
for (int index = LENGTH-1; index > 0; index--)
Array.Copy(a, 1, a, 0, index);
sw.Stop();
sw.ElapsedMilliseconds.Dump();
var l = new List<int>(a);
sw = Stopwatch.StartNew();
for (int index = LENGTH-1; index > 0; index--)
l.RemoveAt(0);
sw.Stop();
sw.ElapsedMilliseconds.Dump();
}
It simply "removes" the 0th element of an array, and a list. The results on my machine are:
3366
3442
3270
3242
3343
3385
That's 3 runs.

Related

No Output for MergeSort Algorithm c#

In my project I've implemented MergeSort, bubble sort, and sequential search algorithms, however the Merge sort is not giving any output to the console, while the others work. I've tried debugging with no real luck.
public static void mergeSort(int[] a)
{
int inputLength = a.Length;
//sets the middle index to the total length divided by 2
int midIndex = a.Length / 2;
//sets the last index to the length minus one (minus one included so
an out of bounds exception is avoided
int endIndex = a.Length - 1;
//left side set to the middle index size
int[] leftArray = new int[midIndex];
//right side set to the total length minus the middle length
int[] rightArray = new int[inputLength - midIndex];
//looping from zero to middle of the array
for (int i = 0; i < midIndex; i++)
{
leftArray[i] = a[i];
}
//looping from the middle to the end of the array
for(int i = midIndex; i < inputLength; i++)
{
rightArray[i - midIndex] = a[i];
}
//recursively called the method to sort these two sides
mergeSort(leftArray);
mergeSort(rightArray);
//this calls the merge method to put the two halves together
merge(a, leftArray, rightArray);
}
private static void merge(int[] a, int[] leftHalf, int[] rightHalf)
{
int leftSize = leftHalf.Length;
int rightSize = rightHalf.Length;
int i = 0, j = 0, k = 0;
//loops until no more elements in left or right array
while(i < leftSize && j < rightSize)
{
if(leftHalf[i] <= rightHalf[j])
{
//sets the element at iteration k to the elements at i in the
left half if
//it is smaller than the right half
a[k] = leftHalf[i];
i++;
}
else
{
//if the right half is smaller, set element at iteration k
equal to the
//element at index j of the right half
a[k] = rightHalf[j];
j++;
}
k++;//iterate K
}
//these account for leftover elements after the above while loop.
while (i < leftSize)
{
a[k] = leftHalf[i];
i++;
k++;
}
while (j < rightSize)
{
a[k] = rightHalf[j];
j++;
k++;
}
}
My main method is here:
static void Main(string[] args)
{
try{
TextFileReader reader = new TextFileReader();
int[] numberArray = reader.ReadFile("numbers.txt");
//printArray(numberArray);
Sorts sorts = new Sorts();
//sorts.bubbleSort(numberArray);
//printArray(numberArray);
//sorts.selectionSort(numberArray);
Searches searches = new Searches();
//searches.SequentialSearch(numberArray, 897);
//searches.binarySearch(numberArray, 9992);
//Console.WriteLine("\n\nArray length: " + numberArray.Length);
mergeSort(numberArray);
printArray(numberArray);
}
catch(IOException ex)
{
Console.WriteLine("IO Exception Found.");
}
}
public static void printArray(int[] numberArray)
{
foreach(int i in numberArray)
{
Console.WriteLine(i);
}
}
}
}
This is driving me crazy because the other algorithms are working and outputting correctly, but when the MergeSort is ran it gives no exceptions or errors.

Stopwatch startup issues

I've got a c# program that tests a sort algorithm and it's performance by using a instance of the Stopwatch class.
So far everything is working correctly and I am getting the expected tick results except in the first run.Somehow the Stopwatch class needs about 900 ticks longer for the first calculation.
Do I have to initiate the Stopwatch class somehow different or is there any way to fix this?
static void Main() {
watch = new Stopwatch();
int amount = 10; // Amount of arrays to test
long[, ] stats= new long[3, amount]; // Array that stores ticks for every size (100,1000,10000) 'amount'-times
for (int size = 100, iteration = 0; size <= 10000; size *= 10, iteration++) {
for (int j = 0; j < amount; j++) {
stats[iteration, j] = TestSort(size); // Save ticks for random tested array in stats
}
}
PrintStats(stats);
}
public static long TestSort(int length) {
int[] testArray = GenerateRandomArray(length); // Generate a random array with size of length
watch.Reset();
watch.Start();
sort(testArray);
watch.Stop();
return watch.ElapsedTicks;
}
public static void PrintStats(long[, ] array) {
for (int i = 0; i < array.GetLength(0); i++) {
Console.Write("[");
for (int j = 0; j < array.GetLength(1); j++) {
Console.Write(array[i, j]);
if (j < array.GetLength(1) - 1) {
Console.Write(",");
}
}
Console.Write("]\n");
}
}
// Sample output
// Note that first entry is about 900 ticks longer then the other ones with size 100
[1150,256,268,262,261,262,263,261,263,262]
[19689,20550,20979,22953,19913,20578,19693,19945,19811,19970]
[1880705,1850265,3006533,1869953,1900301,1846915,1840681,1801887,1931206,2206952]

Strand sort in c# using arrays only

i have a homework using strand sort and i must take the growing sequence of numbers from the initial array and merge them together in the array that represents our result (C#)
Like this one http://imgur.com/nQFzJw7
So far i did something like that
public static int[] Str(int[] a)
{
int i, j, x ,temp,k=0,count=1;
int size = a.Length;
int len = a.Length;
Strand = new int[size];
Merged = new int[size];
for (i = k; i < size; i++)
{
x = a[i];
Strand[0] = x;
for (i = k; i < size; i++) //checking if there's a bigger int than the first one
{
if (a[i] > x)
{
x = a[i];
}
}
for (i = k; i < len; i++)
{
if (a[i] == x) // checking if the max appears more than 1 time
{
temp = a[i];
a[i] = a[len];
a[len] = temp;
len--; //swaps the max numbers to the last position
Strand[count] = x;
count++;
}
}
for (i = 0; i < count; i++) // cant find a way to put in the final merged and sorted array
{
}
count = 1;
k++;
}
Any suggestions?
You always need to extract the first element into strand
strand[0] = a[0]
count = 1
Then you need to extract suitable elements into strand, shifting the rest of array
for i = 1 to size - 1
if a[i] >= strand[count - 1]
strand[count++] = a[i]
else
a[i - count] = a[i]
size = size - count
Then you need merge current strand and merged - look for Merge procedure from MergeSort
Repeat these steps until size becomes 0

Subarrays from a 2D multdimensional array

I've run into a stall trying to put together some code to average out 10x10 subarrays of a 2D multidimensional array.
Given a multidimensional array
var myArray = new byte[100, 100];
How should I go about creating 100 subarrays of 100 bytes (10x10) each.
Here are some examples of the value indexes the subarrays from the multidimensional would contain.
[x1,y1,x2,y2]
Subarray1[0,0][9,9]
Subarray2[10,10][19,19]
Subarray3[20,20][29,29]
Given these subarrays, I would then need to average the subarray values to create a byte[10,10] from the original byte[100,100].
I realize this is not unbelievably difficult, but after spending 4 days debugging very low-level code and now getting stuck on this would appreciate some fresh eyes.
Use this as a reference. I used ints just for ease of use. Code is untested. but the idea is there.
var rowSize = 100;
var colSize = 100;
var arr = new int[rowSize, colSize];
var r = new Random();
for (int i = 0; i < rowSize; i++)
for (int j = 0; j < colSize; j++)
arr[i, j] = r.Next(20);
for (var subcol = 0; subcol < colSize / 10; subcol++)
{
for (var subrow = 0; subrow < colSize/10; subrow++)
{
var startX = subcol*10;
var startY = subrow*10;
var avg = 0;
for (var x=0; x<10; x++)
for (var y = 0; y < 10; y++)
avg += arr[startX + x, startY + y];
avg /= 10*10;
Console.WriteLine(avg);
}
}
It looks like you're new to SO. Next time try to post your attempt at the problem; it's better to fix your code.
The only challenge is figuring out the function, that given the subarray index we're trying to populate, would give you the correct row and column indexes in your original 100x100 array; the rest would just be a matter of copying the values:
// psuedocode
// given a subarrayIndex of 0 to 99, these will calculate the correct indices
rowIndexIn100x100Array = (subarrayIndex / 10) * 10 + subArrayRowIndexToPopulate;
colIndexIn100x100Array = (subarrayIndex % 10) * 10 + subArrayColIndexToPopulate;
I'll leave it as an exercise to you to deduce why the above functions correctly calculate the indices.
With the above, we can easily map the values:
var subArrays = new List<byte[,]>();
for (int subarrayIndex = 0; subarrayIndex < 100; subarrayIndex++)
{
var subarray = new byte[10, 10];
for (int i = 0; i < 10; i++)
for (int j = 0; j < 10; j++)
{
int rowIndexIn100x100Array = (subarrayIndex / 10) * 10 + i;
int colIndexIn100x100Array = (subarrayIndex % 10) * 10 + j;
subarray[i, j] = originalArray[rowIndexIn100x100Array, colIndexIn100x100Array];
}
subArrays.Add(subarray);
}
Once we have the 10x10 arrays, calculating the average would be trivial using LINQ:
var averages = new byte[10, 10];
for (int i = 0; i < 10; i++)
for (int j = 0; j < 10; j++)
{
averages[i, j] = (byte)subArrays[(i * 10) + j].Cast<byte>().Average(b => b);
}
Fiddle.

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;
}

Categories

Resources