Я новичок в 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.
Он явно неправильно использует инструменты, потому что его процесс должен быть строго последовательным. Но нет ничего плохого в том, чтобы запланировать несколько заданий с помощью setInterval()
«одновременно». Расписания вложенности (вызов setInterval()
из блока, начатого другим setInterval()
) также допустимы, как четко указано в документации.
Отредактировано и расширено. Для начала исправим ваше "частично рабочее" 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.
Несколько соображений.
JavaScript является однопоточным (кроме случаев использования «Веб-воркеров»), и задания, запланированные setInterval()
, не будут перекрываться. То есть, если обработка одной «строки» занимает более 0,1 секунды, обработка следующей «строки» начнется позже запланированного. Обе «строки» будут обработаны последовательно, и ваша программа должна получить правильные результаты. (В данном случае это не имеет значения, поскольку обработка каждой «строки» закончится задолго до запланированного времени обработки следующей «строки», так как массив очень мал и 0,1 секунды — это очень много времени, в компьютере терминов.) (Текст исправлен)
Более технический. Переменная 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]. И это продолжается вечно.
Нет, вопрос в selectionSort2()
, а не в selectionSort()
Я расширил свой ответ, включив подробное объяснение того, как selectionSort()
работает, а затем почему selectionSort2()
не работает. Я очень надеюсь, что вам это понравится. Действительно. Действительно.
Это продолжение моего предыдущего ответа с рабочим вариантом вашей исходной функции 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) и ручное перемещение двух индексных переменных, одна из которых подчинена другой.
Огромное спасибо за этот ответ и предыдущий, которые отлично все объяснили, как я уже сказал, я новичок в js, и, черт возьми, это действительно доказало это. Это также была моя первая публикация о переполнении стека, потому что я беспокоился, что мой код ужасен, и я получу много ненавистных комментариев/ответов, но вы и все остальные были очень полезны в этом вопросе. Большое спасибо
selectionSort2()
не может работать, потому что вы используете 2setInterval()
одновременно (setInterval асинхронен)