Как найти квадратный корень без использования функции sqrt?

Я обнаружил алгоритм определения квадратного корня без использования функции sqrt, а затем попытался ввести его в программирование. Я получаю этот рабочий код на C ++

    #include <iostream>
    using namespace std;

    double SqrtNumber(double num)
    {
             double lower_bound=0; 
             double upper_bound=num;
             double temp=0;                    /* ek edited this line */

             int nCount = 50;

        while(nCount != 0)
        {
               temp=(lower_bound+upper_bound)/2;
               if(temp*temp==num) 
               {
                       return temp;
               }
               else if(temp*temp > num)

               {
                       upper_bound = temp;
               }
               else
               {
                       lower_bound = temp;
               }
        nCount--;
     }
        return temp;
     }

     int main()
     {
     double num;
     cout<<"Enter the number\n";
     cin>>num;

     if(num < 0)
     {
     cout<<"Error: Negative number!";
     return 0;
     }

     cout<<"Square roots are: +"<<sqrtnum(num) and <<" and -"<<sqrtnum(num);
     return 0;
     } 

Теперь проблема заключается в инициализации количества итераций nCount в объявлении (здесь 50). Например, чтобы найти квадратный корень из 36, требуется 22 итерации, поэтому нет проблем, тогда как нахождение квадратного корня из 15625 занимает более 50 итераций, поэтому он вернет значение temp после 50 итераций. Пожалуйста, дайте решение для этого.


person Arun Pandey    schedule 26.10.2013    source источник
comment
Обычно вы проверяете разницу, вносимую каждой итерацией, и когда она опускается ниже определенного уровня, вы относитесь к результату как к достаточно точному. Также обратите внимание, что метод Ньютона обычно немного быстрее, чем деление пополам, как вы используете.   -  person Jerry Coffin    schedule 26.10.2013
comment
как насчет проверки разницы между temp * temp и числом и остановки цикла, если оно достаточно близко?   -  person Martin Beckett    schedule 26.10.2013
comment
@jrok в восторге! Если серьезно, то это профессиональный способ: en.wikipedia.org/wiki/Newton's_method   -  person Bathsheba    schedule 27.10.2013
comment
@MartinBeckett да, это сработало, если (temp * temp-num ›0.001) {upper_bound = temp;} else {lower_bound = temp; } Достаточно ли 0,001 для проверки?   -  person Arun Pandey    schedule 27.10.2013
comment
@Bathsheba Да, это был насмешливый комментарий.   -  person jrok    schedule 27.10.2013
comment
@ArunPandey: Насколько точным вы хотите получить результат? Обычно вы хотите получить N значащих цифр, и в этом случае вам нужно что-то вроде input / 10eN.   -  person Jerry Coffin    schedule 27.10.2013
comment
@Bathsheba кроме того, корень n-й степени из x === x ^ (1 / n)   -  person Albert Renshaw    schedule 12.06.2016


Ответы (9)


Есть лучший алгоритм, которому требуется не более 6 итераций для схождения до максимальной точности для чисел типа double:

#include <math.h>

double sqrt(double x) {
    if (x <= 0)
        return 0;       // if negative number throw an exception?
    int exp = 0;
    x = frexp(x, &exp); // extract binary exponent from x
    if (exp & 1) {      // we want exponent to be even
        exp--;
        x *= 2;
    }
    double y = (1+x)/2; // first approximation
    double z = 0;
    while (y != z) {    // yes, we CAN compare doubles here!
        z = y;
        y = (y + x/y) / 2;
    }
    return ldexp(y, exp/2); // multiply answer by 2^(exp/2)
}

Алгоритм начинается с 1 в качестве первого приближения для значения квадратного корня. Затем на каждом шаге он улучшает следующее приближение, принимая среднее значение между текущим значением y и x/y. Если y = sqrt(x), будет то же самое. Если y> sqrt(x), то x/ysqrt(x) примерно на такую ​​же сумму. Другими словами, он очень быстро сойдется.

ОБНОВЛЕНИЕ: чтобы ускорить сходимость для очень больших или очень маленьких чисел, изменена функция sqrt() для извлечения двоичной экспоненты и вычисления квадратного корня из числа в [1, 4) диапазоне. Теперь для получения двоичной экспоненты требуется frexp() из <math.h>, но можно получить эту экспоненту, извлекая биты из числового формата IEEE-754 без использования frexp().

