Як реалізується функція квадратного кореня?
Як реалізується функція квадратного кореня?
Відповіді:
Проста реалізація за допомогою 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
(Обидва дадуть однаковий результат майже для всіх чисел але просто для безпеки).
Редагувати: Перегляньте цю статтю у Вікіпедії для різних спеціальних методів, що спеціалізуються на обчисленні квадратного кореня.
Редагування 2: Застосуйте оновлення, запропоновані @jorgbrown, щоб виправити функцію у разі введення менше 1. Крім того, застосуйте оптимізацію, щоб зробити межі від цільового кореня в 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)
де х - число, з якого ми почали. Це вавилонський метод методу Герона. Це датується Героєм Олександри у першому столітті нашої ери.
Існує ще один метод, який називається швидким зворотним квадратним коренем або зворотним коренем . який використовує деякий "злий рівень зламу з бітовою комою", щоб знайти значення 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 $
sqrt (); функція За лаштунками.
Він завжди перевіряє середні точки на графіку. Приклад: sqrt (16) = 4; sqrt (4) = 2;
Тепер, якщо ви вводите будь-які дані всередині 16 або 4, як sqrt (10) ==?
Він знаходить середню точку 2 і 4, тобто = x, потім знову знаходить середню точку x і 4 (Це виключає нижню межу в цьому введенні). Він повторює цей крок знову і знову, поки не отримає ідеальну відповідь, тобто sqrt (10) == 3.16227766017. Він лежить ч / б 2 і 4. Усі ці вбудовані функції створюються за допомогою обчислення, диференціації та інтеграції.
Реалізація в 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 секунд, все ще нічого в порівнянні з 1 секундою для 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;
}
Але найшвидша реалізація:
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/
Отже, про всяк випадок, якщо немає специфікацій щодо того, чи не використовувати вбудовану функцію «стеля» або «круглу», ось рекурсивний підхід у Java до пошуку квадратного кореня беззнакового числа за допомогою методу Ньютона-Рафсона.
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;
}
Слідом за моїм рішенням у Голангу.
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 (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 (номер, глибина)
Приклад : 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. ОП, мабуть, знав про цю особу і цікавився питанням "як я можу розрахувати number
^ 0,5?"