平方根函数如何实现?
平方根函数如何实现?
Answers:
使用Binary Search和C ++轻松实现
double root(double n){
// Max and min are used to take into account numbers less than 1
double lo = min(1, n), hi = max(1, n), mid;
// Update the bounds to be off the target by a factor of 10
while(100 * lo * lo < n) lo *= 10;
while(100 * hi * hi > n) hi *= 0.1;
for(int i = 0 ; i < 100 ; i++){
mid = (lo+hi)/2;
if(mid*mid == n) return mid;
if(mid*mid > n) hi = mid;
else lo = mid;
}
return mid;
}
请注意,while
循环是二进制搜索中最常见的方法,但我个人更喜欢for
在处理十进制数字时使用它,它节省了一些特殊情况的处理,并从类似1000
甚至什至这样的小循环中获得了非常准确的结果500
(两者都会为几乎所有数字给出相同的结果但为了安全起见)。
编辑:查阅此Wikipedia文章,了解专门用于计算平方根的各种特殊方法。
编辑2:在输入小于1的情况下,应用@jorgbrown建议的更新来修复该功能。此外,应用优化以使目标根的界限减小10倍。
double lo = 1, hi = n, mid; if (n < 1) lo = n, hi = 1;
(lo+hi)/2
为(lo+hi)*0.5
,具体取决于编译器,以确保它没有进行除法)
在Intel硬件上,它通常在硬件SQRT指令之上实现。一些库只使用直接的结果,一些库可能会经过几轮牛顿优化,使其在极端情况下更加准确。
FDLIBM(可自由分发的LIBM)具有很好的sqrt文档版本。e_sqrt.c。
具有一个使用整数算术和递归公式一次修改一位的版本。
另一种方法使用牛顿法。它以黑魔法和查找表开始,以获取前8位,然后应用递归公式
y_{i+1} = 1/2 * ( y_i + x / y_i)
x是我们开始的数字。这是苍鹭法的巴比伦法。它的历史可以追溯到公元一世纪的亚历山德拉英雄。
还有另一种方法称为快速逆平方根或reciproot。它使用一些“恶意浮点位级别黑客”来找到1 / sqrt(x)的值。i = 0x5f3759df - ( i >> 1 );
它使用螳螂和指数利用浮点数的二进制表示形式。如果我们的数字x是(1 + m)* 2 ^ e,其中m是尾数,则e是指数,结果y = 1 / sqrt(x)=(1 + n)* 2 ^ f。记录日志
lg(y) = - 1/2 lg(x)
f + lg(1+n) = -1/2 e - 1/2 lg(1+m)
因此,我们看到结果的指数部分是数字的-1/2。黑魔法基本上对指数进行逐位移位,并对尾数使用线性近似。
一旦有了良好的一阶近似值,就可以使用牛顿方法获得更好的结果,最后可以进行一些位级别的工作来确定最后一位。
这是牛顿算法的一种实现,请参见https://tour.golang.org/flowcontrol/8。
func Sqrt(x float64) float64 {
// let initial guess to be 1
z := 1.0
for i := 1; i <= 10; i++ {
z -= (z*z - x) / (2*z) // MAGIC LINE!!
fmt.Println(z)
}
return z
}
以下是魔术线的数学解释。假设您想找到多项式$ f(x)= x ^ 2-a $的根。通过牛顿的方法,您可以从初始猜测$ x_0 = 1 $开始。下一个猜测是$ x_1 = x_0-f(x_0)/ f'(x_0)$,其中$ f'(x)= 2x $。因此,您的新猜测是
$ x_1 = x_0-(x_0 ^ 2-a)/ 2x_0 $
在Python中的实现: 根值的下限是该函数的输出。示例:8的平方根是2.82842 ...,此函数将给出输出'2'
def mySqrt(x):
# return int(math.sqrt(x))
if x==0 or x==1:
return x
else:
start = 0
end = x
while (start <= end):
mid = int((start + end) / 2)
if (mid*mid == x):
return mid
elif (mid*mid < x):
start = mid + 1
ans = mid
else:
end = mid - 1
return ans
我也正在做一个sqrt函数,100000000次迭代需要14秒,与sqrt的1秒相比还是没什么
double mysqrt(double n)
{
double x = n;
int it = 4;
if (n >= 90)
{
it = 6;
}
if (n >= 5000)
{
it = 8;
}
if (n >= 20000)
{
it = 10;
}
if (n >= 90000)
{
it = 11;
}
if (n >= 200000)
{
it = 12;
}
if (n >= 900000)
{
it = 13;
}
if (n >= 3000000)
{
it = 14;
}
if (n >= 10000000)
{
it = 15;
}
if (n >= 30000000)
{
it = 16;
}
if (n >= 100000000)
{
it = 17;
}
if (n >= 300000000)
{
it = 18;
}
if (n >= 1000000000)
{
it = 19;
}
for (int i = 0; i < it; i++)
{
x = 0.5*(x+n/x);
}
return x;
}
但是最快的实现是:
float Q_rsqrt( float number )
{
long i;
float x2, y;
const float threehalfs = 1.5F;
x2 = number * 0.5F;
y = number;
i = * ( long * ) &y; // evil floating point bit level hacking
i = 0x5f3759df - ( i >> 1 ); // what the fuck?
y = * ( float * ) &i;
y = y * ( threehalfs - ( x2 * y * y ) ); // 1st iteration
// y = y * ( threehalfs - ( x2 * y * y ) ); // 2nd iteration, this can be removed
return y;
}
float mysqrt(float n) {return 1/Q_rsqrt(n);}
Formula: root(number, <root>, <depth>) == number^(root^(-depth))
Usage: root(number,<root>,<depth>)
Example: root(16,2) == sqrt(16) == 4
Example: root(16,2,2) == sqrt(sqrt(16)) == 2
Example: root(64,3) == 4
Implementation in C#:
static double root(double number, double root = 2f, double depth = 1f)
{
return Math.Pow(number, Math.Pow(root, -depth));
}
到目前为止,解决方案主要是浮点运算,并且还假定除法指令可用且快速。
这是一个不使用FP或除法的简单直接的例程。除了第一条if语句,当输入较小时,该语句会加速例程,每一行都会计算结果的另一位。
constexpr unsigned int root(unsigned int x) {
unsigned int i = 0;
if (x >= 65536) {
if ((i + 32768) * (i + 32768) <= x) i += 32768;
if ((i + 16384) * (i + 16384) <= x) i += 16384;
if ((i + 8192) * (i + 8192) <= x) i += 8192;
if ((i + 4096) * (i + 4096) <= x) i += 4096;
if ((i + 2048) * (i + 2048) <= x) i += 2048;
if ((i + 1024) * (i + 1024) <= x) i += 1024;
if ((i + 512) * (i + 512) <= x) i += 512;
if ((i + 256) * (i + 256) <= x) i += 256;
}
if ((i + 128) * (i + 128) <= x) i += 128;
if ((i + 64) * (i + 64) <= x) i += 64;
if ((i + 32) * (i + 32) <= x) i += 32;
if ((i + 16) * (i + 16) <= x) i += 16;
if ((i + 8) * (i + 8) <= x) i += 8;
if ((i + 4) * (i + 4) <= x) i += 4;
if ((i + 2) * (i + 2) <= x) i += 2;
if ((i + 1) * (i + 1) <= x) i += 1;
return i;
}
要计算平方根(不使用内置math.sqrt函数):
SquareRootFunction.java
public class SquareRootFunction {
public double squareRoot(double value,int decimalPoints)
{
int firstPart=0;
/*calculating the integer part*/
while(square(firstPart)<value)
{
firstPart++;
}
if(square(firstPart)==value)
return firstPart;
firstPart--;
/*calculating the decimal values*/
double precisionVal=0.1;
double[] decimalValues=new double[decimalPoints];
double secondPart=0;
for(int i=0;i<decimalPoints;i++)
{
while(square(firstPart+secondPart+decimalValues[i])<value)
{
decimalValues[i]+=precisionVal;
}
if(square(firstPart+secondPart+decimalValues[i])==value)
{
return (firstPart+secondPart+decimalValues[i]);
}
decimalValues[i]-=precisionVal;
secondPart+=decimalValues[i];
precisionVal*=0.1;
}
return(firstPart+secondPart);
}
public double square(double val)
{
return val*val;
}
}
MainApp.java
import java.util.Scanner;
public class MainApp {
public static void main(String[] args) {
double number;
double result;
int decimalPoints;
Scanner in = new Scanner(System.in);
SquareRootFunction sqrt=new SquareRootFunction();
System.out.println("Enter the number\n");
number=in.nextFloat();
System.out.println("Enter the decimal points\n");
decimalPoints=in.nextInt();
result=sqrt.squareRoot(number,decimalPoints);
System.out.println("The square root value is "+ result);
in.close();
}
}
int
在达到正确值之前,它可能会溢出您的类型,不是吗。我不确定整个算法在找到1E-36的平方根上的效果如何。您可以调整指数-但范围通常为±300或更大,并且我认为您的代码在该范围的大部分中效果都不理想。
有一种叫做巴比伦的方法。
static float squareRoot(float n)
{
/*We are using n itself as
initial approximation This
can definitely be improved */
float x = n;
float y = 1;
// e decides the accuracy level
double e = 0.000001;
while(x - y > e)
{
x = (x + y)/2;
y = n/x;
}
return x;
}
有关更多信息链接:https : //www.geeksforgeeks.org/square-root-of-a-perfect-square/
因此,以防万一没有关于是否使用内置ceil或round函数的规范,这是Java中的一种递归方法,它使用Newton-Raphson方法查找无符号数的平方根。
public class FindSquareRoot {
private static double newtonRaphson(double N, double X, double oldX) {
if(N <= 0) return 0;
if (Math.round(X) == Math.ceil(oldX))
return X;
return newtonRaphson(N, X - ((X * X) - N)/(2 * X), X);
}
//Driver method
public static void main (String[] args) {
System.out.println("Square root of 48.8: " + newtonRaphson(48.8, 10, 0));
}
}
按照我在Golang中的解决方案。
package main
import (
"fmt"
)
func Sqrt(x float64) float64 {
z := 1.0 // initial guess to be 1
i := 0
for int(z*z) != int(x) { // until find the first approximation
// Newton root algorithm
z -= (z*z - x) / (2 * z)
i++
}
return z
}
func main() {
fmt.Println(Sqrt(8900009870))
}
遵循经典/通用解决方案。
package main
import (
"fmt"
"math"
)
func Sqrt(num float64) float64 {
const DIFF = 0.0001 // To fix the precision
z := 1.0
for {
z1 := z - (((z * z) - num) / (2 * z))
// Return a result when the diff between the last execution
// and the current one is lass than the precision constant
if (math.Abs(z1 - z) < DIFF) {
break
}
z = z1
}
return z
}
func main() {
fmt.Println(Sqrt(94339))
}
欲了解更多信息,请点击这里
用法:root(数字,root,深度)
示例:root(16,2)== sqrt(16)== 4
示例:root(16,2,2)== sqrt(sqrt(16))== 2
示例:root(64,3)== 4
在C#中的实现:
double root(double number, double root, double depth = 1f)
{
return number ^ (root ^ (-depth));
}
用法:平方(深度)
范例:Sqrt(16)== 4
范例:Sqrt(8,2)== sqrt(sqrt(8))
double Sqrt(double number, double depth = 1) return root(number,2,depth);
创建人:Imk0tter
number
为0.5。OP可能知道此身份,并对“如何计算number
^ 0.5?”感兴趣。