将双打的阵列汇总为较大的价值跨度:正确的算法
summing array of doubles with large value span : proper algorithm
我有一个算法,我需要总和(很多时间)double数字在e-40到e 40。
数组示例(从实际应用程序随机倾倒):
-2.06991e-05
7.58132e-06
-3.91367e-06
7.38921e-07
-5.33143e-09
-4.13195e-11
4.01724e-14
6.03221e-17
-4.4202e-20
6.58873
-1.22257
-0.0606178
0.00036508
2.67599e-07
0
-627.061
-59.048
5.92985
0.0885884
0.000276455
-2.02579e-07
不用说,我知道这将导致的舍入效果,我试图控制它:最终结果不应在双重的部分中没有任何丢失的信息应该至少是n位准确的(定义为n个)。最终结果需要5位数字和指数。
经过一些体面的思考,我最终遵循算法:
- 对数组进行排序,以使最大的绝对值首先出现,最接近零。
- 将所有内容添加到循环中
的想法是,在这种情况下,任何取消大价值(负和正值)都不会影响后者较小的值。简而言之:
- (10E40-10E40) 1 = 1:结果是预期
- (1 10E -40)-10E40 = 0:不好
我最终使用了std :: multiSet(与普通双打相比,PC上的基准的速度高20%,而我可以使用std:Fabs的自定义排序功能。
它仍然很慢(完成整个事情需要5秒钟),我仍然有这种感觉"您错过了算盘中的东西"。任何推荐:
- 用于速度优化。有没有更好的方法来对中间产品进行分类?排序一组40个中间结果(通常)约为总执行时间的70%。
- 对于错过的问题。是否有机会仍然丢失关键数据(最终结果的分数应该是一个? )?
在更大的情况下,我正在实现纯想象变量的真实系数多项式类别(电阻抗:Z(JW))。Z是代表用户定义的系统的大型多个电话,系数指数范围很远。
"大"来自将ZC1 = 1/JC1W之类的东西添加到zc2 = 1/jc2w:
ZC1 ZC2 =(C1C2(JW)^2 0(JW))/(C1 C2)(JW)
在这种情况下,C1C2在Nanofarad(10E-9)中使用C1和C2,已经在10E-18中(并且仅启动...)
)我的排序功能使用复杂变量的曼哈顿距离(因为我的纯真实或纯虚构):
struct manhattan_complex_distance
{
bool operator() (std::complex<long double> a, std::complex<long double> b)
{
return std::fabs(std::real(a) + std::imag(a)) > std::fabs(std::real(b) + std::imag(b));
}
};
和我的多集在行动中:
std:complex<long double> get_value(std::vector<std::complex<long double>>& frequency_vector)
{
//frequency_vector is precalculated once for all to have at index n the value (jw)^n.
std::multiset<std::complex<long double>, manhattan_distance> temp_list;
for (int i=0; i<m_coeficients.size(); ++i)
{
// element of : ℝ * ℂ
temp_list.insert(m_coeficients[i] * frequency_vector[i]);
}
std::complex<long double> ret=0;
for (auto i:temp_list)
{
// it is VERY important to start adding the big values before adding the small ones.
// in informatics, 10^60 - 10^60 + 1 = 1; while 1 + 10^60 - 10^60 = 0. Of course you'd expected to get 1, not 0.
ret += i;
}
return ret;
}
我拥有的项目是启用C 11(主要用于改进数学和复杂数字工具)
ps:我重新制作要制作的代码很容易读取,实际上,所有复合物和长的双人名称都是模板:我可以立即更改多项式类型,或者将类用于ℝ
<的常规多项式>的常规多项式>正如Guygreer所建议的那样,您可以使用Kahan总结:
double sum = 0.0;
double c = 0.0;
for (double value : values) {
double y = value - c;
double t = sum + y;
c = (t - sum) - y;
sum = t;
}
编辑:您还应该考虑使用霍纳的方法评估多项式。
double value = coeffs[degree];
for (auto i = degree; i-- > 0;) {
value *= x;
value += coeffs[i];
}
对数据进行排序在正确的轨道上。但是,您绝对应该从最小到最大的总结,而不是从最大到最小的。从最大到最小的总结,到达最小的时候,将下一个值与当前总和对齐时,可能会导致下一个值的大部分或全部位数"掉下来"。最小的值从最小到最大,可以将更多的款项累积到最大的位置。结合Kahan总和,应该产生相当准确的总和。
首先:让您的数学跟踪错误。将双打替换为错误感知类型,当您添加或乘以两个双打时,它也会计算 maximim error 。
。这是您保证代码在合理快速的同时产生准确结果的唯一方法。
第二,不要使用multiset
。关联容器不是用于排序的,而是用于维护排序集合,同时能够有效地添加或删除元素。
添加/删除元素的能力增量意味着基于节点,而基于节点的表示通常是缓慢的。
如果您只需要分类的集合,请从vector
开始,然后std::sort
。
接下来,要最大程度地减少错误,请保留正面和负元素的列表。从零开始作为您的总和。现在选择最小的正或负元素中的最小元素,以使您的总和最接近零。
使用计算其误差界限的元素。
最后,确定您是否有5个精度。
这些错误的双打应尽可能在算法中尽早使用。
- 为什么这个运算符<重载函数对 STL 算法不可见?
- OpenMP阵列性能较差
- 基于ELO的团队匹配算法
- C++选择排序算法中的逻辑错误
- 有没有办法将谓词中的元素偏移量传递给 std 算法?
- C++A*算法并不总是在路径中具有目标节点
- 排序算法c++
- 如何将三维尺寸不固定的三维阵列展平为一维阵列
- 当我的阵列太大时出现分段错误
- 构建可组合有向图(扫描仪生成器的汤普森构造算法)
- 算法 - 圆形阵列旋转
- 如何在较高和较低数字的阵列中适应搜索算法(3N / 2)-2
- 计算以毫秒为单位的分类算法对整数阵列排序的时间
- 比较阵列相似性的最佳比较算法是什么?
- 将双打的阵列汇总为较大的价值跨度:正确的算法
- 阵列调试不正确的输出,复杂算法
- 哪种算法是求所有子阵列之和的最佳算法
- 实现算法(气泡排序)来组织阵列
- 算法将一个硬币矩阵转移到另一个硬币阵列
- 以以下格式添加2D阵列的元素的算法