Here is a algorithm I have come up with to calculate the square root, currently when testing it with a loop n times and stopwatch it's about 20-100x slower then C# Math.Sqrt()
;
Is there any way to improve the performance of this function or is the performance as good as it gonna get with this specific algorithm?
My C# square root algorithm:
static class MyMath
{
public static double Sqrt(double _d)
{
double x = 0;
double y = 2;
double z = 1;
double w = _d;
double h = 1;
double t = 0;
double px = 0;
int itr = 0;
while (true)
{
w = (w / y);
h *= y;
if (h > w)
{
t = w;
w = h;
h = t;
z *= 0.5;
y = (1 + z);
}
x = ((w + h) * 0.5);
if (itr >= 100 || w == h || px == x)
{
return (x);
}
px = x;
itr++;
}
}
}
How I test the performance:
using System.Diagnostics;
Stopwatch sw = new Stopwatch();
sw.Start();
for (int i = 0; i < 10000; i++)
{
MyMath.Sqrt(2);
}
sw.Stop();
Debug.Print(sw.ElapsedTicks.ToString());
EDIT3: Slightly improved version:
static class MyMath
{
public static double Sqrt(double _d)
{
double x = 0;
double y = 2;
double z = 1;
double w = _d;
double h = 1;
double t = 0;
double px = 1;
while (true)
{
if (x == px)
{
return ((w + h) * 0.5);
}
if (w < h)
{
t = w;
w = h;
h = t;
z *= 0.25;
y = (z + 1);
px = x;
}
w /= y;
h *= y;
x = (w + h);
}
}
}
EDIT3: Updated Slightly improved version2 + changed benchmark method2: (Running in Release Mode)
Stopwatch sw = new Stopwatch();
int n = 100000;
double[] squareArr = new double[n];
Random rng = new Random(1234);
for (int i = 0; i < n; i++)
{
squareArr[i] = rng.Next(1, 100000);
}
sw.Start();
for (int i = 0; i < n; i++)
{
squareArr[i] = MyMath.Sqrt(squareArr[i]) ;
}
sw.Stop();
debugBox.AppendText("AverageTime: " + (sw.ElapsedTicks / (double)n).ToString());
Currently according to my test the default Math.Sqrt() ~0.086 Ticks and mySqrt() ~4.8 Ticks.
EDIT 4:(Fixed bug: moved px = x in if statement)
答案 0 :(得分:0)
我创建了一个较新版本的算法,这个算法执行 ~0.97 Ticks vs Default Math.Sqrt ~0.086 所以仍然大约慢11倍,它不会在使用.ToString("G17")
进行检查时,它总是具有完全的双精度,但它非常接近,但是现在它与sqrt(12544)是正确的,并且与算法的原始版本相比,它是一个相当好的改进,也因为算法收敛如果您无论何时需要用笔和纸计算平方根,那么答案真的很快,这是一个很好的算法。
(原始编辑)
static class MyMath
{
public static double Sqrt(double _d)
{
double w = _d;
double h = 1;
double t = 0;
if (h > w)
{
h = _d;
w = 1;
}
while(true)
{
if (w < h)
{
break;
}
w *= 0.5;
h += h;
}
for (int i = 0; i < 5; i++)
{
t = ((w + h) * 0.5);
h = ((h / t) * w);
w = t;
}
return (t);
}
}
编辑 添加if (h > w)
以便在将数字小于1时提高精度,并将循环数从6减少到5。
编辑2019: 稍微更新的版本快24% (然后我原来的编辑):
public static double Sqrt(double _d)
{
double w = _d, h = 1, t = 0;
if (w < 1)
{
h = _d;
w = 1;
}
do
{
w *= 0.5;
h += h;
} while (w > h);
for (int i = 0; i < 4; i++)
{
t = ((w + h) * 0.5);
h = ((h / t) * w);
w = t;
}
return (((w + h) * 0.5));
}
我的最快版本,快51% (然后是我原来的编辑),总共只使用两个分区,但不是干净的代码:( Math.Sqrt()仍然快〜4倍)
public static double Sqrt(double _d)
{
double w = _d, h = 1;
if (w < 1)
{
h = _d;
w = 1;
}
do
{
w *= 0.5;
h += h;
} while (w > h);
double x = h + w;
double x2 = x * x;
double x4 = x2 * x * x;
double x6 = x4 * x * x;
double x8 = x6 * x * x;
double h2 = h * h;
double h3 = h2 * h;
double h4 = h3 * h;
double w2 = w * w;
double w3 = w2 * w;
double w4 = w3 * w;
double hw = h * w;
double h2w2 = h2 * w2;
double a = (256 * h4 * w4 + 1792 * h3 * w3 * x2 + 1120 * h2w2 * x4 + 112 * hw * x6 + x8);
double b = (16 * h2w2 + 24 * hw * x2 + x4);
double c = (4 * hw + x2);
double xcb = x * c * b;
return (8 * hw * xcb) / a + a / (32 * xcb);
}
编辑2 2019:
这是我创建的一种新算法,它通过y = x^2
(num to sqrt)的负偏移"a"
图形工作,然后x轴相交图形是精确的平方根,算法试图通过在最小最大边界之间创建一条线来找到该交互点,然后从该线与x.axis相交的位置创建两个新的最大最大边界并重复,它将非常快速地收敛在完全双精度平方根,通常在10次迭代。
这是一个紧凑的算法,只需很少的迭代即可收敛到非常高的精度,但遗憾的是它比我的其他算法慢一点(慢10倍),但我仍然认为这是一个有趣的算法。
public static double Sqrt3(double a)
{
double b = a + 1, c = 0, d = -a, e = b * b, f;
g: f = (c * e - d * b) / (e - d);
if (double.IsNaN(f)) return c;
d = f * f - a;
b = (f + c) * 0.5;
e = b * b - a;
c = f;
goto g;
}
/*
Sqrt(3)
0 : 0.631578947368421
1 : 3.37719298245614
2 : 1.81530333576402
3 : 1.74835949499074
4 : 1.73228078277221
5 : 1.7320513552106
6 : 1.7320508075871
7 : 1.73205080756888
8 : 1.73205080756888
9 : 1.73205080756888
1.7320508075688772 : My.Sqrt() .ToString("G17")
1.7320508075688772 : Math.Sqrt() .ToString("G17")
*/