MinIndex всегда = i даже после многократного изменения его значения?

Я новичок в JavaScript и программировании (3 месяца) и пытаюсь создать визуализатор алгоритма сортировки, подобный тем, которые вы видите на YouTube. У меня была проблема, когда обычно используемые циклы for были просто способом быстрой визуализации, поэтому я просто заменил циклы for интервалами. это сработало для пузырькового алгоритма, вставки, быстрой и некоторых других алгоритмов сортировки, но я не могу хоть убей заставить выбор работать. по какой-то причине, когда функция swap(index1, index2) вызывается minIndex, и выбор I всегда один и тот же. я сделал старый добрый console.info, и все работает до тех пор, пока не появится функция подкачки. прошло 3 дня страданий и никакого прогресса, пожалуйста, мне нужна помощь или подсказки

function selectionSort2() {
    let selectionI = 0;
    const selectionIloop = setInterval(() => {
        if (selectionI > arr.length) {
            clearInterval(selectionIloop);
        }
        let minIndex = selectionI;
        let selectionJ = selectionI+1;
        const selectionJloop = setInterval(() => { 
            if (selectionJ > arr.length) {
                clearInterval(selectionJloop);
            }
            if (arr[selectionJ] < arr[minIndex]) {
                minIndex = selectionJ;
            }
            selectionJ++;
        }, 100);
        swap(minIndex, selectionI);
        selectionI++;
    }, 100)
}

мне удалось частично заставить его работать, изменив selectJloop на такой цикл for

function selectionSort() {
    let selectionI = 0;
    const selectionIloop = setInterval(() => {
        if (selectionI > arr.length) {
            clearInterval(selectionIloop);
        }
        let minIndex = selectionI;
        for(let j = selectionI+1; j < arr.length; j++) {
            if (arr[j] < arr[minIndex]) {
                minIndex = j;
            }
        } 
        swap(minIndex, selectionI);
        selectionI++;
    }, 100)
}

но поскольку цикл for не имеет задержки 0,1 с, эта версия имеет несправедливое преимущество перед другими алгоритмами, циклы for которых ограничены. Также обратите внимание, что когда я поместил задержку 0,1 с внутри цикла for как setTimeout(), это привело к той же проблеме, что и раньше.

Сначала я подумал, что это может быть время задержки, поскольку интервалы не могут иметь одинаковое время задержки, но даже после того, как они оба были изменены много-много раз, он просто отказывается работать.

и просто укажите здесь мою функцию подкачки:

function swap(index1, index2) {
    temp = arr[index1];
    arr[index1] = arr[index2];
    arr[index2] = temp;
}

Я попытался изменить интервалы времени, чтобы они отличались друг от друга. Вместо этого я попытался заменить интервалы циклами for с таймаутами. я попробовал полностью переписать его по-другому. что должно произойти, так это то, что minIndex должен быть индексом наименьшего числа в arr, индекс которого не меньше i + 1, но вместо этого minIndex всегда становится тем же значением, что и i.

selectionSort2() не может работать, потому что вы используете 2 setInterval() одновременно (setInterval асинхронен)
Mister Jojo 24.08.2024 03:04

Он явно неправильно использует инструменты, потому что его процесс должен быть строго последовательным. Но нет ничего плохого в том, чтобы запланировать несколько заданий с помощью setInterval() «одновременно». Расписания вложенности (вызов setInterval() из блока, начатого другим setInterval()) также допустимы, как четко указано в документации.

Diego Ferruchelli 24.08.2024 05:45
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
Улучшение производительности загрузки с помощью Google Tag Manager и атрибута Defer
В настоящее время производительность загрузки веб-сайта имеет решающее значение не только для удобства пользователей, но и для ранжирования в...
Безумие обратных вызовов в javascript [JS]
Безумие обратных вызовов в javascript [JS]
Здравствуйте! Юный падаван 🚀. Присоединяйся ко мне, чтобы разобраться в одной из самых запутанных концепций, когда вы начинаете изучать мир...
Система управления парковками с использованием HTML, CSS и JavaScript
Система управления парковками с использованием HTML, CSS и JavaScript
Веб-сайт по управлению парковками был создан с использованием HTML, CSS и JavaScript. Это простой сайт, ничего вычурного. Основная цель -...
JavaScript Вопросы с множественным выбором и ответы
JavaScript Вопросы с множественным выбором и ответы
Если вы ищете платформу, которая предоставляет вам бесплатный тест JavaScript MCQ (Multiple Choice Questions With Answers) для оценки ваших знаний,...
2
2
66
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Отредактировано и расширено. Для начала исправим ваше "частично рабочее" selectionSort(). Вам просто нужно остановить один элемент раньше (когда selectionI == arr.length). В этом примере для массива из 6 элементов допустимые индексы варьируются от 0 до 5 (затем остановитесь на selectionI == 6).

