Разъяснение по ленивой оценке и ее эффективности

Я наткнулся на следующее предложение на Real World Haskell:

Ленивое вычисление имеет пугающие эффекты. Допустим, мы хотим найти k наименьших значений в несортированном списке. На традиционном языке очевидным подходом было бы отсортировать список и взять первые k элементов, но это дорого. Вместо этого для повышения эффективности мы бы написали специальную функцию, которая принимает эти значения за один проход, и ей придется выполнять умеренно сложный учет. В Haskell подход «сортировка, затем получение» на самом деле работает хорошо: лень гарантирует, что список будет отсортирован только для того, чтобы найти k минимальных элементов.

И они дают для этого реализацию кода:

minima k xs = take k (sort xs)

Но так ли это? Я думаю, что даже в Haskell он должен делать полный список, чтобы удалить k элементы. (Представьте, что в конце списка находится наименьшее число). Я что-то упустил?


person Sibi    schedule 09.10.2013    source источник
comment
Поиск наименьшего элемента эквивалентен сортировке всего списка? Предположим, что выполняется быстрая сортировка (хотя это может быть не фактическая реализация), когда вы разбиваете массив вокруг элемента поворота, а затем рекурсивно сортируете левую и правую половину массива. Если вы запрашиваете только элементы в левой половине, тогда нет необходимости сортировать правую половину.   -  person Satvik    schedule 09.10.2013
comment
@Satvik Поместите этот текст в ответ, приятель!   -  person Daniel Wagner    schedule 09.10.2013
comment
Как обычно, это лучший пример преимуществ компоновки и разделения проблем, чем эффективность ленивой оценки как таковой.   -  person jberryman    schedule 09.10.2013
comment
@Sibi: Никто не сказал, что этой реализации не нужно будет читать все xs, но она не должна завершать работу по сортировке xs.   -  person Louis Wasserman    schedule 09.10.2013
comment
Спасибо всем, теперь я понимаю. @Satvik Если вы можете разместить свой комментарий в качестве ответа, я приму его. :)   -  person Sibi    schedule 09.10.2013
comment
@satvik: Будет ли то же самое работать с сортировкой слиянием?   -  person kqr    schedule 09.10.2013
comment
@kqr Ага, и это хорошо, поскольку sort в GHC - это сортировка слиянием. знак равно   -  person Daniel Wagner    schedule 09.10.2013
comment
Также есть подробный пост Генриха Апфельмуса о ленивой быстрой сортировке apfelmus.nfshost.com/articles/quicksearch. html   -  person MoFu    schedule 10.10.2013


Ответы (1)


(Просто помещаю свой комментарий для ответа здесь) Обход всего списка не эквивалентен его сортировке. Предположим, вы выполняете быструю сортировку, когда вы разбиваете список вокруг оси, а затем рекурсивно сортируете левую и правую половину списка. Если вы запрашиваете только элементы в левой половине, тогда нет необходимости сортировать правую половину. Этот аргумент можно применять рекурсивно. Таким образом, если после сортировки вы берете k элементов из списка длины n, сложность составит O(n + klog k), а не O(n log n). Как указал @MoFu, у Генриха есть хорошая запись в блоге, здесь.

person Satvik    schedule 10.10.2013