2009-09-05 3 views
4

Я хотел бы узнать, как выбрать взвешенные элементы. Например: я хочу получать вопросы из пула, но если кто-то не может дать правильный ответ на вопрос, это заставляет этот вопрос удвоить свой вес и увеличить вероятность повторного выбора позже.Взвешенный элементный алгоритм

+1

Примерно сколько вопросов вы можете выбрать максимум? Это повлияет на лучший алгоритм. –

+0

Это зависит, но может быть более 1000. – Tarik

ответ

3

Имейте класс, который сохраняет элемент: весовые пары (key = item: value = weight) в хеш-таблице.

Класс должен также содержать переменную total_weight, которая является суммой всех весов в хеш-таблице. Методы класса до add_item, remove_item и для элемента должны содержать обновленный total_weight. Это позволяет избежать пересчета суммы за каждый выбор.

Чтобы выбрать пункт: Используйте случайное число, такое как 1<=random_number<=total_weight. Перейдите по элементу: весовые пары в хеш-таблице, суммируя весы до случайного числа < = эта текущая сумма. Когда это произойдет, ключом пары, в которой вы находитесь, является выбранный элемент.

Это похоже на прокрутку воображаемой матрицы, размер которой является суммой всех весов. Для каждого рулона каждый элемент имеет свой собственный диапазон чисел на матрице, причем размер каждого диапазона равен весу его предмета. Если результат свертки попадает в диапазон предмета, этот элемент является выбранным.

Редактирование для добавления следующего образца кода после запроса в комментарии ниже. Испытано это с Python 2.5.2:

from random import randint # Import randint function from random module. 

class WeightedCollection(object): 
    def __init__(self): 
     self.total_weight = 0 
     self.items = {} # This is a python dictionary == a hash table 
    def add_item(self, item, weight): 
     self.items[item] = weight 
     self.total_weight += weight 
    def remove_item(self, item): 
     self.total_weight -= self.items[item] # Subtracts the weight. 
     del(self.items[item]) 
    def update_weight(self, item, new_weight): 
     self.total_weight += (new_weight - self.items[item]) 
     self.items[item] = new_weight 
    def get_random_item(self): 
     ''' Returns random selection but weighted by item weights. ''' 
     # Result of call below is 1 <= random_number <= self.total_weight... 
     random_number = randint(1, self.total_weight) 
     sum_so_far = 0 
     # For every item and its weight... 
     for item, weight in self.items.iteritems(): 
      sum_so_far += weight 
      if random_number <= sum_so_far: 
       return item 

# Usage demo... 

questions = WeightedCollection() 

questions.add_item('What is your name?', 1) 
questions.add_item('What is your favorite color?', 50) 
questions.add_item('What is the meaning to life?', 100) 

print 'Here is what the dictionary looks like:' 
print questions.items 
print '' 
print "Total weight:", questions.total_weight 
print '' 
print 'Some sample random picks...' 
for i in range(5): 
    print questions.get_random_item() 

А вот выход:

Here is what the dictionary looks like: 
{'What is the meaning to life?': 100, 'What is your name?': 1, 'What is your favorite color?': 50} 

Total weight: 151 

Some sample random picks... 
What is your favorite color? 
What is the meaning to life? 
What is the meaning to life? 
What is your favorite color? 
What is the meaning to life? 
+0

Не могли бы вы дать мне пример кода, написанный на C#, лучший способ для меня понять его, чтобы пройти через образец кода. Благодарю. – Tarik

+1

Я не программист на C#, но я добавил пример кода в Python, который, надеюсь, поможет, поскольку Python довольно легко следовать. Python имеет встроенный файл dict для хэш-таблиц, но в C# вам, вероятно, придется искать что-то вроде класса Hashtable в библиотеке Collections или некоторых подобных. Другие могут, возможно, поговорить об этом, а также о том, где найти функции случайных чисел для C#. – Anon

+0

Большое спасибо. – Tarik

2

Охватывайте массив элементов-кандидатов. Если один элемент имеет вес 2, поместите его в массив дважды, как правило, если у вас есть вес n, введите его там n раз. Затем выберите случайный элемент из массива. Та-дааа.

+2

Простой, но не очень эффективный с точки зрения памяти. Может или не может быть хорошим ответом в зависимости от количества вопросов, о которых мы говорим. –

+5

Это довольно неэффективно - особенно для больших весов. Лучше рассчитать сумму всех весов, выбрать случайное число в этом интервале и выбрать последний элемент, где сумма всех весов до и включая этот элемент не превышает выбранного случайного числа. – ChssPly76

+1

@ ChssPly76: Это не обязательно «лучше», это действительно зависит от чисел, о которых мы говорим. Это действительно компромисс между памятью (решение varzan) и циклами процессора (ваше решение ... поскольку вам нужно итерировать половину списка элементов в среднем, чтобы выбрать правильный). Это может быть ничтожным, но я думаю, что это хорошая, легкая проблема для людей подумать об этом классическом компромиссе между эффективностью памяти и КПД. –

2

Посмотрите на это this (прокрутите вниз для кода).

EDIT для критики :)

кода на эту тему я связывал показываю, как реализовать бинарный подход дерева, который на самом деле работает с весами и не хранит массу элементов в массиве для достижения взвешенная вероятность. Опять же, это довольно неэффективно, когда веса меняются очень часто, так как бинарное дерево нужно воссоздавать каждый раз, когда изменяется вес.

EDIT2:

после знакомства Тодда Оуэна об использовании самобалансирующихся дерев. Дерево, очевидно, не должно быть создано заново при каждом изменении веса. Эта часть просто не включена в реализацию, которую я связал, и ее необходимо добавить, если ваши веса сильно меняются.

+0

-1, ссылка без описания, резюме или любой соответствующей информации о том, что находится за ссылкой – Argalatyr

+0

downvote снят! – Argalatyr

+0

Приятно, это решение, которое одновременно и пространство, и время эффективно. –

2

Мне нравится идея Андре Хоффмана использовать двоичное дерево, в котором каждый листовой узел соответствует вопросу, а каждый промежуточный узел хранит сумму веса своих дочерних узлов. Но он говорит, что дерево нужно воссоздавать каждый раз, когда вес меняется.

Собственно, этого не может быть! Когда вы изменяете вес данного листа, вам нужно только обновить веса этих узлов между ним и корнем дерева. Но ... вам также нужен способ найти узел в дереве, если вы хотите его изменить.

Так что я предлагаю использовать самобалансирующееся двоичное дерево (например, дерево красного дерева, дерево AVL и т. Д.), Которое упорядочивается идентификатором вопроса. Операции над деревом должны поддерживать свойство, что вес любого узла равен сумме весов его детей.

С этой структурой данных вес корневого узла W равен сумме весов всех вопросов. Вы можете получить вопрос либо по ID вопроса, либо случайным весом (от нуля до W). Эта операция, а также вставки, удаления или обновление веса вопроса - все O (log n).

+0

Я ложился спать, зная, что можно использовать алгоритм без повторного создания дерева. Спасибо что подметил это. Хороший вызов –

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