function selectionSort() {
    let selectionI = 0;
    const selectionIloop = setInterval(() => {
        if (selectionI >= arr.length) {             // Just changed here!
            console.info (arr);                     // And showed the result
            clearInterval(selectionIloop);
        }
        let minIndex = selectionI;
        for(let j = selectionI+1; j < arr.length; j++) {
            if (arr[j] < arr[minIndex]) {
                minIndex = j;
            }
        } 
        console.info(minIndex, selectionI);
        swap(minIndex, selectionI);
        selectionI++;
    }, 100)
}

function swap(index1, index2) {
    temp = arr[index1];
    arr[index1] = arr[index2];
    arr[index2] = temp;
}

arr = [ 100, 200, 3, 4, 5, 6 ];

selectionSort();

И результат:

2 0
3 1
4 2
5 3
4 4
5 5

[3, 4, 5, 6, 100, 200]

Давайте рассмотрим этот код и посмотрим, как и почему он работает (а также чисто «зацикленный» вариант). Ваша функция selectionSort() просто планирует асинхронное (то есть независимое) выполнение блока каждые 100/1000 секунды. Затем selectionSort() заканчивается. Он не выполняет никакой «реальной» обработки и ничего не ждет.

КАЖДЫЙ из этих (асинхронно выполняемых) блоков будет обрабатывать ОДИН (и только один) набор ячеек, который я называю «строкой». Обработка «строки» сравнивает (и может поменять местами) элемент arr[selectionI] с наименьшим элементом, присутствующим между arr[selectionI+1] и концом массива. Допустим, это «строка» 0 (selectionI==0); цикл, управляемый j, будет ПОСЛЕДОВАТЕЛЬНО сравнивать ячейку 0 с ячейкой 1; ячейка 0 – ячейка 2; ячейка 0 – ячейка 3; и т. д.

Такая последовательность является ключом к успеху всего процесса. Также решающим является необходимость последовательной обработки каждой «строки». Другими словами, вы ДОЛЖНЫ сравнить ячейку 0 с ячейкой 1, прежде чем сравнивать ячейку 0 с ячейкой 2; и вся обработка «строки» 0 ДОЛЖНА завершиться до начала обработки «строки» 1.

Несколько соображений.

  1. JavaScript является однопоточным (кроме случаев использования «Веб-воркеров»), и задания, запланированные setInterval(), не будут перекрываться. То есть, если обработка одной «строки» занимает более 0,1 секунды, обработка следующей «строки» начнется позже запланированного. Обе «строки» будут обработаны последовательно, и ваша программа должна получить правильные результаты. (В данном случае это не имеет значения, поскольку обработка каждой «строки» закончится задолго до запланированного времени обработки следующей «строки», так как массив очень мал и 0,1 секунды — это очень много времени, в компьютере терминов.) (Текст исправлен)

  2. Более технический. Переменная selectionI (которая идентифицирует текущую «строку») является общей для ВСЕХ запланированных заданий (т. е. все задания совместно используют, читают и записывают одну и ту же переменную). Это связано с тем, что он определен на уровне функции и является частью так называемого «замыкания» (контекста данных функции, который сохраняется, даже если выполнение самой функции давно закончилось).

А что происходит с вашим selectionSort2() вариантом? Когда первое асинхронное задание должно обработать «строку» 0, оно просто планирует НОВОЕ асинхронное задание (которое будет просто сравнивать ячейку 0 с ячейкой 1; помните: вы удалили внутренние циклы j, которые присутствовали в selectionSort()). Затем обработка «строки» 0 заканчивается (на данный момент).

Примерно через 0,1 секунды второй запуск задания, начавшего обработку «строки» 0, начнет обрабатывать «строку» 1, планируя еще одно НОВОЕ асинхронное задание (которое будет просто сравнивать ячейку 1 с ячейкой 2).

Почти в то же время второй запуск задания, сравнивающего ячейку 0 с ячейкой 1, будет сравнивать ячейку 0 с ячейкой 2 (переменная selectionJ, второй индекс, является общей для всех экземпляров этого «внутреннего» задания, так же, как selectionI был опубликован, потому что это часть другого закрытия).

На этом этапе столь необходимая последовательность полностью теряется, и становится только хуже. В определенный момент времени для массива из N ячеек (т. е. N «строк») через N * 0,1 секунды у вас будет N независимых заданий, запланированных и выполняемых параллельно (на самом деле не одновременно, но идея понятна). ), каждый из которых обрабатывает только одну пару ячеек (что-то вроде ячейки 0 против ячейки 5 в одном задании; ячейки 1 против ячейки 5 в другом; ячейки 2 против ячейки 5 в другом и т. д.).

Вкратце: ваш selectionSort2() никогда не сработает. Ваш selectionSort() работает, с моей небольшой поправкой. Совет: если вы просто хотите отложить выполнение ряда чисто последовательных шагов, используйте await delay(msecs). И если вы действительно хотите использовать асинхронный подход, основанный на таймерах, убедитесь, что вы выполняете один шаг за раз, планируя запуск следующего в цепочке.