person mvp    schedule 26.10.2013
comment
Я предполагаю, что условие для правила остановки должно использовать меньше (или равно): while (fabs (y-z) ‹= 0.00000000001 - person NoChance; 27.10.2013
comment
@EmmadKareem: нет, правило в порядке. Я бы изменил его на относительную точность - для очень больших чисел могут возникнуть проблемы. Еще одно приятное улучшение - ограничить количество раундов до 20. - person mvp; 27.10.2013
comment
Я провел несколько тестов, и для чисел от 1 до 4 требуется всего 6 раундов, чтобы достичь максимальной двойной точности. Однако для больших чисел, таких как 1000000, потребуется больше времени. Решение состоит в том, чтобы взять экспоненту отдельно и вычислить sqrt только из числа, которое не является ни большим, ни малым - где-то около 1. Таким образом, количество раундов можно даже зафиксировать, например, равным 6. - person mvp; 27.10.2013
comment
Кроме того, я думаю, что это один из очень редких случаев, когда вместо проверки fabs(y-z) < delta вы действительно можете использовать точную проверку равенства для двойников: y == z. Это связано с тем, что алгоритм сходится очень быстро, и после схождения с этого момента он будет точно таким же числом. - person mvp; 27.10.2013
comment
@mvp Я знаю, что y = (y + x / y) / 2; происходит от y ^ 2 = x, но что пришло вам в голову, чтобы написать это, в математике как называется этот трюк? Как будто нам нужно изучить этот метод в целом, что нам искать? - person codey modey; 09.03.2014
comment
Это называется вавилонским методом. Эта формула также может быть получена из метода Ньютона. - person mvp; 10.03.2014

Почему бы не попробовать использовать вавилонский метод для поиска квадратного корня.

Вот мой код для этого:

double sqrt(double number)
{
    double error = 0.00001; //define the precision of your result
    double s = number;

    while ((s - number / s) > error) //loop until precision satisfied 
    {
        s = (s + number / s) / 2;
    }
    return s;
}

Удачи!

person Newskooler    schedule 10.11.2016
comment
правильно использовать ( & ), затрудняет чтение кода .. На секунду я запутался, почему вы пишете s-number, когда s = number - person Abhinav Gauniyal; 12.08.2017
comment
Я бы не стал использовать фиксированные десятичные точки. Я бы заменил строку double error = 0.00001; на double error = number * 10e-8; - это сделало бы ошибку относительно размера входного значения. Но +1 за красивый и простой пример. - person Combinatix; 22.07.2018

Полностью удалите свой nCount (поскольку есть некоторые корни, для которых этот алгоритм потребует много итераций).

double SqrtNumber(double num)
{
    double lower_bound=0; 
    double upper_bound=num;
    double temp=0;

    while(fabs(num - (temp * temp)) > SOME_SMALL_VALUE)
    {
           temp = (lower_bound+upper_bound)/2;
           if (temp*temp >= num)
           {
                   upper_bound = temp;
           }
           else
           {
                   lower_bound = temp;
           }
    }
    return temp;
 }
person Zac Howland    schedule 26.10.2013
comment
не работает, он просто показывает ближайшие значения даже для идеального квадрата - person Arun Pandey; 27.10.2013
comment
Ой, да, вы правы (извините, набираю слишком быстро). Как отметил @mvp, это очень медленный алгоритм для поиска приближений квадратного корня. Есть и другие, которые намного быстрее. - person Zac Howland; 27.10.2013

Как я обнаружил, этот вопрос старый и имеет много ответов, но у меня есть простой ответ, который отлично работает.

#define EPSILON 0.0000001 // least minimum value for comparison
double SquareRoot(double _val) {
    double low = 0; 
    double high = _val;
    double mid = 0; 

    while (high - low > EPSILON) {
            mid = low + (high - low) / 2; // finding mid value
            if (mid*mid > _val) {
                high = mid;
            } else {
                low = mid;
            }    
    }   
    return mid;
}

Надеюсь, это будет полезно для будущих пользователей.

person Himanshu    schedule 03.03.2016
comment
Попробуйте вызвать эту функцию со значением, скажем, 10 000 000 000, и посмотрите, сколько времени потребуется, чтобы получить ответ. Или позвоните по номеру 0,000000001 и посмотрите, правильный ли ответ. - person mvp; 01.06.2016

если вам нужно найти квадратный корень без использования sqrt(), используйте root=pow(x,0.5).

Где x - значение, квадратный корень которого вам нужно найти.

person Mani Kant    schedule 09.09.2014

Вот очень простой, но небезопасный подход к нахождению квадратного корня из числа. Небезопасно, потому что он работает только с натуральными числами, когда вы знаете, что основание или показатель степени являются натуральными числами. Мне пришлось использовать его для задачи, в которой мне не разрешалось использовать #include ‹cmath› -библиотеку, и мне не разрешалось использовать указатели.

потенция = основание ^ показатель степени

// FUNCTION: square-root
int sqrt(int x)
{
    int quotient = 0;
    int i = 0;

    bool resultfound = false;
    while (resultfound == false) {
        if (i*i == x) {
          quotient = i;
          resultfound = true;
        }
        i++;
    }
    return quotient;
}
person PatrickSteiner    schedule 12.09.2017

Это очень простой рекурсивный подход.

double mySqrt(double v, double test) {
    if (abs(test * test - v) < 0.0001) {
        return test;
    }

    double highOrLow = v / test;
    return mySqrt(v, (test + highOrLow) / 2.0);
}
double mySqrt(double v) {
    return mySqrt(v, v/2.0);
}
person Cameron Monks    schedule 26.02.2019

Вот отличный код для поиска sqrt, который работает быстрее, чем исходная функция sqrt.

float InvSqrt (float x) 
{
    float xhalf = 0.5f*x;
    int i = *(int*)&x;
    i = 0x5f375a86 - (i>>1);
    x = *(float*)&i;
    x = x*(1.5f - xhalf*x*x);
    x = x*(1.5f - xhalf*x*x);
    x = x*(1.5f - xhalf*x*x);
    x=1/x;
    return x;
}
person galaxyan    schedule 06.11.2014
comment
1) без объяснения причин бесполезен. 2) заканчивается разделением, показывая, что идея непонятна 3) Без указания авторства это плагиат. - person Pascal Cuoq; 24.01.2015
comment
@PascalCuoq: Автор неизвестен. hackersdelight.org/hdcodetxt/rsqrt.c.txt - person namar0x0309; 01.02.2015
comment
Фактически это приписывается Грегу Уолшу: en.wikipedia.org/wiki/. Если вы читали множество эссе, написанных по теме этой функции, вы могли бы использовать те же приемы для вычисления sqrt напрямую, вместо вычисления обратного квадратного корня и получения обратного, что полностью отменяет любую элегантность, которая была в оригинале. алгоритм: en.wikipedia.org/wiki/ - person Pascal Cuoq; 01.02.2015

