Hàm căn bậc hai được thực hiện như thế nào?
Hàm căn bậc hai được thực hiện như thế nào?
Câu trả lời:
Thực hiện đơn giản bằng cách sử dụng Tìm kiếm nhị phân với 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;
}
Lưu ý rằng while
vòng lặp phổ biến nhất với tìm kiếm nhị phân nhưng cá nhân tôi thích sử dụng for
khi xử lý các số thập phân, nó tiết kiệm một số trường hợp đặc biệt xử lý và nhận được kết quả khá chính xác từ các vòng lặp nhỏ như vậy 1000
hoặc thậm chí 500
(Cả hai sẽ cho cùng một kết quả cho hầu hết các số nhưng chỉ để an toàn).
Chỉnh sửa: Xem bài viết Wikipedia này để biết các phương pháp-mục đích đặc biệt khác nhau chuyên tính toán căn bậc hai.
Chỉnh sửa 2: Áp dụng các bản cập nhật do @jorgbrown đề xuất để sửa chức năng trong trường hợp đầu vào ít hơn 1. Ngoài ra, hãy áp dụng tối ưu hóa để làm cho giới hạn gốc mục tiêu theo hệ số 10
double lo = 1, hi = n, mid; if (n < 1) lo = n, hi = 1;
(lo+hi)/2
để (lo+hi)*0.5
, tùy thuộc vào trình biên dịch, chắc chắn nó không phải làm bộ phận)
Trên phần cứng Intel, nó thường được triển khai trên đầu lệnh SQRT phần cứng. Một số thư viện chỉ sử dụng kết quả của việc đó, một số có thể đưa nó qua một vài vòng tối ưu hóa Newton để làm cho nó chính xác hơn trong các trường hợp góc.
FDLIBM (LIBM có thể phân phối tự do) có một phiên bản sqrt được tài liệu hóa khá đẹp. e_sqrt.c .
Có một phiên bản sử dụng số học nguyên và công thức lặp lại sửa đổi từng bit một.
Một phương pháp khác sử dụng phương pháp của Newton. Nó bắt đầu với một số ma thuật đen và một bảng tra cứu để lấy 8 bit đầu tiên và sau đó áp dụng công thức lặp lại
y_{i+1} = 1/2 * ( y_i + x / y_i)
trong đó x là số chúng ta bắt đầu với. Đây là phương pháp Heron của người Babylon . Nó có từ thời Anh hùng Alexandra vào giữa tháng Giêng sau Công nguyên.
Có một phương pháp khác được gọi là căn bậc hai nghịch đảo nhanh hoặc nghịch đảo . sử dụng một số "hack cấp độ bit dấu phẩy động độc ác" để tìm giá trị của 1 / sqrt (x). i = 0x5f3759df - ( i >> 1 );
Nó khai thác biểu diễn nhị phân của một số float bằng cách sử dụng phần định trị và số mũ. Nếu số x của chúng ta là (1 + m) * 2 ^ e, trong đó m là phần định trị và e là số mũ và kết quả y = 1 / sqrt (x) = (1 + n) * 2 ^ f. Ghi nhật ký
lg(y) = - 1/2 lg(x)
f + lg(1+n) = -1/2 e - 1/2 lg(1+m)
Vì vậy, chúng tôi thấy phần số mũ của kết quả là -1/2 số mũ của số. Ma thuật đen về cơ bản thực hiện một sự dịch chuyển theo chiều dọc trên số mũ và sử dụng phép gần đúng tuyến tính trên phần định trị.
Khi bạn đã có giá trị gần đúng đầu tiên, bạn có thể sử dụng các phương pháp của Newton để có kết quả tốt hơn và cuối cùng, một số cấp độ bit có tác dụng sửa chữ số cuối cùng.
Đây là cách triển khai thuật toán của Newton, xem 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
}
Sau đây là một giải thích toán học của đường ma thuật. Giả sử bạn muốn tìm căn của đa thức $ f (x) = x ^ 2 - a $. Theo phương pháp của Newton, bạn có thể bắt đầu với dự đoán ban đầu $ x_0 = 1 $. Dự đoán tiếp theo là $ x_1 = x_0 - f (x_0) / f '(x_0) $, trong đó $ f' (x) = 2x $. Do đó, dự đoán mới của bạn là
$ x_1 = x_0 - (x_0 ^ 2 - a) / 2x_0 $
sqrt (); chức năng Hậu trường.
Nó luôn kiểm tra các điểm giữa trong biểu đồ. Ví dụ: sqrt (16) = 4; sqrt (4) = 2;
Bây giờ nếu bạn cung cấp bất kỳ đầu vào nào bên trong 16 hoặc 4 như sqrt (10) ==?
Nó tìm thấy điểm giữa của 2 và 4 tức là = x, sau đó một lần nữa nó tìm thấy điểm giữa của x và 4 (Nó không bao gồm giới hạn dưới trong đầu vào này). Nó lặp đi lặp lại bước này cho đến khi nhận được câu trả lời hoàn hảo, tức là sqrt (10) == 3,16227766017. Nó nằm ở b / w 2 và 4. Tất cả hàm tích hợp này được tạo bằng cách sử dụng phép tính, phân biệt và tích hợp.
Thực thi bằng Python: Tầng của giá trị gốc là đầu ra của hàm này. Ví dụ: Căn bậc hai của 8 là 2,82842 ..., hàm này sẽ cho đầu ra '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
Tôi cũng đang tạo một hàm sqrt, 100000000 lần lặp mất 14 giây, vẫn chưa là gì so với 1 giây của sqrt
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;
}
Nhưng cách triển khai nhanh nhất là:
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));
}
Các giải pháp cho đến nay chủ yếu là dấu phẩy động ... và cũng đã giả định rằng lệnh chia có sẵn và nhanh chóng.
Đây là một quy trình đơn giản đơn giản không sử dụng FP hoặc chia. Mọi dòng tính toán một bit khác trong kết quả, ngoại trừ câu lệnh if đầu tiên tăng tốc quy trình khi đầu vào nhỏ.
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;
}
Để tính căn bậc hai (mà không sử dụng hàm math.sqrt có sẵn):
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
kiểu của bạn , phải không, trước khi nó đạt đến giá trị chính xác. Tôi cũng không chắc thuật toán nói chung sẽ hoạt động tốt như thế nào khi tìm căn bậc hai của 1E-36. Bạn có thể điều chỉnh số mũ - nhưng phạm vi thường là ± 300 hoặc lớn hơn và tôi không nghĩ rằng mã của bạn hoạt động tốt cho hầu hết phạm vi đó.
có một số thứ gọi là phương pháp Babylon.
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;
}
liên kết để biết thêm thông tin: https://www.geeksforgeeks.org/square-root-of-a-perfect-square/
Vì vậy, đề phòng trường hợp không có thông số kỹ thuật nào về việc không nên sử dụng hàm ceil hay hàm tròn tích hợp sẵn, đây là một cách tiếp cận đệ quy trong Java để tìm căn bậc hai của một số không dấu bằng phương pháp 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));
}
}
long long int floorSqrt(long long int x)
{
long long r = 0;
while((long)(1<<r)*(long)(1<<r) <= x){
r++;
}
r--;
long long b = r -1;
long long ans = 1 << r;
while(b >= 0){
if(((long)(ans|1<<b)*(long)(ans|1<<b))<=x){
ans |= (1<<b);
}
b--;
}
return ans;
}
Theo giải pháp của tôi ở 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))
}
Theo một giải pháp cổ điển / phổ biến.
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))
}
Để biết thêm thông tin kiểm tra tại đây
Cách sử dụng : root (số, gốc, độ sâu)
Ví dụ : root (16,2) == sqrt (16) == 4
Ví dụ : root (16,2,2) == sqrt (sqrt (16)) == 2
Ví dụ : root (64,3) == 4
Thực hiện trong C # :
double root(double number, double root, double depth = 1f)
{
return number ^ (root ^ (-depth));
}
Cách sử dụng : Sqrt (Số, độ sâu)
Ví dụ : Sqrt (16) == 4
Ví dụ : Sqrt (8,2) == sqrt (sqrt (8))
double Sqrt(double number, double depth = 1) return root(number,2,depth);
Bởi: Imk0tter
number
lên 0,5. OP có thể đã biết về danh tính này và quan tâm đến "làm cách nào để tính number
^ 0,5?"