У меня есть отсортированный список, скажем: (на самом деле это не просто числа, это список объектов, которые отсортированы с помощью сложного трудоемкого алгоритма)
mylist = [ 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 ,9 , 10 ]
Есть ли какая-то функция python, которая даст мне N элементов, но сохранит порядок?
Пример:
randomList = getRandom(mylist,4)
# randomList = [ 3 , 6 ,7 , 9 ]
randomList = getRandom(mylist,4)
# randomList = [ 1 , 2 , 4 , 8 ]
и т.д...
python
list
random
sortedlist
Йохай Тиммер
источник
источник
random.sample
а потом сортировать?[0,count)
, сортировка выборки (числа в диапазоне имеют естественный порядок), затем извлечение значенийmylist
на основе индексов. Использованиеzip
может достичь того же эффекта с немного другой механикой.Ответы:
Следующий код сгенерирует случайную выборку размером 4:
import random sample_size = 4 sorted_sample = [ mylist[i] for i in sorted(random.sample(range(len(mylist)), sample_size)) ]
(примечание: с Python 2 лучше использовать
xrange
вместоrange
)Объяснение
random.sample(range(len(mylist)), sample_size)
генерирует случайную выборку индексов исходного списка.
Затем эти индексы сортируются, чтобы сохранить порядок элементов в исходном списке.
Наконец, понимание списка извлекает фактические элементы из исходного списка с учетом выбранных индексов.
источник
Простой код O (N + K * log (K)) способ
Возьмите случайную выборку без замены индексов, отсортируйте индексы и возьмите их из оригинала.
indices = random.sample(range(len(myList)), K) [myList[i] for i in sorted(indices)]
Или более кратко:
[x[1] for x in sorted(random.sample(enumerate(myList),K))]
Оптимизированный O (N) -время, O (1) -пространственный способ
Вы также можете использовать математический трюк и итеративно проходить
myList
слева направо, выбирая числа с динамически изменяющейся вероятностью(N-numbersPicked)/(total-numbersVisited)
. Преимущество этого подхода в том, что этоO(N)
алгоритм, поскольку он не требует сортировки!from __future__ import division def orderedSampleWithoutReplacement(seq, k): if not 0<=k<=len(seq): raise ValueError('Required that 0 <= sample_size <= population_size') numbersPicked = 0 for i,number in enumerate(seq): prob = (k-numbersPicked)/(len(seq)-i) if random.random() < prob: yield number numbersPicked += 1
Подтверждение концепции и проверка верности вероятностей :
Смоделировано с использованием 1 триллиона псевдослучайных выборок в течение 5 часов:
>>> Counter( tuple(orderedSampleWithoutReplacement([0,1,2,3], 2)) for _ in range(10**9) ) Counter({ (0, 3): 166680161, (1, 2): 166672608, (0, 2): 166669915, (2, 3): 166667390, (1, 3): 166660630, (0, 1): 166649296 })
Вероятности отклоняются от истинных вероятностей менее чем в 1.0001 раз. Повторный запуск этого теста привел к другому порядку, что означает, что он не смещен в сторону одного порядка. Выполнение теста с меньшим количеством образцов
[0,1,2,3,4], k=3
и[0,1,2,3,4,5], k=4
дало аналогичные результаты.edit: Не уверен, почему люди голосуют за неправильные комментарии или боятся голосовать за ... НЕТ, в этом методе нет ничего плохого. знак равно
(Также полезное примечание от пользователя tegan в комментариях: если это python2, вы, как обычно, захотите использовать xrange, если вам действительно нужно дополнительное пространство.)
edit : Доказательство: учитывая равномерное распределение (без замены) выбора подмножества
k
из совокупностиseq
размераlen(seq)
, мы можем рассмотреть разделение в произвольной точкеi
на «левый» (0,1, ..., i-1) и 'right' (i, i + 1, ..., len (seq)). Учитывая, что мы выбралиnumbersPicked
из левого известного подмножества, оставшееся должно происходить из того же равномерного распределения в правом неизвестном подмножестве, хотя теперь параметры другие. В частности, вероятностьseq[i]
наличия выбранного элемента равна#remainingToChoose/#remainingToChooseFrom
, или(k-numbersPicked)/(len(seq)-i)
, поэтому мы моделируем это и возвращаемся к результату. (Это должно прекратиться, поскольку если #remainingToChoose == #remainingToChooseFrom, то все оставшиеся вероятности равны 1.) Это похоже на дерево вероятностей, которое случайно создается динамически. По сути, вы можете смоделировать равномерное распределение вероятностей, обусловив предыдущие выборы (по мере роста дерева вероятностей вы выбираете вероятность текущей ветви так, чтобы она была апостериорной, такой же, как предыдущие листья, то есть обусловлена предыдущими выборами; это будет работать, потому что эта вероятность равномерно равна N / k).edit : Тимоти Шилдс упоминает отбор проб коллектора , который является обобщением этого метода, когда
len(seq)
он неизвестен (например, с выражением генератора). В частности, тот, который отмечен как «алгоритм R», занимает O (N) и O (1) пространство, если выполняется на месте; он включает в себя выбор первых N элементов и их медленную замену (также дается намек на индуктивное доказательство). Также на странице википедии можно найти полезные распределенные варианты и различные варианты отбора проб из коллектора.edit : Вот еще один способ закодировать его более семантически очевидным образом.
from __future__ import division import random def orderedSampleWithoutReplacement(seq, sampleSize): totalElems = len(seq) if not 0<=sampleSize<=totalElems: raise ValueError('Required that 0 <= sample_size <= population_size') picksRemaining = sampleSize for elemsSeen,element in enumerate(seq): elemsRemaining = totalElems - elemsSeen prob = picksRemaining/elemsRemaining if random.random() < prob: yield element picksRemaining -= 1 from collections import Counter Counter( tuple(orderedSampleWithoutReplacement([0,1,2,3], 2)) for _ in range(10**5)
)
источник
O(N)
скорее , просто ускорениеO(N log(N))
from __future__ import division
для тех, кто использует Python 2.Может быть, вы можете просто создать образец индексов, а затем собрать элементы из своего списка.
randIndex = random.sample(range(len(mylist)), sample_size) randIndex.sort() rand = [mylist[i] for i in randIndex]
источник
По-видимому,
random.sample
был введен в Python 2.3поэтому для версии ниже мы можем использовать перемешивание (например, для 4 элементов):
myRange = range(0,len(mylist)) shuffle(myRange) coupons = [ bestCoupons[i] for i in sorted(myRange[:4]) ]
источник
random.sample реализовать это.
>>> random.sample([1, 2, 3, 4, 5], 3) # Three samples without replacement [4, 1, 5]
источник