Выберите x случайных элементов из взвешенного списка в C # (без замены)

Updateмоя проблема была решена, я обновил код источника в своем вопросе, чтобы он соответствовал ответу Джейсона. Обратите внимание, что ответом rikitikitik является решение проблемы выбора карт из образца с заменой.

Я хочу выбрать x случайных элементов из взвешенного списка. Выборка без замены. Я нашел этот ответ:https://stackoverflow.com/a/2149533/57369 с реализацией в Python. Я реализовал это в C # и протестировал это. Но результаты (как описано ниже) не соответствовали моим ожиданиям. Я не знаю Python, поэтому я совершенно уверен, что допустил ошибку при переносе кода на C #, но не вижу, где код в Pythong действительно хорошо документирован.

Я выбрал одну карту 10000 раз, и вот результаты, которые я получил (результат одинаков для всех казней):

Card 1: 18.25 % (10.00 % expected)
Card 2: 26.85 % (30.00 % expected)
Card 3: 46.22 % (50.00 % expected)
Card 4: 8.68 % (10.00 % expected)

Как вы можете видеть, карта 1 и карта 4 имеют вес 1, но карта 1 выбирается намного чаще, чем карта 4 (даже если я выбираю 2 или 3 карты).

Тестовые данные:

var cards = new List<Card>
{
    new Card { Id = 1, AttributionRate = 1 }, // 10 %
    new Card { Id = 2, AttributionRate = 3 }, // 30 %
    new Card { Id = 3, AttributionRate = 5 }, // 50 %
    new Card { Id = 4, AttributionRate = 1 }, // 10 %
};

Вот моя реализация в C #

public class CardAttributor : ICardsAttributor
{
    private static Random random = new Random();

    private List<Node> GenerateHeap(List<Card> cards)
    {
        List<Node> nodes = new List<Node>();
        nodes.Add(null);

        foreach (Card card in cards)
        {
            nodes.Add(new Node(card.AttributionRate, card, card.AttributionRate));
        }

        for (int i = nodes.Count - 1; i > 1; i--)
        {
            nodes[i>>1].TotalWeight += nodes[i].TotalWeight;
        }

        return nodes;
    }

    private Card PopFromHeap(List<Node> heap)
    {
        Card card = null;

        int gas = random.Next(heap[1].TotalWeight);
        int i = 1;

        while (gas >= heap[i].Weight)
        {
            gas -= heap[i].Weight;
            i <<= 1;

            if (gas >= heap[i].TotalWeight)
            {
                gas -= heap[i].TotalWeight;
                i += 1;
            }
        }

        int weight = heap[i].Weight;
        card = heap[i].Value;

        heap[i].Weight = 0;

        while (i > 0)
        {
            heap[i].TotalWeight -= weight;
            i >>= 1;
        }

        return card;
    }

    public List<Card> PickMultipleCards(List<Card> cards, int cardsToPickCount)
    {
        List<Card> pickedCards = new List<Card>();

        List<Node> heap = GenerateHeap(cards);

        for (int i = 0; i < cardsToPickCount; i++)
        {
            pickedCards.Add(PopFromHeap(heap));
        }

        return pickedCards;
    }
}

class Node
{
    public int Weight { get; set; }
    public Card Value { get; set; }
    public int TotalWeight { get; set; }

    public Node(int weight, Card value, int totalWeight)
    {
        Weight = weight;
        Value = value;
        TotalWeight = totalWeight;
    }
}

public class Card
{
    public int Id { get; set; }
    public int AttributionRate { get; set; }
}

Ответы на вопрос(4)

Ваш ответ на вопрос