Алгоритм сортировки, где попарное сравнение может возвращать больше информации, чем -1, 0, +1
Большинство алгоритмов сортировки полагаются на парное сравнение, определяет, является ли A < B, A = B или > B.
Я ищу алгоритмы (и для бонусных очков, код в Python), которые используют функцию парного сравнения, которая может отличить намного меньше от немного меньше или намного больше от немного больше. Поэтому, возможно, вместо возвращения {-1, 0, 1} функция сравнения возвращает {-2, -1, 0, 1, 2} или {-5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5} или даже действительное число на интервале (-1, 1).
Для некоторых приложений (например, для сортировки или приближенной сортировки) это позволит определить разумный вид с меньшим количеством сравнений.
Ответы
Ответ 1
Дополнительная информация действительно может быть использована для минимизации общего количества сравнений. Вызовы к функции super_comparison могут использоваться, чтобы сделать вычеты эквивалентными большому количеству вызовов обычной функции сортировки. Например, a much-less-than b
и c little-less-than b
подразумевает a < c < b
.
Вычисления могут быть организованы в ячейки или разделы, которые можно сортировать отдельно. Фактически это эквивалентно QuickSort с n-way-разделом. Здесь реализация в Python:
from collections import defaultdict
from random import choice
def quicksort(seq, compare):
'Stable in-place sort using a 3-or-more-way comparison function'
# Make an n-way partition on a random pivot value
segments = defaultdict(list)
pivot = choice(seq)
for x in seq:
ranking = 0 if x is pivot else compare(x, pivot)
segments[ranking].append(x)
seq.clear()
# Recursively sort each segment and store it in the sequence
for ranking, segment in sorted(segments.items()):
if ranking and len(segment) > 1:
quicksort(segment, compare)
seq += segment
if __name__ == '__main__':
from random import randrange
from math import log10
def super_compare(a, b):
'Compare with extra logarithmic near/far information'
c = -1 if a < b else 1 if a > b else 0
return c * (int(log10(max(abs(a - b), 1.0))) + 1)
n = 10000
data = [randrange(4*n) for i in range(n)]
goal = sorted(data)
quicksort(data, super_compare)
print(data == goal)
Используя этот код с модулем трассировки, можно измерить прирост производительности. В приведенном выше коде регулярное трехстороннее сравнение использует 133 000 сравнений, в то время как супер-функция сравнения уменьшает количество вызовов до 85 000.
Этот код также позволяет легко экспериментировать с различными функциями сравнения. Это покажет, что наивные функции сравнения n-way делают очень мало, чтобы помочь сортировке. Например, если функция сравнения возвращает +/- 2 для различий, превышающих четыре и +/- 1, для различий четыре или менее, есть лишь небольшое уменьшение количества сравнений на 5%. Основная причина заключается в том, что в графе конечных разделов, используемых в начале, есть только несколько "близких совпадений", а все остальное попадает в "дальние соответствия".
Улучшение супер-сравнения заключается в том, чтобы покрывать логарифмические диапазоны (т.е. +/- 1, если в течение десяти, +/- 2, если в пределах сотни, +/-, если в пределах тысячи.
Идеальная функция сравнения будет адаптивной. Для любого заданного размера последовательности функция сравнения должна стремиться подразделить последовательность на разделы примерно равного размера. Теория информации говорит нам, что это позволит максимально увеличить количество бит информации для сравнения.
Адаптивный подход также делает хороший интуитивный смысл. Люди сначала должны быть разделены на любовь, например, перед тем, как делать более изощренные различия, такие как любовь-многое-многое против любви. Дальнейшие проходы на разделение должны производить более тонкие и тонкие различия.
Ответ 2
Вы можете использовать модифицированную быструю сортировку. Позвольте мне объяснить пример, когда функция сравнения возвращает [-2, -1, 0, 1, 2]. Скажем, у вас есть массив A для сортировки.
Создайте 5 пустых массивов - Aminus2, Aminus1, A0, Aplus1, Aplus2.
Выберите произвольный элемент из A, X.
Для каждого элемента массива сравните его с X.
В зависимости от результата поместите элемент в один из массивов Aminus2, Aminus1, A0, Aplus1, Aplus2.
Примените тот же вид рекурсивно к Aminus2, Aminus1, Aplus1, Aplus2 (обратите внимание: вам не нужно сортировать A0, так как все его элементы равны X).
Объедините массивы, чтобы получить окончательный результат: A = Aminus2 + Aminus1 + A0 + Aplus1 + Aplus2.
Ответ 3
Похоже, что использование измененной quicksort с использованием raindog позволит вам быстрее разорвать результаты и, возможно, перейти на них быстрее.
Возможно, эти функции уже доступны из тщательно контролируемой операции qsort? Я об этом не думал.
Это также похоже на сортировку radix, но вместо того, чтобы смотреть на каждую цифру (или на другое правило ведро), вы составляете ведра из богатых сравнений. Мне тяжело думать о случае, когда доступны богатые сравнения, но цифры (или что-то вроде них) не являются.
Ответ 4
Я не могу придумать никакой ситуации, в которой это было бы действительно полезно. Даже если бы я мог, я подозреваю, что добавленные циклы ЦП, необходимые для сортировки нечетких значений, будут больше, чем те дополнительные сравнения, на которые вы ссылаетесь. Но я все равно предложу предложение.
Рассмотрим эту возможность (все строки используют 27 символов a-z и _):
11111111112
12345678901234567890
1/ now_is_the_time
2/ now_is_never
3/ now_we_have_to_go
4/ aaa
5/ ___
Очевидно, что строки 1 и 2 более похожи друг на друга 1 и 3 и намного больше, чем 1 и 4.
Один из подходов - масштабирование значения разности для каждой идентичной позиции символа и использование первого другого символа для установки последней позиции.
Отложив в сторону знаки на данный момент, сравнивая строку 1 с 2, они отличаются в позиции 8 на 'n' - 't'. Это различие 6. Чтобы превратить это в одну цифру 1-9, мы используем формулу:
digit = ceiling(9 * abs(diff) / 27)
так как максимальная разница равна 26. Минимальная разница 1 становится цифрой 1. Максимальная разница 26 становится цифрой 9. Наша разница 6 становится равной 3.
И поскольку разница в позиции 8, функция сравнения вернет 3x10 -8 (на самом деле она вернет отрицательный результат, поскольку строка 1 появляется после строки 2.
Используя аналогичный процесс для строк 1 и 4, функция сравнения возвращает -5x10 -1. Наивысший возможный возврат (строки 4 и 5) имеет разницу в позиции 1 '-' - 'a' (26), которая генерирует цифру 9 и, следовательно, дает нам 9x10 -1.
Возьмите эти предложения и используйте их по своему усмотрению. Мне было бы интересно узнать, как закончится разработка вашего нечеткого кода сравнения.
Ответ 5
Учитывая, что вы хотите заказать ряд предметов на основе сравнения людей, вы можете подойти к этой проблеме, например, к спортивному турниру. Вы можете позволить каждому человеческому голосу увеличить баллы победителя на 3 и уменьшить ослабление на 3, +2 и -2, +1 и -1 или только 0 0 для ничьей.
Затем вы просто выполняете обычную сортировку на основе результатов.
Другой альтернативой может быть структура турнира с одним или двумя исключениями.
Ответ 6
Для достижения этой цели вы можете использовать два сравнения. Умножьте более важное сравнение на 2 и добавьте их вместе.
Вот пример того, что я имею в виду в Perl.
Он сравнивает две ссылки массива на первый элемент, затем на второй элемент.
use strict;
use warnings;
use 5.010;
my @array = (
[a => 2],
[b => 1],
[a => 1],
[c => 0]
);
say "$_->[0] => $_->[1]" for sort {
($a->[0] cmp $b->[0]) * 2 +
($a->[1] <=> $b->[1]);
} @array;
a => 1
a => 2
b => 1
c => 0
Вы можете легко распространить это на любое количество сравнений.
Ответ 7
Возможно, есть веская причина для этого, но я не думаю, что это превосходит альтернативы для любой конкретной ситуации и, конечно же, не подходит для общих случаев. Причина? Если вы не знаете что-то о домене входных данных и о распределении значений, вы не можете действительно улучшить, скажем, quicksort. И если вы знаете эти вещи, часто бывают способы, которые будут намного эффективнее.
Анти-пример: предположим, что ваше сравнение возвращает значение "огромная разница" для чисел, отличающихся более чем на 1000, и что входные данные {0, 10000, 20000, 30000,...}
Анти-пример: тот же, что и выше, но с вводом {0, 10000, 10001, 10002, 20000, 20001,...}
Но, вы говорите, я знаю, что мои входы не выглядят так! Ну, в таком случае расскажите нам, что ваши входы действительно выглядят, подробно. Тогда кто-то может действительно помочь.
Например, когда мне нужно было отсортировать исторические данные. Данные были отсортированы. Когда новые данные были добавлены, он был добавлен, затем список снова запущен. У меня не было информации о том, где были добавлены новые данные. Я разработал гибридную сортировку для этой ситуации, которая легко изнашивала qsort и другие, выбирая сортировку, которая была быстрой на уже отсортированных данных и быстро настраивала ее (по сути, переключаясь на qsort), когда она обнаруживала несортированные данные.
Единственный способ, которым вы собираетесь улучшить общий вид, - это знать свои данные. И если вы хотите получить ответы, вам придётся сообщить об этом здесь очень хорошо.