В следующем фрагменте Python-функции намереваются найти k‑й наименьший элемент массива, но она работает неверно для некоторых входов: def kth_smallest(arr, k): pivot = arr[0] left = [x for x in arr if x pivot] if k

12 Окт в 08:16
4 +1
0
Ответы
1

Ниже — разбор проблем в исходном фрагменте, исправленный вариант и оценка сложности.

Исходный код:
def kth_smallestarr,karr, karr,k:
pivot = arr000 left = [x for x in arr if x < pivot]
right = [x for x in arr if x > pivot]
if k <= lenleftleftleft:
return kth_smallestleft,kleft, kleft,k elif k == lenleftleftleft + 1:
return pivot
else:
return kth_smallestright,k−len(left)−1right, k - len(left) - 1right,klen(left)1

Основные логические ошибки и ситуации, приводящие к неправильному поведению или падению

Дубликаты равные pivot теряются. Компрехеншены left и right исключают элементы == pivot кромесамогоpivotкроме самого pivotкромесамогоpivot, поэтому элементы, равные pivot, никуда не попадают. Пример: arr = 2,2,22,2,22,2,2, k = 2 — функция рекурсивно вызовет себя на пустом right и упадёт или зациклится.Неправильная обработка случаев, когда k попадает в диапазон равных pivot еслиихбольшеодногоесли их больше одногоеслиихбольшеодного. Код считает только один pivot, поэтому для k внутри блока равных вернёт неверный ответ.Нет проверки границ k (k < 1 или k > len(arr)) — возможны неверные вызовы или бесконечная рекурсия.Нет явного базового случая для пустого массива — вызов на пустом списке приведёт к arr000 и падению.Выбор pivot = arr000 делает алгоритм чувствительным к порядку входа например,ужеотсортированныймассивдастхудшуюпроизводительностьO(n2)например, уже отсортированный массив даст худшую производительность O(n^2)например,ужеотсортированныймассивдастхудшуюпроизводительностьO(n2). Для среднего времени лучше выбирать случайный pivot или применять медиану из трёх.

Исправленный простой вариант разделениенатричасти—меньше/равно/больше,случайныйpivotразделение на три части — меньше / равно / больше, случайный pivotразделениенатричастименьше/равно/больше,случайныйpivot:
import random

def kth_smallestarr,karr, karr,k:
if not 1 <= k <= lenarrarrarr:
raise IndexError("k is out of bounds")
if lenarrarrarr == 1:
return arr000 pivot = random.choicearrarrarr lows = [x for x in arr if x < pivot]
pivots = xforxinarrifx==pivotx for x in arr if x == pivotxforxinarrifx==pivot highs = [x for x in arr if x > pivot]

if k <= lenlowslowslows:
return kth_smallestlows,klows, klows,k elif k <= lenlowslowslows + lenpivotspivotspivots:
return pivot
else:
return kth_smallesthighs,k−len(lows)−len(pivots)highs, k - len(lows) - len(pivots)highs,klen(lows)len(pivots)

Почему это исправляет ошибки

Все элементы распределяются в три группы: <pivot, ==pivot, >pivot — ничего не теряется.Если k попадает в диапазон группы равных — возвращаем pivot.Проверяем границы k и добавлен базовый случай lenarrarrarr == 1.Случайный pivot даёт ожидаемое линейное среднее время и предотвращает злостные паттерны входа.

Альтернатива для экономии памяти: in-place quickselect Lomuto/Hoa­­reLomuto/Hoa­­reLomuto/Hoa­­re. Она использует обмены в массиве и работает с индексами O(1)дополнительнойпамяти,рекурсивнаяглубина—O(logn)всреднемO(1) дополнительной памяти, рекурсивная глубина — O(log n) в среднемO(1)дополнительнойпамяти,рекурсивнаяглубинаO(logn)всреднем. Вот простой in-place LomutoLomutoLomuto вариант:

import random

def partitiona,lo,hia, lo, hia,lo,hi:
pivot = ahihihi i = lo
for j in rangelo,hilo, hilo,hi:
if ajjj < pivot:
aiii, ajjj = ajjj, aiii i += 1
aiii, ahihihi = ahihihi, aiii return i

def quickselect_inplacea,ka, ka,k:
if not 1 <= k <= lenaaa:
raise IndexError("k is out of bounds")
lo, hi = 0, lenaaa - 1
k_index = k - 1
while lo <= hi:

случайный выбор опорного элемента pivot_index = random.randintlo,hilo, hilo,hi apivotindexpivot_indexpivoti ndex, ahihihi = ahihihi, apivotindexpivot_indexpivoti ndex p = partitiona,lo,hia, lo, hia,lo,hi if p == k_index:
return appp elif p > k_index:
hi = p - 1
else:
lo = p + 1
raise RuntimeError"unreachable""unreachable""unreachable"

Оценка сложности
1) Исправленная версия с созданием новых списков первыйвариантпервый вариантпервыйвариант:

Среднее ожидаемоеожидаемоеожидаемое время: Onnn — при случайном выборе pivot ожидается линейная сложность.Худшее время: On2n^2n2 — если pivot постоянно очень плох например,всегдаэкстремальныйэлементнапример, всегда экстремальный элементнапример,всегдаэкстремальныйэлемент.Доп. память пиковаяпиковаяпиковая: в среднем Onnn присбалансированныхразделенияхсуммарныеразмерысписковпоуровнямрекурсииограниченыO(n)при сбалансированных разделениях суммарные размеры списков по уровням рекурсии ограничены O(n)присбалансированныхразделенияхсуммарныеразмерысписковпоуровнямрекурсииограниченыO(n), но в худшем случае сильнонеудачныеразбиения,рекурсияглубинойO(n)сильно неудачные разбиения, рекурсия глубиной O(n)сильнонеудачныеразбиения,рекурсияглубинойO(n) — On2n^2n2 из‑за накопления копий списков на стеке вызовов.

2) In-place quickselect:

Среднее время: OnnnХудшее время: On2n^2n2 зависитотвыбораpivot;можноуменьшитьвероятностьплохогослучаярандомизациейилимедианойизтрёхзависит от выбора pivot; можно уменьшить вероятность плохого случая рандомизацией или медианой из трёхзависитотвыбораpivot;можноуменьшитьвероятностьплохогослучаярандомизациейилимедианойизтрёхДоп. память: O111 дополнительной памяти изменяетсямассивнаместеизменяется массив на местеизменяетсямассивнаместе плюс стек вызовов — Olognlog nlogn в среднем, Onnn в худшем случае можнопереписатьитеративно,тогдастекO(1)можно переписать итеративно, тогда стек O(1)можнопереписатьитеративно,тогдастекO(1).

Рекомендации

Если важна простота и читабельность — использовать первый исправленный вариант с разделением на три списка и random.choice.Если важна экономия памяти и производительность в худших практических случаях — использовать in-place quickselect с рандомизированным опорным элементом.Всегда проверяйте границы k и обрабатывайте пустые массивы/базовые случаи.
12 Окт в 08:37
Не можешь разобраться в этой теме?
Обратись за помощью к экспертам
Гарантированные бесплатные доработки в течение 1 года
Быстрое выполнение от 2 часов
Проверка работы на плагиат
Поможем написать учебную работу
Прямой эфир