I don't understand: why is my insertion sort implementation beating merge sort every time, for any size of n
?
public List<Int32> InsertionSort(List<Int32> elements, Boolean ascending = true)
{
for (Int32 j = 1; j < elements.Count; j++)
{
Int32 key = elements[j];
Int32 i = j - 1;
while (i >= 0 && (elements[i].CompareTo(key) > 0) == ascending)
elements[i + 1] = elements[i--];
elements[i + 1] = key;
}
return elements;
}
public List<Int32> MergeSort(List<Int32> elements, Boolean ascending = true)
{
Sort(elements, 0, elements.Count - 1);
return elements;
}
private void MergeSort(List<Int32> elements, Int32 startIndex, Int32 count)
{
if(startIndex < count)
{
Int32 half = (startIndex + count).Divide(2, RoundMethod.Floor);
Sort(elements, startIndex, half);
Sort(elements, half + 1, count);
Merge(elements, startIndex, half, count);
}
}
public List<Int32> Merge(List<Int32> elements, Int32 lowerBound, Int32 half, Int32 upperBound)
{
Int32 i = 0;
Int32 j = 0;
Int32 lowerElementsCount = half - lowerBound + 1;
Int32 upperElementsCount = upperBound - half;
List<Int32> left = new List<Int32>();
while (i < lowerElementsCount)
left.Add(elements[lowerBound + i++]);
List<Int32> right = new List<Int32>();
while (j < upperElementsCount)
right.Add(elements[half + j++ + 1]);
left.Add(Int32.MaxValue);
right.Add(Int32.MaxValue);
i = 0;
j = 0;
for (int k = lowerBound; k <= upperBound; k++)
if (left[i] <= right[j])
{
elements[k] = left[i];
i++;
}
else
{
elements[k] = right[j];
j++;
}
return elements;
}
Here are my results:
SORTING 1 ELEMENTS
MERGE-SORT: TIME SPENT: 0ms (1513 ticks)
INSERTION-SORT: TIME SPENT: 0ms (1247 ticks)
SORTING 10 ELEMENTS
MERGE-SORT: TIME SPENT: 1ms (2710 ticks)
INSERTION-SORT: TIME SPENT: 0ms (3 ticks)
SORTING 100 ELEMENTS
MERGE-SORT: TIME SPENT: 0ms (273 ticks)
INSERTION-SORT: TIME SPENT: 0ms (11 ticks)
SORTING 1000 ELEMENTS
MERGE-SORT: TIME SPENT: 1ms (3142 ticks)
INSERTION-SORT: TIME SPENT: 0ms (72 ticks)
SORTING 10000 ELEMENTS
MERGE-SORT: TIME SPENT: 18ms (30491 ticks)
INSERTION-SORT: TIME SPENT: 0ms (882 ticks)
And the code for testing:
static void Main(string[] args)
{
for (int i = 1; i < 100000; i*=10)
{
List<Int32> elements = GetFilledList(i, 0, Int32.MaxValue, false);
Console.WriteLine("SORTING {0} ELEMENTS", elements.Count);
Stopwatch sw = new Stopwatch();
//MERGE SORT
sw.Start();
new MergeSort().Sort(elements);
sw.Stop();
Console.WriteLine("MERGE-SORT: TIME SPENT: {0}ms ({1} ticks)", sw.ElapsedMilliseconds, sw.ElapsedTicks);
//INSERTION SORT
sw.Restart();
new InsertionSort().Sort(elements);
sw.Stop();
Console.WriteLine("INSERTION-SORT: TIME SPENT: {0}ms ({1} ticks)", sw.ElapsedMilliseconds, sw.ElapsedTicks);
Console.WriteLine();
}
Console.ReadKey();
}
In case anyone wondering I got these algorithms from Introduction to Algorithms, Thomas H. Cormen (Author), Charles E. Leiserson (Author), Ronald L. Rivest (Author), Clifford Stein (Author)
EDIT:
static List<Int32> GetFilledList(Int32 quantity, Int32 lowerBound, Int32 upperBound, Boolean mayRepeat = true)
{
List<Int32> numbers = new List<Int32>();
Random r = new Random();
for (int i = 0; i < quantity; i++)
{
Int32 numero = r.Next(lowerBound, upperBound);
while(!mayRepeat && numbers.Contains(numero))
numero = r.Next(lowerBound, upperBound);
numbers.Add(numero);
}
return numbers;
}
An insertion sort should be faster than a merge sort for small inputs; that's how O(N) works.
Algorithms like with good complexities typically have a higher C values, so they don't start actually beating "slower" algorithms until you get large inputs.
While esskar seems to have found the main problem you're facing, keep in mind that in the future you'll likely have to test algorithms with much, much larger inputs to really see the better algorithm shine.
Sorting 10000 elements isn't nearly enough to effectively evaluate an algorithm. Go bigger.
Also, is the input random? Post your implementation of
GetFilledList
AND you need to unsort
elements
before doing insertion sort (or just re-initializeelements
).If you flip the order in which you do the sorts, what happens? I'm guessing that you are doing all the work in mergesort, and then insertion sort is merely sorting an already sorted list, which it's actually very good at (O(n), assuming a sane implementation).
because, after the merge sort, the objects in elements are already sorted. do another
before the