using System.Collections.Generic;
using System.Diagnostics;
using Medallion.Collections;
public static (int String, int Frequency)[] RandomStrings(int length, Random random)
return Enumerable.Range(0, length)
.Select(_ => random.Next(100_000))
.Select(g => (g.Key, g.Count()))
static void Main(string[] args)
RunExperiment("Sort", repetitions: repetitions, length: n, k: k, method: SortEverything, new Random(0));
RunExperiment("ChaosMonkey", repetitions, n, k, ChaosMonkey, new Random(0));
RunExperiment("CountingSort", repetitions, n, k, CountingSort, new Random(0));
RunExperiment("Mo B. Max-Heap", repetitions, n, k, Mo, new Random(0));
RunExperiment("Mo B. Alternative (Quickselect)", repetitions, n, k, QuickSelect, new Random(0));
public static void RunExperiment(string name, int repetitions, int length, int k,
Func<int, int, (int, int)[], List<int>> method, Random random)
Stopwatch sw = new Stopwatch();
for (int i = 0; i < repetitions; i++)
var randomStrings = RandomStrings(length, random);
method(length, k, randomStrings);
Console.WriteLine($"Average time for {name}: {((double)sw.ElapsedMilliseconds)/repetitions} ms");
public static List<int> SortEverything(int length, int k, (int String, int Frequency)[] frequencyMap)
.OrderBy(x => -x.Frequency)
public static List<int> Mo(int length, int k, (int String, int Frequency)[] frequencyMap)
PriorityQueue<(int String, int Frequency)> queue =
new PriorityQueue<(int String, int Frequency)>(frequencyMap, Comparer<(int String, int Frequency)>
.Create((x, y) => y.Frequency.CompareTo(x.Frequency)));
List<int> result = new List<int>(k);
for (int i = 0; i < k; i++)
result.Add(queue.Dequeue().String);
public static List<int> CountingSort(int length, int k, (int String, int Frequency)[] frequencyMap)
var max = frequencyMap.Max(x => x.Frequency);
var count = new List<int>[max + 1];
foreach (var x in frequencyMap)
if (count[x.Frequency] == null)
count[x.Frequency] = new List<int>(x.String);
count[x.Frequency].Add(x.String);
List<int> result = new List<int>();
for (int i = count.Length - 1; i >= 0; i--)
if (count[i] == null) continue;
result.AddRange(count[i].Take(k - result.Count));
if (result.Count == k) break;
public static List<int> ChaosMonkey(int length, int k, (int String, int Frequency)[] frequencyMap)
var queue = new PriorityQueue<(int String, int Frequency)>(Comparer<(int String, int Frequency)>
.Create((x, y) => x.Frequency.CompareTo(y.Frequency)));
foreach (var x in frequencyMap)
public static List<int> QuickSelect(int length, int k, (int String, int Frequency)[] frequencyMap)
QuickSelect(frequencyMap, k);
public static void QuickSelect((int String, int Frequency)[] input, int k)
var endIndex = input.Length - 1;
while (endIndex > startIndex)
pivotIndex = QuickSelectPartition(input, startIndex, endIndex, pivotIndex);
endIndex = pivotIndex - 1;
startIndex = pivotIndex + 1;
pivotIndex = r.Next(startIndex, endIndex);
static int QuickSelectPartition((int String, int Frequency)[] array, int startIndex, int endIndex, int pivotIndex)
int pivotValue = array[pivotIndex].Frequency;
Swap(pivotIndex, endIndex, array);
for (int i = startIndex; i < endIndex; i++)
if (array[i].Frequency.CompareTo(pivotValue) < 0)
Swap(i, startIndex, array);
Swap(endIndex, startIndex, array);
static void Swap(int i, int j, (int String, int Frequency)[] array)