Ответ 1
Имитированный многоязычный словарь
multi_key_dict
не разрешал __getitem__()
с несколькими ключами в onces...
(например, d["red", "green"]
)
Мульти-ключ можно моделировать с помощью tuple
или set
. Если порядок не имеет значения, set
кажется лучшим (на самом деле хешируемый frozen set
, так что [ "red", "blue"]
совпадает с a ["blue", "red"]
.
Имитированный словарь MultiVal
Многозначные значения присущи, используя определенные типы данных, это может быть любой элемент хранения, который может быть удобно проиндексирован. Стандартное dict
должно предоставить это.
недетерминизм
Используя распределение вероятности, определяемое правилами и предположениями 1 недетерминированный выбор выполняется с помощью этот рецепт из документов python.
MultiKeyMultiValNonDeterministicDict
Класс
Какое имя. !\О/ -Ницца
Этот класс принимает несколько ключей, которые определяют вероятностное множество правил из нескольких значений. При создании элемента (__setitem__()
) все значения вероятности предварительно вычисляются для всех комбинаций клавиш 1. При доступе к элементу (__getitem__()
) выбирается предварительно вычисленное распределение вероятности, и результат оценивается на основе случайного взвешенного выбора.
Определение
import random
import operator
import bisect
import itertools
# or use itertools.accumulate in python 3
def accumulate(iterable, func=operator.add):
'Return running totals'
# accumulate([1,2,3,4,5]) --> 1 3 6 10 15
# accumulate([1,2,3,4,5], operator.mul) --> 1 2 6 24 120
it = iter(iterable)
try:
total = next(it)
except StopIteration:
return
yield total
for element in it:
total = func(total, element)
yield total
class MultiKeyMultiValNonDeterministicDict(dict):
def key_combinations(self, keys):
"""get all combinations of keys"""
return [frozenset(subset) for L in range(0, len(keys)+1) for subset in itertools.combinations(keys, L)]
def multi_val_rule_prob(self, rules, rule):
"""
assign probabilities for each value,
spreading undefined result probabilities
uniformly over the leftover results not defined by rule.
"""
all_results = set([result for result_probs in rules.values() for result in result_probs])
prob = rules[rule]
leftover_prob = 1.0 - sum([x for x in prob.values()])
leftover_results = len(all_results) - len(prob)
for result in all_results:
if result not in prob:
# spread undefined prob uniformly over leftover results
prob[result] = leftover_prob/leftover_results
return prob
def multi_key_rule_prob(self, key, val):
"""
assign probability distributions for every combination of keys,
using the default for combinations not defined in rule set
"""
combo_probs = {}
for combo in self.key_combinations(key):
if combo in val:
result_probs = self.multi_val_rule_prob(val, combo).items()
else:
result_probs = self.multi_val_rule_prob(val, frozenset([])).items()
combo_probs[combo] = result_probs
return combo_probs
def weighted_random_choice(self, weighted_choices):
"""make choice from weighted distribution"""
choices, weights = zip(*weighted_choices)
cumdist = list(accumulate(weights))
return choices[bisect.bisect(cumdist, random.random() * cumdist[-1])]
def __setitem__(self, key, val):
"""
set item in dictionary,
assigns values to keys with precomputed probability distributions
"""
precompute_val_probs = self.multi_key_rule_prob(key, val)
# use to show ALL precomputed probabilities for key rule set
# print precompute_val_probs
dict.__setitem__(self, frozenset(key), precompute_val_probs)
def __getitem__(self, key):
"""
get item from dictionary,
randomly select value based on rule probability
"""
key = frozenset([key]) if isinstance(key, str) else frozenset(key)
val = None
weighted_val = None
if key in self.keys():
val = dict.__getitem__(self, key)
weighted_val = val[key]
else:
for k in self.keys():
if key.issubset(k):
val = dict.__getitem__(self, k)
weighted_val = val[key]
# used to show probabality for key
# print weighted_val
if weighted_val:
prob_results = self.weighted_random_choice(weighted_val)
else:
prob_results = None
return prob_results
Использование
d = MultiKeyMultiValNonDeterministicDict()
d["red","blue","green"] = {
# {rule_set} : {result: probability}
frozenset(["red", "green"]): {"ballon": 0.8},
frozenset(["blue"]): {"toy": 0.15},
frozenset([]): {"car": 0.05}
}
Тестирование
Проверьте вероятности
N = 10000
red_green_test = {'car':0.0, 'toy':0.0, 'ballon':0.0}
red_blue_test = {'car':0.0, 'toy':0.0, 'ballon':0.0}
blue_test = {'car':0.0, 'toy':0.0, 'ballon':0.0}
red_blue_green_test = {'car':0.0, 'toy':0.0, 'ballon':0.0}
default_test = {'car':0.0, 'toy':0.0, 'ballon':0.0}
for _ in xrange(N):
red_green_test[d["red","green"]] += 1.0
red_blue_test[d["red","blue"]] += 1.0
blue_test[d["blue"]] += 1.0
default_test[d["green"]] += 1.0
red_blue_green_test[d["red","blue","green"]] += 1.0
print 'red,green test =', ' '.join('{0}: {1:05.2f}%'.format(key, 100.0*val/N) for key, val in red_green_test.items())
print 'red,blue test =', ' '.join('{0}: {1:05.2f}%'.format(key, 100.0*val/N) for key, val in red_blue_test.items())
print 'blue test =', ' '.join('{0}: {1:05.2f}%'.format(key, 100.0*val/N) for key, val in blue_test.items())
print 'default test =', ' '.join('{0}: {1:05.2f}%'.format(key, 100.0*val/N) for key, val in default_test.items())
print 'red,blue,green test =', ' '.join('{0}: {1:05.2f}%'.format(key, 100.0*val/N) for key, val in red_blue_green_test.items())
red,green test = car: 09.89% toy: 10.06% ballon: 80.05%
red,blue test = car: 05.30% toy: 47.71% ballon: 46.99%
blue test = car: 41.69% toy: 15.02% ballon: 43.29%
default test = car: 05.03% toy: 47.16% ballon: 47.81%
red,blue,green test = car: 04.85% toy: 49.20% ballon: 45.95%
Правила соответствия вероятностей!
Сноски
-
Предпосылка распределения
Поскольку набор правил не полностью определен, сделаны предположения о распределении вероятностей, большая часть этого делается в
multi_val_rule_prob()
. В принципе, любая вероятность undefined будет распределена равномерно по оставшимся значениям. Это делается для всех комбинаций клавиш и создает обобщенный ключ для случайного взвешенного выбора.Учитывая примерный набор правил
d["red","blue","green"] = { # {rule_set} : {result: probability} frozenset(["red", "green"]): {"ballon": 0.8}, frozenset(["blue"]): {"toy": 0.15}, frozenset([]): {"car": 0.05} }
это создаст следующие дистрибутивы
'red' = [('car', 0.050), ('toy', 0.475), ('ballon', 0.475)] 'green' = [('car', 0.050), ('toy', 0.475), ('ballon', 0.475)] 'blue' = [('car', 0.425), ('toy', 0.150), ('ballon', 0.425)] 'blue,red' = [('car', 0.050), ('toy', 0.475), ('ballon', 0.475)] 'green,red' = [('car', 0.098), ('toy', 0.098), ('ballon', 0.800)] 'blue,green' = [('car', 0.050), ('toy', 0.475), ('ballon', 0.475)] 'blue,green,red'= [('car', 0.050), ('toy', 0.475), ('ballon', 0.475)] default = [('car', 0.050), ('toy', 0.475), ('ballon', 0.475)]
Если это неверно, сообщите нам.