Як реалізується функція квадратного кореня? [зачинено]


79

Як реалізується функція квадратного кореня?


17
Як це реалізується де?
fenomas

Якщо вам цікаво, у книзі « Числові рецепти» є багато відомостей про те, як обчислити квадратні корені, синуси та косинуси, експоненції, логарифми тощо.
Філіп Поттер

3
@Matt: додайте "... але спробуйте на цей раз трохи краще здогадатися", і це насправді точний опис!
Том Андерсон,


Відповіді:


22

Проста реалізація за допомогою 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 разів


3
Це збігається повільніше, ніж метод Ньютона-Рафсона. Це додає 1 біт точності за ітерацію, тоді як NR подвоює кількість бітів точності. Отже, якщо ви не проти "повільного", це працює.
Джонатан Леффлер

Чи можу я запитати, як це додає лише 1 біт на ітерацію? в той час як метод Ньютона його подвоює?
Amr Sabre

4
Ваш код зменшує вдвічі інтервал пошуку на кожній ітерації, що в основному еквівалентно додаванню 1 біта. Це трохи грубе наближення, але порахуйте ітерації і подивіться. NR використовує числення і робить кращу роботу з прогнозування результату. Після того, як ви отримаєте кілька бітів точності в квадратному корені, він дуже швидко сходиться. Див . Метод Ньютона у Вікіпедії - Приклад - Квадратний корінь - або SO щодо написання власної функції квадратного кореня або скористайтесь улюбленою пошуковою системою.
Джонатан Леффлер

1
Ця процедура взагалі не працює, коли n менше 1. Наприклад, для квадратного кореня 1/4 процедура починається з lo = 0 і hi = 1/4, але відповідь 1/2, який не знаходиться між 0 і 1/4. Тому він завжди повертає n, для всіх n менше 1. Після ітерації 1000 разів. Ви можете це виправити, змінивши свій перший рядок, де оголошуєте lo, hi і mid to bedouble lo = 1, hi = n, mid; if (n < 1) lo = n, hi = 1;
jorgbrown

1
Крім того, якщо хтось каже, що метод Ньютона використовує менше циклів, ви можете вказати, що метод Ньютона використовує поділ, який становить 15-80 циклів, залежно від того, який процесор використовується. З іншого боку, ваш цикл використовує лише множення та додавання, які складають лише кілька циклів кожен. (Незначна примітка: можливо, вам доведеться перейти (lo+hi)/2на (lo+hi)*0.5, залежно від компілятора, щоб переконатися, що він не робить поділу)
jorgbrown

9

На апаратному забезпеченні Intel воно часто реалізовується поверх апаратної інструкції SQRT. Деякі бібліотеки просто використовують результат цього прямо, деякі можуть застосувати його до кількох раундів оптимізації Ньютона, щоб зробити його більш точним у кутових випадках.


Чи точна інструкція sqrt апаратного забезпечення для всіх входів (помилка до 1ULP у всіх випадках, коли математично є дійсний результат)
Пол Стіліан,

@PaulStelian Я вважаю, що це так. У розділі 4.8.4 посібника розробника програмного забезпечення для архітектур Intel® 64 та IA-32 йдеться про округлення результатів загалом, і сказано: "Округлення призводить до помилки результату, яка в останньому місці менше, ніж одна одиниця". Розділ 5.2.2 перелічує інструкцію FSQRT із квадратним коренем як одну з "Основних арифметичних інструкцій xP FPU", тому я вважаю, що вона підпадає під це. У розділі 8.3.10 йдеться про точність трансцендентних функцій, але це означає тригонометричні інструкції.
Том Андерсон,

1
@PaulStelian Хоча зауважте, що документації щодо цього не завжди слід довіряти !
Том Андерсон,

8

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 показник числа. Чорна магія в основному здійснює побітове зсув на показник ступеня і використовує лінійне наближення на мантисі.

Отримавши гарне перше наближення, ви можете скористатися методами Ньютона, щоб отримати кращий результат і, нарешті, трохи розробити роботу, щоб виправити останню цифру.


6

Це реалізація алгоритму Ньютона, див. 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 $


1
Більш конкретно, це більш стисла реалізація cs.wustl.edu/~kjg/CS101_SP97/Notes/SquareRoot/sqrt.html , на яку вже згадувалося раніше, тобто метод Вавилону / Герони. Див. Також en.wikipedia.org/wiki/…
lima.sierra

1

sqrt (); функція За лаштунками.

Він завжди перевіряє середні точки на графіку. Приклад: sqrt (16) = 4; sqrt (4) = 2;

Тепер, якщо ви вводите будь-які дані всередині 16 або 4, як sqrt (10) ==?

Він знаходить середню точку 2 і 4, тобто = x, потім знову знаходить середню точку x і 4 (Це виключає нижню межу в цьому введенні). Він повторює цей крок знову і знову, поки не отримає ідеальну відповідь, тобто sqrt (10) == 3.16227766017. Він лежить ч / б 2 і 4. Усі ці вбудовані функції створюються за допомогою обчислення, диференціації та інтеграції.


1

Реалізація в 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

1

Я теж роблю функцію 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);}

1
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));
}

1
Дампи коду без будь-яких пояснень рідко бувають корисними. Stack Overflow - це навчання, а не надання фрагментів для сліпого копіювання та вставлення. Будь ласка, відредагуйте своє запитання та поясніть, як воно працює краще, ніж те, що надано операційною програмою (і не підписуйте свої повідомлення)
Кріс

1

Дотепер рішення були переважно з плаваючою комою ... і також передбачали, що інструкція розділення доступна і швидка.

Ось проста, проста процедура, яка не використовує 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;
}

0

Щоб обчислити квадратний корінь (без використання вбудованої функції 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();

    }

}

Обчислення цілочисельної частини здається повільним, якщо, наприклад, ви обчислюєте квадратний корінь 1E36. Насправді це, мабуть, переповнює ваш intтип, чи не так, перш ніж досягне правильного значення. Я не впевнений, наскільки добре працюватиме алгоритм в цілому, знаходячи також квадратний корінь 1E-36. Ви можете налаштувати експоненти - але діапазон зазвичай становить ± 300 або більше, і я не думаю, що ваш код працює добре для більшості цього діапазону.
Джонатан Леффлер

0

існує щось, що називається вавилонським методом.

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/


0

Отже, про всяк випадок, якщо немає специфікацій щодо того, чи не використовувати вбудовану функцію «стеля» або «круглу», ось рекурсивний підхід у 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));
    }
}

-1
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;
}

-1

Слідом за моїм рішенням у Голангу.

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))
}

Для отримання додаткової інформації перегляньте тут


-1

Використання : корінь (число, корінь, глибина)

Приклад : 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?"
weirdev
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.