带/不带解算器的C#简单约束加权平均算法 我不明白为什么我不能用微软Salver基金会解决这个看似简单的问题。
我只需要修改某些观察的权重(数字),以确保没有一个观察的权重百分比超过25%。这是为了以后使用该算法的结果计算约束加权平均值 例如,给定5个权重{45,100,33,500,28},我希望这个算法的结果是{45,53,33,53,28},其中2个数字必须减少,以使它们在新总数的25%阈值内(212=45+53+33+53+28)而其他人则保持原样。请注意,尽管最初,第二个100的权重仅为总权重(706)的14%,但由于第四个500的权重减少,它随后推高了其他观察值的%,这是唯一的挑战 我试图重新创建这个使用解算器,只是为了它告诉我,这个解决方案是“不可行的”,它只是返回所有的1更新:解决方案不需要使用解算器,只要在处理适当数量的权重时速度快,任何替代方案都是受欢迎的带/不带解算器的C#简单约束加权平均算法 我不明白为什么我不能用微软Salver基金会解决这个看似简单的问题。,c#,algorithm,solver,weighted-average,ms-solver-foundation,C#,Algorithm,Solver,Weighted Average,Ms Solver Foundation,我只需要修改某些观察的权重(数字),以确保没有一个观察的权重百分比超过25%。这是为了以后使用该算法的结果计算约束加权平均值 例如,给定5个权重{45,100,33,500,28},我希望这个算法的结果是{45,53,33,53,28},其中2个数字必须减少,以使它们在新总数的25%阈值内(212=45+53+33+53+28)而其他人则保持原样。请注意,尽管最初,第二个100的权重仅为总权重(706)的14%,但由于第四个500的权重减少,它随后推高了其他观察值的%,这是唯一的挑战 我试图重新
var solver=SolverContext.GetContext();
var model=solver.CreateModel();
var decisionList=新列表();
decisionList.Add(新决策(Domain.integragerage(1,45),“Dec1”);
decisionList.Add(新决策(Domain.integragerage(1100),“Dec2”);
decisionList.Add(新决策(Domain.integragerage(1,33),“Dec3”);
decisionList.Add(新决策(Domain.integragerage(1500),“Dec4”);
decisionList.Add(新决策(Domain.integragerage(1,28),“Dec5”);
model.AddDecisions(decisionList.ToArray());
int-weightLimit=25;
foreach(模型中的var决策。决策)
{
model.AddConstraint(decision.Name+“weightLimit”,100*(decision/model.Sum(model.Decisions.ToArray())我放弃了解算器b/c它没有达到它的名字imo(或者我没有达到它的标准:)).下面是我登陆的地方。由于此函数被多次使用,并且在大量输入权重列表中使用,因此效率和性能是关键,因此此函数尝试尽可能少地进行迭代(尽管有人建议改进,请告诉我)。结果用于加权平均值,因此我使用“AttributeWeightPair”为了存储值(属性)及其权重,下面的函数在给定这些AWP的列表时修改权重,使其在约束范围内。该函数假设权重限制以a%的形式传入,例如,25%以25的形式传入,而不是0.25的形式传入——好的,我将不再说明代码中显而易见的内容-因此它是:
public static List<AttributeWeightPair<decimal>> WeightLimiter(List<AttributeWeightPair<decimal>> source, decimal weightLimit)
{
weightLimit /= 100; //convert to percentage
var zeroWeights = source.Where(w => w.Weight == 0).ToList();
var nonZeroWeights = source.Where(w => w.Weight > 0).ToList();
if (nonZeroWeights.Count == 0)
return source;
//return equal weights if given infeasible constraint
if ((1m / nonZeroWeights.Count()) > weightLimit)
{
nonZeroWeights.ForEach(w => w.Weight = 1);
return nonZeroWeights.Concat(zeroWeights).ToList();
}
//return original list if weight-limiting is unnecessary
if ((nonZeroWeights.Max(w => w.Weight) / nonZeroWeights.Sum(w => w.Weight)) <= weightLimit)
{
return source;
}
//sort (ascending) and store original weights
nonZeroWeights = nonZeroWeights.OrderBy(w => w.Weight).ToList();
var originalWeights = nonZeroWeights.Select(w => w.Weight).ToList();
//set starting point and determine direction from there
var initialSumWeights = nonZeroWeights.Sum(w => w.Weight);
var initialLimit = weightLimit * initialSumWeights;
var initialSuspects = nonZeroWeights.Where(w => w.Weight > initialLimit).ToList();
var initialTarget = weightLimit * (initialSumWeights - (initialSuspects.Sum(w => w.Weight) - initialLimit * initialSuspects.Count()));
var antepenultimateIndex = Math.Max(nonZeroWeights.FindLastIndex(w => w.Weight <= initialTarget), 1); //needs to be at least 1
for (int i = antepenultimateIndex; i < nonZeroWeights.Count(); i++)
{
nonZeroWeights[i].Weight = originalWeights[antepenultimateIndex - 1]; //set cap equal to the preceding weight
}
bool goingUp = (nonZeroWeights[antepenultimateIndex].Weight / nonZeroWeights.Sum(w => w.Weight)) > weightLimit ? false : true;
//Procedure 1 - find the weight # at which a cap would result in a weight % just UNDER the weight limit
int penultimateIndex = antepenultimateIndex;
bool justUnderTarget = false;
while (!justUnderTarget)
{
for (int i = penultimateIndex; i < nonZeroWeights.Count(); i++)
{
nonZeroWeights[i].Weight = originalWeights[penultimateIndex - 1]; //set cap equal to the preceding weight
}
var currentMaxPcntWeight = nonZeroWeights[penultimateIndex].Weight / nonZeroWeights.Sum(w => w.Weight);
if (currentMaxPcntWeight == weightLimit)
{
return nonZeroWeights.Concat(zeroWeights).ToList();
}
else if (goingUp && currentMaxPcntWeight < weightLimit)
{
nonZeroWeights[penultimateIndex].Weight = originalWeights[penultimateIndex]; //reset
if (penultimateIndex < nonZeroWeights.Count() - 1)
penultimateIndex++; //move up
else break;
}
else if (!goingUp && currentMaxPcntWeight > weightLimit)
{
if (penultimateIndex > 1)
penultimateIndex--; //move down
else break;
}
else
{
justUnderTarget = true;
}
}
if (goingUp) //then need to back up a step
{
penultimateIndex = (penultimateIndex > 1 ? penultimateIndex - 1 : 1);
for (int i = penultimateIndex; i < nonZeroWeights.Count(); i++)
{
nonZeroWeights[i].Weight = originalWeights[penultimateIndex - 1];
}
}
//Procedure 2 - increment the modified weights (subject to a cap equal to their original values) until the weight limit is hit (allowing a very slight overage for the last term in some cases)
int ultimateIndex = penultimateIndex;
var sumWeights = nonZeroWeights.Sum(w => w.Weight); //use this counter instead of summing every time for condition check within loop
bool justOverTarget = false;
while (!justOverTarget)
{
for (int i = ultimateIndex; i < nonZeroWeights.Count(); i++)
{
if (nonZeroWeights[i].Weight + 1 > originalWeights[i])
{
if (ultimateIndex < nonZeroWeights.Count() - 1)
ultimateIndex++;
else justOverTarget = true;
}
else
{
nonZeroWeights[i].Weight++;
sumWeights++;
}
}
if ((nonZeroWeights.Last().Weight / sumWeights) >= weightLimit)
{
justOverTarget = true;
}
}
return nonZeroWeights.Concat(zeroWeights).ToList();
}
public class AttributeWeightPair<T>
{
public T Attribute { get; set; }
public decimal? Weight { get; set; }
public AttributeWeightPair(T attribute, decimal? count)
{
this.Attribute = attribute;
this.Weight = count;
}
}
公共静态列表权重限制器(列表源,十进制权重限制)
{
weightLimit/=100;//转换为百分比
var zerowweights=source.Where(w=>w.Weight==0.ToList();
var non-zerowweights=source.Where(w=>w.Weight>0.ToList();
if(non-zerowweights.Count==0)
返回源;
//如果给定不可行约束,则返回相等的权重
if((1m/non-zerowits.Count())>weightLimit)
{
非零权重。ForEach(w=>w.Weight=1);
返回non-zerowweights.Concat(zerowweights.ToList();
}
//如果不需要重量限制,请返回原始列表
if((nonzerowweights.Max(w=>w.Weight)/nonzerowweights.Sum(w=>w.Weight))w.Weight.ToList();
var originalWeights=non-zerowweights.Select(w=>w.Weight.ToList();
//设置起点并从那里确定方向
var initialSumWeights=非零权重.Sum(w=>w.Weight);
var initialLimit=权重限制*initialSumWeights;
var initialSuspects=non-zerowweights.Where(w=>w.Weight>initialLimit).ToList();
var initialTarget=weightLimit*(initialSumWeights-(initialSumSumWeights.Sum(w=>w.Weight)-initialLimit*initialSumWeights.Count());
var antepenultimateIndex=Math.Max(非零权重.FindLastIndex(w=>w.Weight w.Weight))>weightLimit?false:true;
//程序1-找到重量#在该重量下,上限将导致重量%刚好低于重量限制
int penultimateIndex=前penultimateIndex;
bool justUnderTarget=false;
而(!justUnderTarget)
{
对于(int i=penultimateIndex;iw.Weight);
如果(currentMaxPcntWeight==权重限制)
{
返回non-zerowweights.Concat(zerowweights.ToList();
}
否则如果(goingUp¤tMaxPcntWeight<重量限制)
{
非零权重[penultimateIndex]。权重=原始权重[penultimateIndex];//重置
if(penultimateIndex<非零权重.Count()-1)
penultimateIndex++;//上移
否则就断了;
}
如果(!goingUp&¤tMaxPcntWeight>weightLimit),则为else
{
如果(倒数第二个索引>1)
倒数第二个索引--;//下移
否则就断了;
}
其他的
{
justUnderTarget=正确;
}
}
if(goingUp)//则需要备份一个步骤
{
penultimateIndex=(penultimateIndex>1?penultimateIndex-1:1);
对于(int i=penultimateIndex;i public static List<AttributeWeightPair<decimal>> WeightLimiter(List<AttributeWeightPair<decimal>> source, decimal weightLimit)
{
weightLimit /= 100; //convert to percentage
var zeroWeights = source.Where(w => w.Weight == 0).ToList();
var nonZeroWeights = source.Where(w => w.Weight > 0).ToList();
if (nonZeroWeights.Count == 0)
return source;
//return equal weights if given infeasible constraint
if ((1m / nonZeroWeights.Count()) > weightLimit)
{
nonZeroWeights.ForEach(w => w.Weight = 1);
return nonZeroWeights.Concat(zeroWeights).ToList();
}
//return original list if weight-limiting is unnecessary
if ((nonZeroWeights.Max(w => w.Weight) / nonZeroWeights.Sum(w => w.Weight)) <= weightLimit)
{
return source;
}
//sort (ascending) and store original weights
nonZeroWeights = nonZeroWeights.OrderBy(w => w.Weight).ToList();
var originalWeights = nonZeroWeights.Select(w => w.Weight).ToList();
//set starting point and determine direction from there
var initialSumWeights = nonZeroWeights.Sum(w => w.Weight);
var initialLimit = weightLimit * initialSumWeights;
var initialSuspects = nonZeroWeights.Where(w => w.Weight > initialLimit).ToList();
var initialTarget = weightLimit * (initialSumWeights - (initialSuspects.Sum(w => w.Weight) - initialLimit * initialSuspects.Count()));
var antepenultimateIndex = Math.Max(nonZeroWeights.FindLastIndex(w => w.Weight <= initialTarget), 1); //needs to be at least 1
for (int i = antepenultimateIndex; i < nonZeroWeights.Count(); i++)
{
nonZeroWeights[i].Weight = originalWeights[antepenultimateIndex - 1]; //set cap equal to the preceding weight
}
bool goingUp = (nonZeroWeights[antepenultimateIndex].Weight / nonZeroWeights.Sum(w => w.Weight)) > weightLimit ? false : true;
//Procedure 1 - find the weight # at which a cap would result in a weight % just UNDER the weight limit
int penultimateIndex = antepenultimateIndex;
bool justUnderTarget = false;
while (!justUnderTarget)
{
for (int i = penultimateIndex; i < nonZeroWeights.Count(); i++)
{
nonZeroWeights[i].Weight = originalWeights[penultimateIndex - 1]; //set cap equal to the preceding weight
}
var currentMaxPcntWeight = nonZeroWeights[penultimateIndex].Weight / nonZeroWeights.Sum(w => w.Weight);
if (currentMaxPcntWeight == weightLimit)
{
return nonZeroWeights.Concat(zeroWeights).ToList();
}
else if (goingUp && currentMaxPcntWeight < weightLimit)
{
nonZeroWeights[penultimateIndex].Weight = originalWeights[penultimateIndex]; //reset
if (penultimateIndex < nonZeroWeights.Count() - 1)
penultimateIndex++; //move up
else break;
}
else if (!goingUp && currentMaxPcntWeight > weightLimit)
{
if (penultimateIndex > 1)
penultimateIndex--; //move down
else break;
}
else
{
justUnderTarget = true;
}
}
if (goingUp) //then need to back up a step
{
penultimateIndex = (penultimateIndex > 1 ? penultimateIndex - 1 : 1);
for (int i = penultimateIndex; i < nonZeroWeights.Count(); i++)
{
nonZeroWeights[i].Weight = originalWeights[penultimateIndex - 1];
}
}
//Procedure 2 - increment the modified weights (subject to a cap equal to their original values) until the weight limit is hit (allowing a very slight overage for the last term in some cases)
int ultimateIndex = penultimateIndex;
var sumWeights = nonZeroWeights.Sum(w => w.Weight); //use this counter instead of summing every time for condition check within loop
bool justOverTarget = false;
while (!justOverTarget)
{
for (int i = ultimateIndex; i < nonZeroWeights.Count(); i++)
{
if (nonZeroWeights[i].Weight + 1 > originalWeights[i])
{
if (ultimateIndex < nonZeroWeights.Count() - 1)
ultimateIndex++;
else justOverTarget = true;
}
else
{
nonZeroWeights[i].Weight++;
sumWeights++;
}
}
if ((nonZeroWeights.Last().Weight / sumWeights) >= weightLimit)
{
justOverTarget = true;
}
}
return nonZeroWeights.Concat(zeroWeights).ToList();
}
public class AttributeWeightPair<T>
{
public T Attribute { get; set; }
public decimal? Weight { get; set; }
public AttributeWeightPair(T attribute, decimal? count)
{
this.Attribute = attribute;
this.Weight = count;
}
}