Выберите случайный элемент из взвешенного списка

Я пытаюсь написать программу для выбора случайного имени из

Ответ 1

"Самый простой" способ справиться с этим - сохранить это в списке.

Затем вы можете просто использовать:

Name GetRandomName(Random random, List<Name> names)
{
    double value = random.NextDouble() * names[names.Count-1].Culmitive;
    return names.Last(name => name.Culmitive <= value);
}

Если скорость вызывает беспокойство, вы можете сохранить отдельный массив только значений Culmitive. При этом вы можете использовать Array.BinarySearch, чтобы быстро найти соответствующий индекс:

Name GetRandomName(Random random, List<Name> names, double[] culmitiveValues)
{
    double value = random.NextDouble() * names[names.Count-1].Culmitive;
    int index = Array.BinarySearch(culmitiveValues, value);
    if (index >= 0)
        index = ~index;

    return names[index];
}

Другим вариантом, который, вероятно, является наиболее эффективным, было бы использовать что-то вроде одной из C5 Generic Collection Library древовидные классы. Затем вы можете использовать RangeFrom для поиска соответствующего имени. Это имеет то преимущество, что не требуется отдельная коллекция

Ответ 2

Я создал библиотеку С# для случайно выбранных взвешенных элементов.

  • Он реализует алгоритмы алгоритма выбора дерева и алгоритма walker alias, чтобы обеспечить максимальную производительность для всех случаев использования.
  • Он тестируется и оптимизирован.
  • Поддержка LINQ.
  • Он бесплатный и с открытым исходным кодом, лицензированный по лицензии MIT.

Пример кода:

IWeightedRandomizer<string> randomizer = new DynamicWeightedRandomizer<string>();
randomizer["Joe"] = 1;
randomizer["Ryan"] = 2;
randomizer["Jason"] = 2;

string name1 = randomizer.RandomWithReplacement();
//name1 has a 20% chance of being "Joe", 40% of "Ryan", 40% of "Jason"

string name2 = randomizer.RandomWithRemoval();
//Same as above, except whichever one was chosen has been removed from the list.

Ответ 3

Я бы сказал, что массив (векторы, если вы предпочитаете) лучше всего их удерживать. Что касается взвешенного среднего значения, найдите сумму, выберите случайное число между нулем и суммой и выберите фамилию, суммарное значение которой меньше. (например, здесь < 1,006 = smith, 1,006-1,816 = johnson и т.д.

P.S. он кумулятивный.

Ответ 4

Просто для удовольствия и никоим образом не оптимальный

List<Name> Names = //Load your structure into this

List<String> NameBank = new List<String>();
foreach(Name name in Names)
   for(int i = 0; i <= (int)(name.Weight*1000); i++)
     NameBank.Add(name.Name)

то

String output = NameBank[rand(NameBank.Count)];