输入:
public class MyObject
{
public double Value { get; set; }
public DateTime Date { get; set; }
}
生成测试对象的方法:
public static MyObject[] GetTestObjects()
{
var rnd = new Random();
var date = new DateTime(2021, 1, 1, 0, 0, 0);
var result = new List<MyObject>();
for (int i = 0; i < 50000; i++)
{
//this is to simulate real data having gaps
if (rnd.Next(100) < 25)
{
continue;
}
var myObject = new MyObject()
{
Value = rnd.NextDouble(),
Date = date.AddMinutes(15 * i)
};
result.Add(myObject);
}
return result.ToArray();
}
考虑到这一点,我需要计算每个myObject在过去12个月的最大值。我可以考虑并行计算,但也许有一个优化的解决方案?
抱歉我说得不清楚,这是我现在用来得到我想要的东西的东西:
public MyObject[] BruteForceBackward(MyObject[] testData)
{
return testData.AsParallel().Select(point =>
{
var max = testData.Where(x => x.Date <= point.Date && x.Date >= point.Date.AddYears(-1)).Max(x => x.Value);
return new MyObject() { Date = point.Date, Value = point.Value / max };
}).OrderBy(r => r.Date).ToArray();
}
这是可行的,但它是缓慢的,消耗处理器资源(想象一下,你有100k对象),我相信一定有更好的东西
4条答案
按热度按时间yzxexxkh1#
我有一个类似的项目,我必须计算这样的东西对吨的传感器数据。
现在,您可以在我的Github仓库中找到一个更完善的版本,它应该可以使用(.Net):https://github.com/forReason/Statistics-Helper-Library
一般来说,您希望减少遍历所有数据的循环次数,最好的情况是,您希望只对每个元素执行一次操作。
处理阵列(相当于
BruteForceBackwards
)真实的数据或流数据
注意:如果列表非常大,传递完整数组的方法可能会出现内存问题。在这种情况下:一次传递一个值,从最旧的值传递到最新的值。一次存储一个值。此函数也可用于真实的数据。
测试方法包含在代码中。
试验方法
测试结果
测试是在一台拥有32个内核的机器上进行的,因此从理论上讲,多线程方法应该是有利的,但您将看到;)
| 功能说明|函数时间|时间百分比|
| - -|- -|- -|
| 蛮力后退|5334毫秒|九十九点九|
| 前向流通量|5毫秒|百分之零点零九四|
性能改善系数:~次/千
带有数据验证控制台输出:
由于并行化的原因,在Brutefforce上CPU使用率向后高得多。x1c 0d1x
最坏的情况是长时间的值递减。代码仍然可以进行大量优化,但我想这已经足够了。为了进一步优化,可以在删除/添加
maximumValues
元素时减少列表乱序。kulphzqa2#
这是一个有趣而富有挑战性的问题。我用动态编程的方法提出了一个解决方案首先,构造包含在递归定义的范围上预先计算的局部最大值的树。一旦构造,可以主要使用预先计算的值来有效地计算任意范围的最大值。只有在范围的边缘,计算才下降到元素级别。
它没有julian bechtold的FlowThroughForward方法那么快,但随机访问范围可能是一个优点。
要添加到Main的代码:
试验功能:
支持类:
还有很多改进的空间,比如参数化类型和泛化功能以支持不仅仅是Max(Value),但是框架已经存在了。
vhipe2zx3#
Assuming you meant you need the maximum
Value
for each of the last 12 months fromresult
, then you can use LINQ:Running some timing, I get that putting
AsParallel
afterresult
changes the run time from around 16ms (first run) to around 32ms, so it is actually slower. It is about the same after theWhere
and about 23ms after theGroupBy
(processing the 12 groups in parallel). On my PC at least, there isn't enough data or complex operations for parallelism, but theGroupBy
isn't the most efficient.Using an array and testing each element, I get the results in about 1.2ms:
Note that the results are not chronological; you could offset
monthIndex
by today's month to order the results if desired.A micro-optimization (mostly useful on repeat runnings) is to invert the test and use the null-propagating operator:
This saves about 0.2ms on the first run but up to 0.5ms on subsequent runs.
hmmo2u0o4#
这是一个类似于julian bechtold的解决方案。不同之处在于最大值(以及所有相关变量)被隐藏在主实现之外,在一个单独的类中,其目的仅仅是跟踪过去一年的最大值。算法是相同的,我只是在这里和那里使用了一些Linq表达式。
我们在下面的类中记录最大值:
它可以按如下方式使用:
请参见下面的相对时间-上述解决方案稍快(运行时间超过1000万次),但几乎无法察觉:
Relative timings