После просмотра предыдущих ответов я надеюсь, что это поможет разрешить любые неясности. В случае, если сходство в предыдущих решениях и моем решении иллюзорно, или этот метод решения для корней неясен, я также сделал график, который можно найти здесь.

Это рабочая корневая функция, способная решать любые задачи с корнем n-й степени.

(по умолчанию для этого вопроса используется квадратный корень)

#include <cmath> 
// for "pow" function

double sqrt(double A, double root = 2) {
    const double e = 2.71828182846;
    return pow(e,(pow(10.0,9.0)/root)*(1.0-(pow(A,-pow(10.0,-9.0)))));
}

Объяснение:

щелкните здесь, чтобы просмотреть график

Это работает через ряды Тейлора, логарифмические свойства и немного алгебры.

Возьмем, например:

log A = N
   x

* Примечание: для квадратного корня N = 2; для любого другого корня вам нужно изменить только одну переменную N.

1) Измените базу, преобразуйте базовую логарифмическую функцию 'x' в натуральный логарифм,

log A   =>   ln(A)/ln(x) = N
   x

2) Переставьте, чтобы изолировать ln (x), и в конечном итоге просто 'x',

ln(A)/N = ln(x)

3) Установите обе стороны как экспоненты от 'e',

e^(ln(A)/N) = e^(ln(x))  >~{ e^ln(x) == x }~>  e^(ln(A)/N) = x

4) Ряд Тейлора представляет "ln" как бесконечный ряд,

ln(x) = (k=1)Sigma: (1/k)(-1^(k+1))(k-1)^n

           <~~~ expanded ~~~>

[(x-1)] - [(1/2)(x-1)^2] + [(1/3)(x-1)^3] - [(1/4)(x-1)^4] + . . .

* Примечание: продолжайте серию для повышения точности. Для краткости в моей функции используется 10 ^ 9, которая выражает сходимость ряда для натурального логарифма примерно с 7 цифрами или 10-миллионным разрядом для точности,

ln(x) = 10^9(1-x^(-10^(-9)))

5) Теперь просто вставьте это уравнение для натурального логарифма в упрощенное уравнение, полученное на шаге 3.

e^[((10^9)/N)(1-A^(-10^-9)] = nth-root of (A)

6) Эта реализация может показаться излишней; однако его цель - продемонстрировать, как можно найти корни, не угадывая и не проверяя. Кроме того, это позволит вам заменить функцию pow из библиотеки cmath вашей собственной функцией pow:

double power(double base, double exponent) {
    if (exponent == 0) return 1;
    int wholeInt = (int)exponent;
    double decimal = exponent - (double)wholeInt;
    if (decimal) {
        int powerInv = 1/decimal;
        if (!wholeInt) return root(base,powerInv);
        else return power(root(base,powerInv),wholeInt,true);
    }
    return power(base, exponent, true);
}

double power(double base, int exponent, bool flag) {
    if (exponent < 0) return 1/power(base,-exponent,true);
    if (exponent > 0) return base * power(base,exponent-1,true);
    else return 1;
}

int root(int A, int root) {
    return power(E,(1000000000000/root)*(1-(power(A,-0.000000000001))));
}
person JReyn    schedule 18.12.2015