別のスレッドでは、バイナリヒープ重み付きランダムサンプルの時間複雑さがO(n * log(m))に等しく、ここでnは選択肢の数であり、mは数選択するノードの数。random.sampleの時間複雑度
Pythonでrandom.sampleとして使用されている重み付けされていないランダムなサンプルの時間的な複雑さが不思議でした。時間の複雑さは単にO(n)かそれとも完全に他の何かですか?
別のスレッドでは、バイナリヒープ重み付きランダムサンプルの時間複雑さがO(n * log(m))に等しく、ここでnは選択肢の数であり、mは数選択するノードの数。random.sampleの時間複雑度
Pythonでrandom.sampleとして使用されている重み付けされていないランダムなサンプルの時間的な複雑さが不思議でした。時間の複雑さは単にO(n)かそれとも完全に他の何かですか?
Pythonソース:random.py
(行267)。
315 selected = set()
316 selected_add = selected.add
317 for i in range(k):
318 j = randbelow(n)
319 while j in selected:
320 j = randbelow(n)
321 selected_add(j)
322 result[i] = population[j]
それは基本的にへのランダムインデックスの「サイコロを振り」:
はここに関連するビットです。すでにselected
というセットに含まれているインデックスを取得した場合は、それがリロールされます。すすぎ、泡立て、そしてくり返しk
回(k
はあなたが求めたサンプルの数です)
要求されたサンプル数のサイズでは、O(n)
と思われます。小さなセットにはいくつかの最適化がありますが、物の肉は上のメインループです。
編集:サンプル数が要求されたとき、k
は、総人口n
の大部分であるため
私は信じてはライン305-313は特殊なケースです。人口全体からランダムな要素をローリングする代わりに(すでに選択した要素と衝突した場合に再ロールバックする)、選択する要素のリストを明示的に維持します。私たちは毎回新しい要素を得ることが保証されていますが、リストを維持しなければならないというトレードオフがあります。
私がこれを間違って解釈している場合は、以下にコメントしてください。
303 result = [None] * k
304 setsize = 21 # size of a small set minus size of an empty list
305 if k > 5:
306 setsize += 4 ** _ceil(_log(k * 3, 4)) # table size for big sets
307 if n <= setsize:
308 # An n-length list is smaller than a k-length set
309 pool = list(population)
310 for i in range(k): # invariant: non-selected at [0,n-i)
311 j = randbelow(n-i)
312 result[i] = pool[j]
313 pool[j] = pool[n-i-1] # move non-selected item into vacancy
314 else:
315 selected = set()
316 selected_add = selected.add
317 for i in range(k):
318 j = randbelow(n)
319 while j in selected:
320 j = randbelow(n)
321 selected_add(j)
322 result[i] = population[j]
323 return result
私はこれがO(n)だとは思いません。 n要素のリストからn個の要素の近くをサンプリングしようとすると、 'selected'がいっぱいになり、新しいインデックスを見つけるためにダイのリロールがますます多くなります。私はその複雑さを正確には分かっていませんが、内側ループの予想時間はO(n)であり、全体が二次的であるということがあります。 –
'while'ループのため、これは' O(k) 'とは言えません。実際、 'k'が' n'に近い場合、これは 'O(n * logn)'を平均して取るでしょう。最悪の場合の性能はもちろん無限です。 – interjay
@larsmans:最悪の場合は、 'n'の母集団から' n'個の要素を選択する場合です。私は、このコードには、「既に残っているアイテムのリスト」の代わりに、「アイテムのプール」を維持する特別なケースがあると考えています(キャッシュミスはありません)。私はそれが何をしようとしているのかは分かりません。 –
[ 'random'モジュールのソースコード(http://hg.python.org/cpython/file/ab500b297900/Lib/random.py)を読み取るために照射されるかもしれません。 'random.sample()'関数は267行目で定義されています。 –