2016-01-10 3 views
4

У меня есть словарь питона, в котором значения являются списками целых чисел:Выберите произвольный элемент из «неоднородных» словаря питона

key1 -> [1, 2, 3] 
key2 -> [1, 2, 3, ... 17] 
key3 -> [1, 2, 3, 4, 5] 

Я хочу, чтобы выбрать случайный набор (ключ, VAL) Допустима является случайным значение из списка значений (например: key2, 8). Случайный выбор должен быть однородным во всех значениях, так, например, этот метод не является равномерным:

random_key = random.choice(d.keys()) 
random_val = random.choice(d[random_key]) 

, потому что списки не являются одной и той же длиной. Я знаю длину конкатенации списков, п, поэтому мой текущий подход заключается в следующем:

idx = np.random.randint(n) 
c = 0 
found = False 

for k in D: 
    for v in D[k]: 
     if c == idx: 
      found = True 
      do_something_with_val(k, v); 
      break 
     c += 1 
    if found: 
     break 

Мой вопрос: есть ли лучший/быстрый способ сделать это?

+0

вы контролируете установку значений в этих списках? – miraculixx

+0

Знаете ли вы, какая будет максимальная длина списков? Или это не под вашим контролем? – Marek

+0

Максимальная длина списков известна и не изменяется. Однако распределение элементов по спискам меняется. Например: элемент 5 можно удалить из «key2», а новый элемент 1 можно добавить к «key3», но максимальная длина списков всегда будет одинаковой. – Blackecho

ответ

2

Вы можете попробовать (в Python 3-для Python 2, используйте iteritems()): измерение

idx = random.randint(0, n) 
for k, v in D.items(): 
    if idx < len(v): 
     do_something_with_val(k, v[idx]) 
     break 
    else: 
     idx -= len(v) 

Скорость:

def ref(): 
    idx = random.randint(0, n) 
    c = 0 
    found = False 
    for k in D: 
     for v in D[k]: 
      if c == idx: 
       found = True 
       # do_something_with_val(k, v); 
       break 
      c += 1 
     if found: 
      break 


def uut(): 
    idx = random.randint(0, n) 
    for k, v in D.items(): 
     if idx < len(v): 
      # do_something_with_val(k, v[idx]) 
      break 
     else: 
      idx -= len(v) 


if __name__ == '__main__': 
    print(timeit.timeit('ref()', setup="from __main__ import ref", number=1000)) 
    print(timeit.timeit('uut()', setup="from __main__ import uut", number=1000)) 

Результаты:

1.7672173159990052 
0.011254642000494641 

Я проверил распределение с помощью небольшой D как {'key2': [3, 4, 5], 'key1': [0, 1, 2]} и распределение выглядит хорошо для меня:

0,166851 
1,166141 
2,166269 
3,167094 
4,167130 
5,166515 
+0

Я думаю, что он должен быть idx Blackecho

+0

Извините, len (v) sheh

+0

@Frost, я добавил измерение скорости. Вы можете проверить это? – sheh

0

Вы можете создать хелпер «значения» контейнер и использовать его для случайного выбора ...

import random 


d = {1: [1, 2], 
    2: [1, 2, 3, 4]} 


values = [(k, v) for k, l in d.items() for v in l ]  
k,v = random.choice(values) 
print (k, v) 

Этот подход очень быстро, но требует больше памяти ... получайте удовольствие;)

+0

На самом деле у dict есть много обновлений, поэтому мне пришлось бы многократно пересчитывать 'values', делая его медленнее. – Blackecho

+0

@Frost Возможно, подумайте, не использовать ли dict, но «значения», как контейнер, чтобы удовлетворить ваши потребности? –

Смежные вопросы