给定字典中的一组N个项及其与之关联的事件。 现在我必须根据每个项目的整体概率为每个项目分配精确的X个插槽,但每个项目至少有1个插槽。
以下是我提出的建议:
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
public static class Program
{
public static void Main( string[] args )
{
var dict = new Dictionary<char,int>();
dict.Add( 'a' , 10 ); dict.Add( 'b' , 0 );
dict.Add( 'c' , 4 ); dict.Add( 'd' , 1 );
dict.Add( 'e' , 9 ); dict.Add( 'f' , 0 );
var distributionMap = Distribute( dict , 40 );
}
public static Dictionary<T,int> Distribute<T>( Dictionary<T,int> occurMap , int slots )
{
var freeSlots = slots - occurMap.Count;
var total = occurMap.Sum( x => x.Value );
var distMap = new Dictionary<T,int>();
foreach( var pair in occurMap )
{
var probability = (double)pair.Value / total;
var assignedSlots = probability * freeSlots;
distMap[ pair.Key ] = (int)( 1 + assignedSlots );
}
Debug.Assert( distMap.Select( x => x.Value ).Sum() == slots );
return distMap;
}
}
然而,断言触发,因为从double
到int
的转换会在某个时刻截断概率。
如何根据计数将所有广告位至少映射一次?
答案 0 :(得分:1)
之前的方法是根据totalcount分配剩余的项目,而根据它们的小数部分分配它们似乎更合理。例如,如果有一个最后一个要分配的槽,则一个0.8的项应该比最后一个槽有45.3(并且之前已经有45个槽)
我会:
示例实现如下所示:
public static Dictionary<T,int> Distribute<T>( Dictionary<T,int> occurMap , int slots )
{
var freeSlots = slots - occurMap.Count;
var totalFreeSlots = freeSlots;
var total = occurMap.Sum( x => x.Value );
var distMap = new Dictionary<T,int>();
var remainingSlots = new Dictionary<T,double>();
foreach( var pair in occurMap )
{
var probability = (double)pair.Value / total;
var assignedSlots = probability * totalFreeSlots;
var integralPart = Math.Truncate(assignedSlots);
var fractionalPart = assignedSlots - integralPart;
distMap[ pair.Key ] = 1 + (int)integralPart;
remainingSlots[pair.Key] = fractionalPart;
freeSlots -= (int)integralPart;
}
foreach (var pair in remainingSlots.ToList().OrderByDescending(x => x.Value))
{
if (freeSlots == 0)
break;
distMap[ pair.Key ]++;
freeSlots -= 1;
}
return distMap;
}
答案 1 :(得分:0)
因为插槽的数量是整数而平均频率不是 - 在初始空闲插槽分配之后,您可能有剩余空闲插槽(如果您将频率降低)或者可能分配了比实际更多的插槽(如果您向上舍入) )。合理的方法是:
示例实施:
public static Dictionary<T, int> Distribute<T>(Dictionary<T, int> occurMap, int slots)
{
if(slots < occurMap.Count)
throw new ArgumentException("Not enough slots");
var freeSlots = slots - occurMap.Count;
var total = occurMap.Sum(x => x.Value);
var distMap = new Dictionary<T, int>();
var keysByProb = new Queue<T>();
foreach (var pair in occurMap.OrderByDescending(c => (double)c.Value / total))
{
var probability = (double)pair.Value / total;
var assignedSlots = probability * freeSlots;
distMap[pair.Key] = 1+ (int)Math.Floor(assignedSlots);
keysByProb.Enqueue(pair.Key);
}
var left = slots - distMap.Select(x => x.Value).Sum();
while (left > 0) {
distMap[keysByProb.Dequeue()]++;
left--;
}
Debug.Assert(distMap.Select(x => x.Value).Sum() == slots);
return distMap;
}