考虑下面代表经纪人的类:
public class Broker { public string Name = string.Empty; public int Weight = 0; public Broker(string n, int w) { this.Name = n; this.Weight = w; } }
我想从阵列中随机选择一个Broker,同时考虑它们的权重.
您如何看待下面的代码?
class Program { private static Random _rnd = new Random(); public static Broker GetBroker(Listbrokers, int totalWeight) { // totalWeight is the sum of all brokers' weight int randomNumber = _rnd.Next(0, totalWeight); Broker selectedBroker = null; foreach (Broker broker in brokers) { if (randomNumber <= broker.Weight) { selectedBroker = broker; break; } randomNumber = randomNumber - broker.Weight; } return selectedBroker; } static void Main(string[] args) { List brokers = new List (); brokers.Add(new Broker("A", 10)); brokers.Add(new Broker("B", 20)); brokers.Add(new Broker("C", 20)); brokers.Add(new Broker("D", 10)); // total the weigth int totalWeight = 0; foreach (Broker broker in brokers) { totalWeight += broker.Weight; } while (true) { Dictionary result = new Dictionary (); Broker selectedBroker = null; for (int i = 0; i < 1000; i++) { selectedBroker = GetBroker(brokers, totalWeight); if (selectedBroker != null) { if (result.ContainsKey(selectedBroker.Name)) { result[selectedBroker.Name] = result[selectedBroker.Name] + 1; } else { result.Add(selectedBroker.Name, 1); } } } Console.WriteLine("A\t\t" + result["A"]); Console.WriteLine("B\t\t" + result["B"]); Console.WriteLine("C\t\t" + result["C"]); Console.WriteLine("D\t\t" + result["D"]); result.Clear(); Console.WriteLine(); Console.ReadLine(); } } }
我不那么自信.当我运行它时,经纪人A总是获得比经纪人D更多的点击量,并且他们具有相同的权重.
有更准确的算法吗?
谢谢!
你的算法几乎是正确的.但是,测试应该是:<
而不是<=
:
if (randomNumber < broker.Weight)
这是因为0是包含在随机数中,totalWeight
而是排他性的.换句话说,权重为0的经纪人仍然很少被选中 - 根本不是你想要的.这说明经纪人A的点击次数比经纪人D多.
除此之外,你的算法很好,实际上是解决这个问题的规范方法.
class Program { static void Main(string[] args) { var books = new List{ new Book{Isbn=1,Name="A",Weight=1}, new Book{Isbn=2,Name="B",Weight=100}, new Book{Isbn=3,Name="C",Weight=1000}, new Book{Isbn=4,Name="D",Weight=10000}, new Book{Isbn=5,Name="E",Weight=100000}}; Book randomlySelectedBook = WeightedRandomization.Choose(books); } } public static class WeightedRandomization { public static T Choose (List list) where T : IWeighted { if (list.Count == 0) { return default(T); } int totalweight = list.Sum(c => c.Weight); Random rand = new Random(); int choice = rand.Next(totalweight); int sum = 0; foreach (var obj in list) { for (int i = sum; i < obj.Weight + sum; i++) { if (i >= choice) { return obj; } } sum += obj.Weight; } return list.First(); } } public interface IWeighted { int Weight { get; set; } } public class Book : IWeighted { public int Isbn { get; set; } public string Name { get; set; } public int Weight { get; set; } }
那些可以用于任何数据类型的更通用的东西怎么样?
using System; using System.Linq; using System.Collections; using System.Collections.Generic; public static class IEnumerableExtensions { public static T RandomElementByWeight(this IEnumerable sequence, Func weightSelector) { float totalWeight = sequence.Sum(weightSelector); // The weight we are after... float itemWeightIndex = new Random().NextDouble() * totalWeight; float currentWeightIndex = 0; foreach(var item in from weightedItem in sequence select new { Value = weightedItem, Weight = weightSelector(weightedItem) }) { currentWeightIndex += item.Weight; // If we've hit or passed the weight we are after for this item then it's the one we want.... if(currentWeightIndex >= itemWeightIndex) return item.Value; } return default(T); } }
只需打电话
Dictionaryfoo = new Dictionary (); foo.Add("Item 25% 1", 0.5f); foo.Add("Item 25% 2", 0.5f); foo.Add("Item 50%", 1f); for(int i = 0; i < 10; i++) Console.WriteLine(this, "Item Chosen {0}", foo.RandomElementByWeight(e => e.Value));