Если вы не остановитесь, когда selectionI == 6, вы в конечном итоге вызовете swap(6,6), который не делает ничего полезного, но присваивает новый пустой элемент по адресу arr[6], увеличивая массив на 1 элемент. В следующий раз, когда вы проверите arr.length, это 7. Затем вы устанавливаете selectionI == 7 и вызываете swap(7,7), который добавляет новый пустой элемент в arr[7]. И это продолжается вечно.

Diego Ferruchelli 24.08.2024 03:04

Нет, вопрос в selectionSort2(), а не в selectionSort()

Mister Jojo 24.08.2024 03:07

Я расширил свой ответ, включив подробное объяснение того, как selectionSort() работает, а затем почему selectionSort2() не работает. Я очень надеюсь, что вам это понравится. Действительно. Действительно.

Diego Ferruchelli 24.08.2024 06:40

Это продолжение моего предыдущего ответа с рабочим вариантом вашей исходной функции selectionSort2(). Я думаю, это довольно очевидно. Он не использует никаких циклов и обрабатывает одно единственное сравнение (arr[J] vs arr[minIndex]) в каждом запланированном «задании». Для массива из 6 элементов будет 5+4+3+2+1 «заданий», выполняемых друг за другом с интервалом 100/1000 секунд между ними.

function selectionSort2bis() {
    // Closure variables (common context)
    let selectionI = 0;
    let selectionJ = selectionI + 1
    let minIndex = selectionI
    
    // Schedule just one job, which will be run once each 100 mseconds.
    const jobHandle = setInterval(() => {
        if (selectionI < arr.length) {         
          if (selectionJ < arr.length) {
              // Look for a new min: compare arr[J] with arr[minIndex]
              console.info ("Comparing arr[" + selectionJ + "] against arr[" + minIndex + "] " + "(" + arr[selectionJ] + " against " + arr[minIndex] + ")");
              if (arr[selectionJ] < arr[minIndex]) {
                  minIndex = selectionJ;
              }
              // Advance J
              selectionJ++;
          }
          else {
              // J is already at the end; swap arr[I] and arr[minIndex]
              if (minIndex != selectionI) {
                  console.info ("- Swapping arr[" + minIndex + "] with arr[" + selectionI + "]" + " (" + arr[minIndex] + " with " + arr[selectionI] + ")");
                  swap(minIndex, selectionI);
              }

              // Advance I; reset J and minIndex
              selectionI++;
              selectionJ = selectionI + 1;
              minIndex = selectionI;
          }
        }
        else {
          // I is already at the end; show the result and cancel the job
          console.info (arr);
          clearInterval(jobHandle);
        }

    }, 100)
}


function swap(index1, index2) {
    temp = arr[index1];
    arr[index1] = arr[index2];
    arr[index2] = temp;
}

arr = [ 100, 200, 3, 4, 5, 6 ];

selectionSort2bis();

И это результат:

Comparing arr[1] against arr[0] (200 against 100)
Comparing arr[2] against arr[0] (3 against 100)
Comparing arr[3] against arr[2] (4 against 3)
Comparing arr[4] against arr[2] (5 against 3)
Comparing arr[5] against arr[2] (6 against 3)
- Swapping arr[2] with arr[0] (3 with 100)

Comparing arr[2] against arr[1] (100 against 200)
Comparing arr[3] against arr[2] (4 against 100)
Comparing arr[4] against arr[3] (5 against 4)
Comparing arr[5] against arr[3] (6 against 4)
- Swapping arr[3] with arr[1] (4 with 200)

Comparing arr[3] against arr[2] (200 against 100)
Comparing arr[4] against arr[2] (5 against 100)
Comparing arr[5] against arr[4] (6 against 5)
- Swapping arr[4] with arr[2] (5 with 100)

Comparing arr[4] against arr[3] (100 against 200)
Comparing arr[5] against arr[4] (6 against 100)
- Swapping arr[5] with arr[3] (6 with 200)

Comparing arr[5] against arr[4] (200 against 100)

[3, 4, 5, 6, 100, 200]

Да, это похоже на один цикл (управляемый извне средой выполнения JS) и ручное перемещение двух индексных переменных, одна из которых подчинена другой.

Diego Ferruchelli 24.08.2024 08:41

Огромное спасибо за этот ответ и предыдущий, которые отлично все объяснили, как я уже сказал, я новичок в js, и, черт возьми, это действительно доказало это. Это также была моя первая публикация о переполнении стека, потому что я беспокоился, что мой код ужасен, и я получу много ненавистных комментариев/ответов, но вы и все остальные были очень полезны в этом вопросе. Большое спасибо

Oliver Beckon 25.08.2024 09:34

Другие вопросы по теме