【发布时间】:2012-07-31 08:02:12
【问题描述】:
更新:我的问题已经解决,我更新了问题中的代码源以匹配 Jason 的回答。请注意,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; }
}
【问题讨论】:
-
嗬,我会用 linq、
order by Guid.NewGuid()和双/三/...根据速率的实例数量来做到这一点。更容易实现和更容易阅读——不过没有关于性能的消息。 -
System.Random 不是一个好的随机数生成器(Guids 根本不是随机生成器)。如果您需要真正的随机分布,则必须使用其他东西。别无选择。
-
注意:即使是“完美”的 RNG,两张卡的命中数也不会相同(即使它们具有相同的权重)...
-
System.Random 是一个非常好的用于此目的的随机数生成器。当然它只是一个伪随机数生成器,但在这种情况下这不是问题。
-
@Adriano 你读过我之前的评论吗?使用另一种算法,我能够在选择一张卡片 10 000 次时获得预期的分布。 .NET 的伪随机生成器不是这里的问题。
标签: c# statistics probability