Sander Rossel Ответов: 2

Проблема в алгоритме быстрой сортировки...


Всем привет,
Я весь день боролся с реализацией quicksort в JavaScript. У меня почти получилось, но, кажется, я что-то упускаю...
Я хочу, чтобы алгоритм был стабильным (то есть сохранялся относительный порядок элементов с одним и тем же ключом).
В настоящее время кажется, что все сортируется, за исключением одного элемента. Я искал проблему, но не мог ее найти.
Я почти уверен, что это как-то связано с циклами while (комментируемые whiles-это необработанные реализации quicksort, которые работают, текущие должны сделать его стабильным). Идея состоит в том, что элементы, равные оси, не переключаются, но теперь мы должны проверить, не является ли текущий индекс точкой поворота.

Любая помощь очень ценится, спасибо!
<script>
    function quicksort (items, map, startIndex, endIndex, compare) {
        var low = startIndex;
        var high = endIndex;
        var pindex = Math.floor((low + high) / 2);
        var pivot = map[pindex];

        while (low <= high) {
            while (items[map[low]].value <= items[pivot].value && low < pindex) {
            //while (items[map[low]].value < items[pivot].value) {
                low += 1;
            }
            while (items[map[high]].value >= items[pivot].value && high > pindex) {
            //while (items[map[high]].value > items[pivot].value) {
                high -= 1;
            }
            if (low <= high) {
                var temp = map[low];
                map[low] = map[high];
                map[high] = temp;
                low += 1;
                high -= 1;
            }
        }

        if (low < endIndex) {
            quicksort(items, map, low, endIndex);
        }
        if (high > startIndex) {
            quicksort(items, map, startIndex, high);
        }
    }

    // test above code.
    var size = 7;
    var ints = new Array(size);
    var intMap = new Array(size);
    var i = 0;
    for (i; i < size; i += 1) {
    	ints[i] = {
    		id: i,
    		value: Math.floor((Math.random() * 10) + 1)
    	};
    	intMap[i] = i;
    }
    ints[4].value = ints[size - 1].value;
    quicksort(ints, intMap, 0, size - 1);
    var sorted = intMap.map(function (i) {
    	return ints[i];
    });
    // Sorted should be sorted by value and then id,
    // there is at least one double value.
    // In my tests a single value is unsorted every time.
</script>


Что я уже пробовал:

Отлаживал весь день, просматривал другие реализации на разных сайтах, пробовал разные реализации, задавал этот вопрос здесь, на CP.

2 Ответов

Рейтинг:
2

Patrice T

Цитата:
Отлаживал весь день
Это предложение говорит о том, что вы не знаете, как использовать отладчик.
Отладчик не найдет ошибок по волшебству ! это только позволит вам увидеть, что делает ваш код !
Вы должны воспользоваться этим!

Цитата:
Я хочу, чтобы алгоритм был стабильным (то есть сохранялся относительный порядок элементов с одним и тем же ключом).

Во-первых, если вы посмотрите на документацию Quicksort - Википедия[^], вы увидите, что по определению QuickSort не является стабильным !

Советы:
- Этот:
var i = 0;
for (i; i < size; i += 1) {

следует переписать как:
for (i=0; i < size; i += 1) {

или
for (i=0; i < size; i ++) {

потому что это считается более легким для чтения.
- Вы должны написать функцию обертывания, которая принимает список в качестве параметра и возвращает отсортированный список в качестве результата. Это упрощает повторное использование и тестирование.
- Возьмите лист бумаги, составьте список тестов и спланируйте, что будет делать программа для решения проблемы, затем, по мере выполнения теста, используйте отладчик, чтобы проверить, ведет ли программа себя так, как ожидалось.
Начните с небольших списков тестов:
{1}
{1, 5}
{5, 1}
{1, 4, 9}
{1, 9, 4}
{4, 1, 9}
{9, 4, 1}
...

[обновление]
Почему у вас есть эта линия ?
ints[4].value = ints[size - 1].value;

Для меня это убивает ints[4].

У меня есть сомнения по поводу вашего теста, чтобы pindex на 2-х петлях while.
[обновление]
Попробуйте отсортировать эту последовательность: {0, 2, 4, 6, 1, 3, 5}
Если есть проблема, это подтвердит сомнение, которое я выразил в предыдущем обновлении.
Дайте мне знать результат.


Sander Rossel

Так... Ваш совет - "исправить" цикл while (которого даже нет в моем алгоритме) и продолжать пытаться? На самом деле это не та "помощь", на которую я надеялся...

Я знаю, как работают отладчики (и, к сожалению, Chrome не является моим определением хорошего отладчика), я успешно использовал их в течение многих лет (а иногда, как сейчас, менее успешно).
И я также знаю, что quicksort в своей простейшей форме не является стабильным, поэтому я и пытаюсь получить его таким образом.
Думаю, сегодня вечером я попробую еще раз взглянуть на это дело по-новому.

Patrice T

Где я сказал "цикл while"?

Sander Rossel

для*...

Sander Rossel

Я только что заметил твое обновление.
Я просто устанавливаю значение ints[4] на значение ints[size-1], чтобы убедиться, что у меня есть дублирующее значение (с разными идентификаторами).
Для целей отладки проще иметь фиксированный список, а не Math. random (), но я просто написал пример, чтобы показать, что алгоритм не работает.
Проблема заключается в функции quicksort, а не в коде, который ее тестирует.

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

В настоящее время у меня есть другая, гораздо более медленная реализация, которая стабильна, но не так быстро...

Chris Maunder

Действительно ли необходим комментарий "это предложение говорит о том, что вы не знаете, как использовать отладчик"? Помогает ли это дать ответ? Или это просто для того, чтобы заставить плакат чувствовать себя плохо?

Если вопрос плохой, удалите его или отредактируйте, чтобы сделать его лучше.

Patrice T

Привет Крис,
Цель была не в том, чтобы заставить плакат чувствовать себя плохо.
Моей целью было выявить противоречие между использованием отладчика в течение дня и отсутствием чего-либо в такой простой программе, а также подчеркнуть необходимость правильного изучения отладчика.

Sander Rossel

Назовем это просто... Алгоритмы-не моя сильная сторона...
Почему ты не говоришь по-голландски? Это такой простой язык (сказал голландец).
Просто говорю, что не все такие, как ты. :-)
Я знаю, как использовать отладчик, но не знаю, как работает быстрая сортировка (Ну, теперь я это понял).

Что меня беспокоило больше, так это то, что вы ответили на мой вопрос, но на самом деле никогда не упоминали о быстрой сортировке, о которой шла речь (за исключением того, что она не стабильна, о чем я уже говорил в своем вопросе)...
Хотя эту часть я уже понял, смотрите мой ответ :-)

Рейтинг:
13

Sander Rossel

Нашел!
После нескольких часов попыток я решил оставить проблему в покое на некоторое время, и теперь, через несколько недель, я исправил ее примерно за 5 минут.
Циклы while должны выглядеть следующим образом:

while (items[map[low]].value < items[pivot].value || (items[map[low]].value === items[pivot].value && map[low] < pivot)) {
    low += 1;
}
while (items[map[high]].value > items[pivot].value || (items[map[high]].value === items[pivot].value && map[high] > pivot)) {
    high -= 1;
}
Идея заключается в том, что когда элемент имеет то же значение, что и pivot, он перемещается только тогда, когда исходный индекс (содержащийся в map) ниже или выше pivot соответственно.

Я не могу поверить, что потратил часы на поиски этого...: doh: