Я был несколько удивлен в этой теме , как много неправильных ответов было опубликовано. Просто ради других, которые придумали решение, аналогичное тому, которое опубликовал ОП, следующий код выглядит правильным:
int[] nums = new int[1000];
for (int i = 0; i < nums.Length; i++)
{
nums[i] = i;
}
Random r = new Random();
Array.Sort<int>(nums, (x, y) => r.Next(-1, 2));
foreach(var num in nums)
{
Console.Write("{0} ", num);
}
Однако код будет выдавать исключение иногда, но не всегда. Это то, что делает отладку забавной :) Если вы запускаете ее достаточно много раз или выполняете процедуру сортировки примерно 50 раз, вы получите сообщение об ошибке:
IComparer (or the IComparable methods it relies upon) did not return zero when Array.Sort called x. CompareTo(x). x: '0' x's type: 'Int32' The IComparer: ''.
Другими словами, быстрая сортировка сравнила некоторое число x
с собой и получила ненулевой результат. Очевидное решение для кода было бы написать:
Array.Sort<int>(nums, (x, y) =>
{
if (x == y) return 0;
else return r.NextDouble() < 0.5 ? 1 : -1;
});
Но даже это не работает, потому что в некоторых случаях .NET сравнивает 3 числа друг с другом, которые возвращают противоречивые результаты, такие как A> B, B> C и C> A (упс!). Независимо от того, используете ли вы Guid, GetHashCode или какой-либо другой случайно сгенерированный ввод, решение, подобное показанному выше, по-прежнему неверно.
С учетом вышесказанного, Фишер-Йейтс является стандартным способом перетасовки массивов, поэтому нет никакой реальной причины использовать IComparer в первую очередь. Fisher-Yates - это O (n), тогда как любая реализация, использующая IComparer, использует быструю сортировку за кулисами, которая имеет временную сложность O (n log n). Просто нет веской причины не использовать хорошо известный эффективный стандартный алгоритм для решения такого рода проблем.
Однако, если вы действительно настаиваете на использовании IComparer и ранда, примените ваши случайные данные перед тем, как вы отсортируете. Для этого требуется проекция данных на другой объект, чтобы вы не потеряли случайные данные:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ConsoleApplication1
{
class Pair<T, U>
{
public T Item1 { get; private set; }
public U Item2 { get; private set; }
public Pair(T item1, U item2)
{
this.Item1 = item1;
this.Item2 = item2;
}
}
class Program
{
static void Main(string[] args)
{
Pair<int, double>[] nums = new Pair<int, double>[1000];
Random r = new Random();
for (int i = 0; i < nums.Length; i++)
{
nums[i] = new Pair<int, double>(i, r.NextDouble());
}
Array.Sort<Pair<int, double>>(nums, (x, y) => x.Item2.CompareTo(y.Item2));
foreach (var item in nums)
{
Console.Write("{0} ", item.Item1);
}
Console.ReadKey(true);
}
}
}
Или получите LINQy со своим плохим я:
Random r = new Random();
var nums = from x in Enumerable.Range(0, 1000)
orderby r.NextDouble()
select x;