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



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

A 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...
}

我的代码可以工作,但是当列表太大(数千个元素)时,我的性能真的很差。

你知道我该如何改进这个算法吗?如何将每种资源的预测总和与相关的容量进行比较?

您可以使用FirstFirstOrdefault来获取当前资源的容量,然后比较它们。我将使用类似于DictionaryToLookup来获取所有资源的所有预测。

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;
    // ...
}

似乎每个ResourceID只有一个Capacity,然后我将使用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;
    // ...
}

我会尝试在进入循环之前选择出每个月的能力和预测,这样你就不会在每次循环时迭代每个列表。

像这样:

 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;
     }
 }

你可以做很多事情来加速这个过程。首先,单次通过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倍代码重复中可以明显看出,将能力等建模为每个月的单独属性可能不是最好的方法——使用某种索引集合将允许消除重复。

相关内容

  • 没有找到相关文章