web-dev-qa-db-ja.com

リストからランダムな要素をポップする最もPython的な方法は何ですか?

リストに後で要素が含まれないように、1つの要素をランダムにポップしたい未知の長さのリストxがあるとします。これを行うための最もPython的な方法は何ですか?

poprandom.randint、およびlenのかなり不便な組み合わせを使用してそれを行うことができ、より短いまたはより良い解決策を見たいと思います。

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

私が達成しようとしているのは、リストから連続してランダムな要素をポップすることです。 (つまり、1つの要素をランダムにポップして辞書に移動し、別の要素をランダムにポップして別の辞書に移動します...)

私はPython 2.6を使用しており、検索機能で解決策を見つけられなかったことに注意してください。

65
Henrik

あなたがしているように見えることは、そもそもあまりPython的ではありません。リストはすべてのPython私が知っている実装で配列として実装されているので、これはO(n)操作であるため、リストの中央からデータを削除しないでください。

アルゴリズムの一部としてこの機能が本当に必要な場合は、 blist のようなデータ構造をチェックアウトして、中間からの効率的な削除をサポートする必要があります。

純粋なPythonでは、残りの要素にアクセスする必要がない場合にできることは、最初にリストをシャッフルしてから繰り返します。

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

あなたが本当に必要な場合残り(これは少しコード臭いです、私見)、少なくともあなたはからpop()できますリストの最後(これは高速です!):

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

一般に、状態を変更するのではなく、より機能的なスタイルを使用すると、リストを使用してプログラムをよりエレガントに表現できます。

73
Niklas B.

あなたはそれよりはるかに良くなることはありませんが、ここにわずかな改善があります:

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

random.randrange() に関するドキュメント:

random.randrange([start]、stop [、step])
range(start, stop, step)からランダムに選択された要素を返します。これはchoice(range(start, stop, step))と同等ですが、実際には範囲オブジェクトを作成しません。

43
Andrew Clark

ここに別の選択肢があります:なぜリストをシャッフルしないでくださいfirst、それから要素がなくなるまで要素のポップを開始しますか?このような:

import random

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

while x:
    p = x.pop()
    # do your stuff with p
8
Óscar López

リスト要素の残りの順序が重要でない場合、リストからランダムなインデックスでsingle要素を削除するには:

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)

スワップは、リストの中央からの削除時のO(n)動作を回避するために使用されます。

8
jfs

リストからポップしない間、重複することなくリストからX個のランダムアイテムを取得しようとしたときに、Googleでこの質問に遭遇しました。最終的に使用したものは次のとおりです。

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

リスト全体をシャッフルしているがリストのごく一部しか使用していないため、これは少し非効率的かもしれませんが、私は最適化の専門家ではないので間違っている可能性があります。

2
Noah McIlraith

1つの方法は次のとおりです。

x.remove(random.choice(x))
2
Simeon Visser

この答えは @ niklas-b の好意によるものです。

おそらく pypi.python.org/pypi/blistのようなものを使用したいでしょう」

PYPIページ を引用するには:

...漸近的なパフォーマンスと小さなリストで同様のパフォーマンスを持つリストのようなタイプ

Blistは、Pythonリストのドロップイン置換です。大きなリストを変更するときのパフォーマンスが向上します。blistパッケージは、sortedlist、sortedset、weaksortedlist、weaksortedset、sortdictdict、およびbtupleタイプも提供します。

「書き込み時のコピー」データ構造であるため、ランダムアクセス/ランダムランエンドのパフォーマンスが低下すると想定されます。これは、Pythonリスト、)の多くのユースケースの前提に違反するため、注意して使用してください

ただし、主なユースケースがリストで奇妙で不自然なことをすることである場合(@OPまたはmy Python 2.6 FIFO =キューオーバーパスオーバーの問題)、これは法案にうまく適合します。

1

私はこれが古い質問であることを知っていますが、ドキュメントのためだけです:

あなた(同じ質問をグーグルしている人)があなたがしていると思うことをやっている場合、リストからk個のアイテムをランダムに選択します(k <= len(yourlist))が、各アイテムがより多く選択されないことを確認します1回(=置換なしのサンプリング)よりも、@ jf-sebastianが示唆するように random.sample を使用できます。しかし、ユースケースについて詳しく知ることなく、これがあなたに必要なものかどうかはわかりません。

1
Dolf Andringa