이것은 O(n log(n))
트리 를 사용하여 구현할 수 있습니다 .
먼저, 각 노드에서 각 노드의 오른쪽과 왼쪽에있는 모든 하위 노드의 누적 합계를 유지하면서 트리를 만듭니다.
항목을 샘플링하려면 누적 합계를 사용하여 루트 노드에서 재귀 적으로 샘플링하여 현재 노드, 왼쪽에서 노드 또는 오른쪽에서 노드를 반환할지 여부를 결정합니다. 노드를 샘플링 할 때마다 가중치를 0으로 설정하고 부모 노드도 업데이트하십시오.
이것은 파이썬에서 구현 한 것입니다.
import random
def weigthed_shuffle(items, weights):
if len(items) != len(weights):
raise ValueError("Unequal lengths")
n = len(items)
nodes = [None for _ in range(n)]
def left_index(i):
return 2 * i + 1
def right_index(i):
return 2 * i + 2
def total_weight(i=0):
if i >= n:
return 0
this_weigth = weights[i]
if this_weigth <= 0:
raise ValueError("Weigth can't be zero or negative")
left_weigth = total_weight(left_index(i))
right_weigth = total_weight(right_index(i))
nodes[i] = [this_weigth, left_weigth, right_weigth]
return this_weigth + left_weigth + right_weigth
def sample(i=0):
this_w, left_w, right_w = nodes[i]
total = this_w + left_w + right_w
r = total * random.random()
if r < this_w:
nodes[i][0] = 0
return i
elif r < this_w + left_w:
chosen = sample(left_index(i))
nodes[i][1] -= weights[chosen]
return chosen
else:
chosen = sample(right_index(i))
nodes[i][2] -= weights[chosen]
return chosen
total_weight() # build nodes tree
return (items[sample()] for _ in range(n - 1))
용법:
In [2]: items = list(range(10))
...: weights = list(range(10, 0, -1))
...:
In [3]: for _ in range(10):
...: print(list(weigthed_shuffle(items, weights)))
...:
[5, 0, 8, 6, 7, 2, 3, 1, 4]
[1, 2, 5, 7, 3, 6, 9, 0, 4]
[1, 0, 2, 6, 8, 3, 7, 5, 4]
[4, 6, 8, 1, 2, 0, 3, 9, 7]
[3, 5, 1, 0, 4, 7, 2, 6, 8]
[3, 7, 1, 2, 0, 5, 6, 4, 8]
[1, 4, 8, 2, 6, 3, 0, 9, 5]
[3, 5, 0, 4, 2, 6, 1, 8, 9]
[6, 3, 5, 0, 1, 2, 4, 8, 7]
[4, 1, 2, 0, 3, 8, 6, 5, 7]
weigthed_shuffle
생성기이므로 최상위 k
항목을 효율적으로 샘플링 할 수 있습니다 . 전체 배열을 섞으려면 고갈 될 때까지 ( list
함수 사용) 생성기를 반복하십시오 .
최신 정보:
가중 랜덤 샘플링 (2005; Efraimidis, Spirakis)은이를 위한 매우 우아한 알고리즘을 제공합니다. 구현은 매우 간단하며 다음과 같이 실행됩니다 O(n log(n))
.
def weigthed_shuffle(items, weights):
order = sorted(range(len(items)), key=lambda i: -random.random() ** (1.0 / weights[i]))
return [items[i] for i in order]