Почему использование Random in Sort вызывает [Unable to sort IComparer.Compare error] - PullRequest
6 голосов
/ 09 ноября 2010

Я попытался перетасовать список байтов (List), используя любой код:

myList.Sort((a, b) => this._Rnd.Next(-1, 1));

или

myList.Sort(delegate(byte b1, byte b2)
{
    return this._Rnd.Next(-1, 1);
});

, и они выдавали следующую ошибку:

Unableсортировать, потому что метод IComparer.Compare () возвращает противоречивые результаты.Либо значение не сравнивается равным самому себе, либо одно значение, неоднократно сравниваемое с другим значением, дает разные результаты.x: '{0}', тип x: '{1}', IComparer: '{2}'.

Что плохого в использовании случайного, а не функции сравнения байтов?

Я попытался использовать вместо этого функцию LINQ, и она работает.

var myNewList = myList.OrderBy(s => Guid.NewGuid());
var myNewList = myList.OrderBy(s => this._Rnd.NextDouble());

Я прочитал, что эти методы медленнее, чем перемешивание Фишера-Йейтса, дающее только O (n) время выполнения.Но было просто интересно использовать функцию Sort и random.

Ответы [ 3 ]

11 голосов
/ 09 ноября 2010

Мало того, что отношение сравнения должно быть согласованным , оно также требует наложения общего заказа . Например, нельзя сказать, что «носки меньше обуви, рубашки не меньше и не больше брюк», бла-бла-бла, включите это в алгоритм сортировки и ожидайте получить топологическую сортировку из других. конец. Сорта сравнения называются сортами сравнения , потому что они требуют правильно сформированного отношения сравнения . В частности, быстрая сортировка может выполняться вечно или давать бессмысленные результаты, если отношение сравнения не является последовательным, транзитивным и полностью упорядоченным.

Если то, что вы хотите, - это случайное перемешивание, то используйте случайное перемешивание Фишера-Йейтса. (Сделайте это правильно; хотя алгоритм тривиален, он почти всегда реализован неправильно.) Если то, что вы хотите, является топологической сортировкой, то реализуйте топологическую сортировку. Используйте правильный инструмент для работы.

4 голосов
/ 09 ноября 2010

Потому что, как говорит ошибка, Random не согласован. У вас должен быть компаратор, который всегда возвращает один и тот же результат, если заданы одинаковые параметры. в противном случае сортировка не будет последовательной.

У Кнута есть алгоритм случайной сортировки, который работал как сортировка вставкой, но вы меняли значение случайно выбранным местоположением в существующем массиве.

1 голос
/ 20 марта 2016

Алгоритмы сортировки обычно работают путем определения функции сравнения. Алгоритмы будут повторно сравнивать два элемента в последовательности для сортировки и менять их местами, если их текущий порядок не соответствует желаемому порядку. Различия между алгоритмами главным образом связаны с поиском наиболее эффективного способа, который возможен в данных обстоятельствах для выполнения всех сравнений.

В процессе проведения всех этих сравнений одни и те же два элемента в последовательности необходимо сравнивать более одного раза! Используя нечисловые данные, чтобы сделать это проще, допустим, у вас есть элементы со значениями «Red» и «Apple». Случайный компаратор выбирает «Apple» в качестве большего элемента при первом сравнении. Позже, если случайный компаратор выберет «Красный» в качестве большего элемента, и этот процесс продолжается, вы можете оказаться в ситуации, когда алгоритм никогда не завершает .

В основном вы получаете счастливчиков , и ничего не происходит. Но иногда нет. .Net довольно хорош в том, что он не просто работает вечно и защищает от этого, но он (и должен! ) создает исключение, когда эти охранники обнаруживают непоследовательное упорядочение.

Конечно, правильный способ справиться с этим в общем случае - это перетасовка Кнута-Фишера-Йейтса.

Кроме того, стоит упомянуть, что бывают случаи, когда простой Фишер-Йейтс не подходит. Одним из примеров является необходимость рандомизации последовательности неизвестной длины ... скажем, вы хотите случайным образом переставить данные, полученные из сетевого потока, не зная, сколько данных находится в потоке, и передать эти данные как можно быстрее работнику нить в другом месте.

В этой ситуации вы не можете идеально рандомизировать эти данные. Не зная длины потока, у вас не будет достаточно информации, чтобы правильно выполнить перемешивание, и даже если вы это сделаете, вы обнаружите, что длина настолько велика, что делает невозможным хранение всего этого в ОЗУ или даже на диске. Или вы можете обнаружить, что поток не будет завершен в течение нескольких часов, но ваш рабочий поток должен начать работать намного раньше. В этом случае вы, скорее всего, согласитесь (и понимаете, что это «установление» важно) для алгоритма, который загружает буфер адекватной длины, рандомизирует буфер, подает примерно половину буфера в рабочий поток, а затем повторно -Заполняет пустую часть буфера, чтобы повторить процесс. Даже здесь вы, вероятно, используете Knuth-Fisher-Yates для шага, который рандомизирует буфер.

...