更新:私の問題は解決されました。ジェイソンの回答と一致するように、質問のコード ソースを更新しました。rikitikitik の回答は、サンプルからカードを選択して交換する問題を解決していることに注意してください。
加重リストから x 個のランダムな要素を選択したい。サンプリングは交換なしです。私はこの答えを見つけました:https ://stackoverflow.com/a/2149533/57369 Pythonで実装されています。C# で実装してテストしました。しかし、結果 (後述) は、私が期待したものと一致しませんでした。私は Python の知識がないので、コードを C# に移植する際に間違いを犯したと確信していますが、Pythong のコードが本当によく文書化されているため、どこにあるのかわかりません。
私は 1 枚のカードを 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; }
}