将列表中的值与另一个列表中的特定总和进行比较的最快方法是什么?

时间:2014-03-10 12:23:01

标签: c# algorithm linq list sum

我有两个巨大的创建对象列表。 List<Forecast>包含来自不同资源的所有预测,List<Capacity>具有这些资源的容量。

Forecast还包含布尔值,表示此资源是否超出或低于其所有预测总和的容量。

public class Forecast
{
    public int ResourceId { get; set; }
    public double? ForecastJan { get; set; }
    // and ForecastFeb, ForecastMarch, ForecastApr, ForecastMay, etc.

    public bool IsOverForecastedJan { get; set; }
    // and IsOverForecastedFeb, IsOverForecastedMarch, IsOverForecastedApr, etc.
}

public class Capacity
{
    public int ResourceId { get; set; }
    public double? CapacityJan { get; set; }
    // and CapacityFeb, CapacityMar, CapacityApr, CapacityMay, etc.
}

我必须设置IsOverForecastXXX属性,因此每个月我必须知道每个资源的预测总和是否高于此特定资源的容量总和。

这是我的代码:

foreach (Forecast f in forecastList)
{
    if (capacityList.Where(c => c.Id == f.ResourceId)
                    .Select(c => c.CapacityJan)
                    .First()
        < forecastList.Where(x => x.ResourceId == f.ResourceId)
                      .Sum(x => x.ForecastJan)
    )
    {
        f.IsOverForecastedJan = true;
    }

    //Same for each month...
}

我的代码有效,但当列表太大(数千个元素)时,我的表现非常糟糕。

您如何改进此算法?如何比较每种资源的预测总和与相关的容量?

3 个答案:

答案 0 :(得分:1)

您可以使用FirstFirstOrdefault获取当前资源的容量,然后进行比较。我会使用与Dictionary类似的ToLookup来获取所有资源的所有预测。

ILookup<int, Forecast> forecastMonthGroups = forecastList
    .ToLookup(fc => fc.ResourceId);
foreach (Forecast f in forecastList)
{
    double? janSum = forecastMonthGroups[f.ResourceId].Sum(fc => fc.ForecastJan);
    double? febSum = forecastMonthGroups[f.ResourceId].Sum(fc => fc.ForecastFeb);
    var capacities = capacityList.First(c => c.ResourceId == f.ResourceId);
    bool overJan = capacities.CapacityJan < janSum;
    bool overFeb = capacities.CapacityFeb < febSum;
    // ...
    f.IsOverForecastedJan = overJan;
    f.IsOverForecastedFeb = overFeb;
    // ...
}

似乎每Capacity只有一个ResourceID,然后我会使用Dictionary来存储从ResourceIdCapacity的“路径” ,这将进一步提高性能:

ILookup<int, Forecast> forecastMonthGroups = forecastList
    .ToLookup(fc => fc.ResourceId);
Dictionary<int, Capacity> capacityResources = capacityList
    .ToDictionary(c => c.ResourceId);
foreach (Forecast f in forecastList)
{
    double? janSum = forecastMonthGroups[f.ResourceId].Sum(fc => fc.ForecastJan);
    double? febSum = forecastMonthGroups[f.ResourceId].Sum(fc => fc.ForecastFeb);
    bool overJan = capacityResources[f.ResourceId].CapacityJan < janSum;
    bool overFeb = capacityResources[f.ResourceId].CapacityFeb < febSum;
    // ...
    f.IsOverForecastedJan = overJan;
    f.IsOverForecastedFeb = overFeb;
    // ...
}

答案 1 :(得分:1)

我会尝试在进入循环之前选择每个月的容量和预测,这样每次循环时都不会迭代每个列表。

这样的事情:

 var capicities = capacityList.GroupBy(c => c.ResourceId).ToDictionary(c=>c.First().ResourceId, c=>c.First().CapacityJan);
 var forecasts = forecastList.GroupBy(x => x.ResourceId).ToDictionary(x => x.First().ResourceId, x => x.Sum(f => f.ForecastJan));
 foreach (Forecast f in forecastList)
 {
     if (capicities[f.ResourceId] < forecasts[f.ResourceId])
     {
         f.IsOverForecastedJan = true;
     }

 }

答案 2 :(得分:0)

你可以做很多事情来加快速度。首先,对forecastList进行一次传递并总结每个月的容量预测:

var demandForecasts = new Dictionary<int, double?[]>();

foreach (var forecast in forecastList)
{
    var rid = forecast.ResourceId;
    if (!demandForecasts.ContainsKey(rid))
    {
        demandForecasts[rid] = new double?[12];
    }

    var demandForecast = demandForecasts[rid];

    demandForecast[0] += forecast.ForecastJan;
    // etc
    demandForecast[11] += forecast.ForecastDec;
}

对容量执行相同操作,从而生成capacities字典。然后,在forecastList上再循环一次以设置“过度预测”标志:

foreach (var forecast in forecastList)
{
    var rid = forecast.ResourceId;
    forecast.IsOverForecastedJan = capacities[rid][0] < demandForecast[rid][0];
    // ...
    forecast.IsOverForecastedDec = capacities[rid][11] < demandForecast[rid][11];
}

从本代码中隐含的12倍代码重复中可以明显看出,建模容量等作为每个月的单独属性可能不是最好的处理方式 - 使用某种索引集合可以允许重复消除。