Ответ 1
Модуль heapq в стандартной библиотеке предлагает функцию nlargest() для этого:
top100 = heapq.nlargest(100, iterable [,key])
Он не будет сортировать весь список, поэтому вы не будете тратить время на элементы, которые вам не нужны.
Я бы хотел получить из списка не менее 100000000 наименьших 100 элементов.
Я мог бы отсортировать весь список и просто взять последние 100 элементов из отсортированного списка, но это было бы очень дорого с точки зрения как памяти, так и времени.
Есть ли какой-либо существующий простой, питонический способ сделать это?
То, что я хочу, это следующая функция вместо чистой сортировки. На самом деле я не хочу тратить время на сортировку элементов, которые мне все равно.
Например, это функция, которую я хотел бы иметь:
getSortedElements(100, lambda x,y:cmp(x,y))
Обратите внимание, что это требование предназначено только для перспективы производительности.
Модуль heapq в стандартной библиотеке предлагает функцию nlargest() для этого:
top100 = heapq.nlargest(100, iterable [,key])
Он не будет сортировать весь список, поэтому вы не будете тратить время на элементы, которые вам не нужны.
Алгоритмы выбора должны помочь здесь.
Очень простое решение состоит в том, чтобы найти 100-й самый большой элемент, а затем запустить список, выделяя элементы, которые больше, чем этот элемент. Это даст вам 100 самых больших элементов. Это линейно по длине списка; это возможно.
Есть более сложные алгоритмы. Например, куча, очень подходит для этой проблемы. Алгоритм, основанный на куче, n log k
, где n
- это длина списка, а k
- это число наибольших элементов, которые вы хотите выбрать.
Здесь обсуждается проблема на странице Википедии для алгоритмов выбора.
Изменить: еще один плакат указал, что у Python есть встроенное решение этой проблемы. Очевидно, что это намного проще, чем катиться самостоятельно, но я сохраню это сообщение, если вы хотите узнать, как работают такие алгоритмы.
Вы можете использовать структуру данных кучи. Куча необязательно должна быть заказана, но это довольно быстрый способ сохранить полуупорядоченные данные, и он имеет преимущество самого маленького элемента, всегда являющегося первым элементом в куче.
В куче есть две основные операции, которые помогут вам: Добавить и заменить.
В основном то, что вы делаете, это добавлять к нему элементы, пока вы не доберетесь до 100 предметов (ваше первое число N на ваш вопрос). Затем после этого вы заменяете первый элемент каждым новым элементом, если новый элемент больше, чем первый элемент.
Всякий раз, когда вы заменяете первый элемент чем-то большим, внутренний код в куче настраивает содержимое кучи, так что если новый элемент не самый маленький, он будет пузыриться в кучу, а самый маленький элемент будет "пузыряться" вниз "до первого элемента, готового к замене по пути.
Лучший способ сделать это - поддерживать сортированную очередь приоритетов кучи, которую вы удаляете после того, как в ней будет 100 записей.
Пока вам все равно, будут ли результаты отсортированы, интуитивно очевидно, что вы получите это бесплатно. Чтобы узнать, что у вас есть 100 лучших, вам нужно заказать свой текущий список верхних номеров в порядке, используя некоторую эффективную структуру данных. Эта структура будет знать минимальный, максимальный и относительное положение каждого элемента каким-то естественным образом, чтобы вы могли утверждать его положение рядом с ним соседей.
Как уже упоминалось в python, вы использовали бы heapq. В java PriorityQueue: http://java.sun.com/javase/6/docs/api/java/util/PriorityQueue.html
Вот решение, которое я использовал, которое не зависит от библиотек, и что будет работать на любом языке программирования с массивами:
Инициализация:
Make an array of 100 elements and initialise all elements
with a low value (less than any value in your input list).
Initialise an integer variable to 0 (or any value in
[0;99]), say index_minvalue, that will point to the
current lowest value in the array.
Initialise a variable, say minvalue, to hold the current
lowest value in the array.
Для каждого значения, например current_value, в списке ввода:
if current_value > minvalue
Replace value in array pointed to by index_minvalue
with current_value
Find new lowest value in the array and set index_minvalue to
its array index. (linear search for this will be OK as the array
is quickly filled up with large values)
Set minvalue to current_value
else
<don't do anything!>
minvalue быстро получит высокое значение и, следовательно, большинство значений в списке входных данных нужно будет только сравнить с minvalue (результат сравнения будет в основном ложным).
Для алгоритмов weenies в аудитории: вы можете сделать это с простой вариацией алгоритма Тони Хоаре Найти:
find(topn, a, i, j)
pick a random element x from a[i..j]
partition the subarray a[i..j] (just as in Quicksort)
into subarrays of elements <x, ==x, >x
let k be the position of element x
if k == 0 you're finished
if k > topn, call find(topn, a, i, k)
if k < topn, call find(topn-k, k, j)
Этот алгоритм помещает наибольшие элементы topn
в первые topn
элементы массива a
, не сортируя их. Конечно, если вы хотите, чтобы они отсортировались или просто для простоты, куча лучше, а вызов функции библиотеки еще лучше. Но это классный алгоритм.