program story

목록에서 임의의 요소를 팝하는 가장 비단뱀적인 방법은 무엇입니까?

inputbox 2020. 11. 14. 10:10
반응형

목록에서 임의의 요소를 팝하는 가장 비단뱀적인 방법은 무엇입니까?


x나중에 목록에 요소가 포함되지 않도록 한 요소를 임의로 팝하려는 알 수없는 길이 의 목록이 있다고 가정 해 보겠습니다. 이를 수행하는 가장 비단뱀적인 방법은 무엇입니까?

나는의 다소 손재주가 combincation를 사용하여 작업을 수행 할 수 있습니다 pop, random.randint그리고 len, 짧은 또는 더 좋은 솔루션을보고 싶습니다 :

import random
x = [1,2,3,4,5,6]
x.pop(random.randint(0,len(x)-1))

내가 달성하려는 것은 목록에서 무작위 요소를 연속적으로 팝하는 것입니다. (즉, 한 요소를 무작위로 팝하고 사전으로 이동하고, 다른 요소를 무작위로 팝하고 다른 사전으로 이동합니다. ...)

Python 2.6을 사용하고 있으며 검색 기능을 통해 솔루션을 찾지 못했습니다.


당신이하고있는 것처럼 보이는 것은 처음에는 그다지 파이썬 적이 지 않습니다. 목록은 내가 아는 모든 Python 구현에서 배열로 구현되므로 목록 중간에서 항목을 제거해서는 안됩니다 O(n). 따라서 이것은 작업입니다.

알고리즘의 일부로이 기능이 정말로 필요한 경우 blist중간에서 효율적인 삭제를 지원 하는 데이터 구조를 확인해야 합니다.

순수 Python에서 나머지 요소에 액세스 할 필요가없는 경우 수행 할 수있는 작업은 목록을 먼저 섞은 다음 반복하는 것입니다.

lst = [1,2,3]
random.shuffle(lst)
for x in lst:
  # ...

당신이 경우 정말 필요한 , (코드 냄새, IMHO의 비트가있는) 나머지를 최소 할 수 있습니다 pop()지금 목록의 끝에서 (빨리!)

while lst:
  x = lst.pop()
  # do something with the element      

일반적으로 상태를 변경하는 대신에보다 기능적인 스타일을 사용하면 (목록 에서처럼) 프로그램을 더 우아하게 표현할 수 있습니다.


그보다 훨씬 나아질 수는 없지만 여기에 약간의 개선이 있습니다.

x.pop(random.randrange(len(x)))

에 대한 문서 random.randrange():

random.randrange ([start], stop [, step])
에서 임의로 선택된 요소를 반환합니다 range(start, stop, step). 이것은와 동일 choice(range(start, stop, step))하지만 실제로 범위 객체를 빌드하지는 않습니다.


또 다른 대안이 있습니다. 먼저 목록을 섞은 다음 더 이상 요소가 남아 있지 않을 때까지 목록의 요소를 터뜨리는 것이 어떻습니까? 이렇게 :

import random

x = [1,2,3,4,5,6]
random.shuffle(x)

while x:
    p = x.pop()
    # do your stuff with p

나머지 목록 요소의 순서가 중요하지 않은 경우 목록에서 임의의 인덱스에 있는 단일 요소 를 제거하려면 :

import random

L = [1,2,3,4,5,6]
i = random.randrange(len(L)) # get random index
L[i], L[-1] = L[-1], L[i]    # swap with the last element
x = L.pop()                  # pop last element O(1)

The swap is used to avoid O(n) behavior on deletion from a middle of a list.


One way to do it is:

x.remove(random.choice(x))

While not popping from the list, I encountered this question on Google while trying to get X random items from a list without duplicates. Here's what I eventually used:

items = [1, 2, 3, 4, 5]
items_needed = 2
from random import shuffle
shuffle(items)
for item in items[:items_needed]:
    print(item)

This may be slightly inefficient as you're shuffling an entire list but only using a small portion of it, but I'm not an optimisation expert so I could be wrong.


This answer comes courtesy of @niklas-b:

"You probably want to use something like pypi.python.org/pypi/blist "

To quote the PYPI page:

...a list-like type with better asymptotic performance and similar performance on small lists

The blist is a drop-in replacement for the Python list that provides better performance when modifying large lists. The blist package also provides sortedlist, sortedset, weaksortedlist, weaksortedset, sorteddict, and btuple types.

One would assume lowered performance on the random access/random run end, as it is a "copy on write" data structure. This violates many use case assumptions on Python lists, so use it with care.

HOWEVER, if your main use case is to do something weird and unnatural with a list (as in the forced example given by @OP, or my Python 2.6 FIFO queue-with-pass-over issue), then this will fit the bill nicely.


I know this is an old question, but just for documentation's sake:

If you (the person googling the same question) are doing what I think you are doing, which is selecting k number of items randomly from a list (where k<=len(yourlist)), but making sure each item is never selected more than one time (=sampling without replacement), you could use random.sample like @j-f-sebastian suggests. But without knowing more about the use case, I don't know if this is what you need.

참고URL : https://stackoverflow.com/questions/10048069/what-is-the-most-pythonic-way-to-pop-a-random-element-from-a-list

반응형