我正在处理一系列点,这些点都具有相同的Y值,但X值不同。我通过将X递增1来完成这些点。例如,我可能有Y = 50,X是-30到30之间的整数。我的算法的一部分包括从每个点找到到原点的距离,然后进行进一步处理。
在分析之后,我发现距离计算中的sqrt调用占用了我很大的时间。是否有迭代方法来计算距离?
换句话说:
我想有效地计算:r[n] = sqrt(x[n]*x[n] + y*y))
。我可以保存上一次迭代的信息。每次迭代都会通过递增x来改变,因此x[n] = x[n-1] + 1
。我不能使用sqrt或trig函数,因为它们太慢,除了在每条扫描线的开头。
我可以使用近似值,只要它们足够好(误差小于0.1%)并且引入的误差是平滑的(我不能将其归结为预先计算的近似表)。
其他信息: x和y总是-150到150之间的整数
我明天会尝试一些想法,并根据最快的答案标出最佳答案。
结果
我做了一些时间
我希望测试能在两者之间做出决定,因为我喜欢这两个答案。我要和Pete一起去,因为它占用的内存较少。
答案 0 :(得分:4)
为了感受它,对于你的范围y = 50,x = 0给出r = 50和y = 50,x = +/- 30给出r~ = 58.3。您希望近似值为+/- 0.1%,或+/- 0.05绝对值。这比大多数图书馆都要低很多。
两种近似方法 - 您根据先前值的插值计算r,或使用合适系列的几个项。
r =(x 2 + y 2 ) 1/2
dr / dx = 1/2。 2x。 (x 2 + y 2 ) -1/2 = x / r
double r = 50;
for ( int x = 0; x <= 30; ++x ) {
double r_true = Math.sqrt ( 50*50 + x*x );
System.out.printf ( "x: %d r_true: %f r_approx: %f error: %f%%\n", x, r, r_true, 100 * Math.abs ( r_true - r ) / r );
r = r + ( x + 0.5 ) / r;
}
给出:
x: 0 r_true: 50.000000 r_approx: 50.000000 error: 0.000000%
x: 1 r_true: 50.010000 r_approx: 50.009999 error: 0.000002%
....
x: 29 r_true: 57.825065 r_approx: 57.801384 error: 0.040953%
x: 30 r_true: 58.335225 r_approx: 58.309519 error: 0.044065%
似乎符合0.1%误差的要求,所以我没有打扰下一个编码,因为它需要更多的计算步骤。
对于x接近零的sqrt(1 + x)的泰勒系列是
sqrt(1 + x)= 1 + 1/2 x - 1/8 x 2 ... +( - 1/2) n + 1 x 名词
使用r = y sqrt(1 +(x / y) 2 )然后你要找一个项t =( - 1/2) n + 1 0.36 n ,其幅度小于0.001,log(0.002)> n log(0.18)或n> 3.6,所以将术语带到x ^ 4应该是好的。
答案 1 :(得分:2)
Y=10000
Y2=Y*Y
for x=0..Y2 do
D[x]=sqrt(Y2+x*x)
norm(x,y)=
if (y==0) x
else if (x>y) norm(y,x)
else {
s=Y/y
D[round(x*s)]/s
}
如果您的坐标是平滑的,那么可以使用线性插值扩展该想法。为了更精确,增加Y.
这个想法是s *(x,y)在y = Y的线上,你预先计算了它的距离。获取距离,然后除以s。
我认为你真的做需要距离而不是它的正方形。
你也可以找到一个普通的sqrt实现,牺牲一些速度的准确性,但我很难想象打败FPU可以做什么。
通过线性插值,我的意思是将D[round(x)]
更改为:
f=floor(x)
a=x-f
D[f]*(1-a)+D[f+1]*a
答案 2 :(得分:1)
这并不能真正回答你的问题,但可能有所帮助...
我要问的第一个问题是:
所以我从:
开始如此聪明的数学可能不是最快的解决方案。
答案 3 :(得分:1)
那么总是会尝试优化你的sqrt,我见过的最快的就是旧的carmack地震3 sqrt:
http://betterexplained.com/articles/understanding-quakes-fast-inverse-square-root/
那就是说,因为sqrt是非线性的,所以你不可能沿着你的线进行简单的线性插值来得到你的结果。最好的想法是使用表查找,因为这将使您能够快速访问数据。而且,由于您似乎是按整数进行迭代,因此表查找应该非常准确。
答案 4 :(得分:0)
好吧,你可以围绕x = 0镜像开始(你只需要计算n&gt; = 0,并且那些结果会导致相应的n <0)。在那之后,我将看一下使用sqrt上的导数(a ^ 2 + b ^ 2)(或相应的sin)来利用常数dx。
如果这还不够准确,我是否可以指出这对于SIMD来说是一个相当不错的工作,它将在SSE和VMX(以及着色器模型2)上为您提供倒数平方根操作。
答案 5 :(得分:0)
这与HAKMEM item:
有关第149项(明斯基):循环算法 这是一种优雅的绘画方式 点绘图显示中的圆圈:
NEW X = OLD X - epsilon * OLD Y NEW Y = OLD Y + epsilon * NEW(!) X
这是一个非常圆的椭圆 以其大小为中心的原点 由初始点确定。 epsilon确定角度 循环点的速度,和 略微影响偏心率。如果 epsilon是2的幂,那么我们就没有 甚至需要乘法,更不用说了 平方根,正弦和余弦!该 “圆圈”将非常稳定 因为分数很快就会变成 周期性的。
圆算法是由发明的 我试图保存一个错误 注册显示黑客!本·格利 有一个惊人的显示黑客只使用 约六或七条指令,和 这是一个伟大的奇迹。但事实确实如此 基本上是面向行的。发生了 对我来说,这将是令人兴奋的 有曲线,我试图得到一个 曲线显示黑客与最小 指令。