음이 아닌 실수 A 의 평방근 sqrt(A) 를 구하는 Heron 의 방법:

        반복함수  g(x) = (x + A/x) / 2   를 이용

 

실수 A 의 n제곱근 root(n, A) 를 구하는 Newton-Raphson 의 방법

        반복함수  g(x) = ((n-1)*x + A/(x**(n - 1))) / n    를 이용

n = 2 인 경우에는 Newton-Raphson 의 방법이 Heron 의 방법과 동일하다.

(참조. http://en.wikipedia.org/wiki/Newton's_method )

 

C 언어와 C++ 언어에는 (math.h 를 인클루드하여 사용하는) 표준 수학 라이브러리에 지수 계산 함수 pow() 가 이미 구현되어 있다. 하지만 차후 필요한 데가 있을 것 같아서 이와 유사한 n 제곱 함수와 n 제곱근 함수를 구현해 보았다.

지수가 정수인 거듭제곱을 계산하는  함수도 nPow(), gPow, mPow() 세 개 구현해 놓았는데, 이들 세 함수는 절차적 언어의 성능상 재귀호출이 아니고 단순 반복 기법을 사용하는 함수이다. 이 세 함수 중 mPow() 의 성능이 가장 우수하다. 큰 지수의 경우 for 반복문의 반복회수를 따져 보면 성능 비교를 할 수 있을 것이다. (성능 비교를 위해 세 가지를 모두 소스에 남겨 두었다.) mPow() 함수는 n 제곱근을 구하는 재귀함수 newtonNthRoot(int, double) 의 구현에 사용되기도 한다. if ... else ... 구문이 많아 소스가 복잡하게 보일지 모르겠으나 이는 밑수나 지수가 음수이거나 0인 경우의 처리를 위함이다. 구현된 모든 함수의 구현에는 예외상황(예를 들어, 음수의 짝수 제곱근 같은 예외상황) 처리 과정이 있다.

아래의 소스는 Visual C++ 외에 g++ 로 컴파일해도 수정 없이 잘 되리라고 본다.

예외상황 처리 때문에, Visual C++ 로 컴파일 할 때는 /EHsc 옵션을 붙여 주어야 하고, g++ 로 컴파일할 때는 -fexceptions 옵션을 반드시 붙여 주어야 한다.

// Filename: testNthRoot.cpp
//
//            Approximate square roots, cubic roots and n-th roots of a given number.
//
// Compile: cl /EHsc testNthRoot.cpp
// Execute: testNthRoot
//
//  Or
//
// Compile:  g++ -o testNthRoot testNthRoot.cpp -fexceptions
// Execute: ./testNthRoot
//
// Date: 2013. 1. 6.
// Copyright (c) 2013 PH Kim  (pkim __AT__ scripts.pe.kr)

#include <stdio.h>
#include <iostream>
#include <cmath>
#include <cstring>
// #include <memory.h>
#include <exception>

using namespace std;

class CalcException: public exception
{
  virtual const char* what() const throw()
  {
    return "Not supported calculation exception";
  }
} cannotCalcEx;

class NotDefinedException: public exception
{
  virtual const char* what() const throw()
  {
    return "Not defined calculation exception";
  }
} notDefinedEx;

class MayInaccurateException: public exception
{
  virtual const char* what() const throw()
  {
    return "Inaccurate approximation by too many iterrations.";
  }
} inaccurateApproxEx;

 

#define MAX_ITER 20000
#define M_EPSILON 1.0e-15


/**
  * Compute the n-th root of x to a given scale, x > 0.
  */
double nPow(double a, int n)
{
    if (n > 0) {
        if (n == 1)
            return a;
        else {
            if (a == 0.0 || a == 1.0) {
                return a;
            }
            else if (a == -1.0) {
                if (n % 2 == 1)
                    return -1.0;
                else
                    return 1.0;
            }
            else if (a < 0.0) {
                if (n % 2 == 1)
                    return -nPow(-a, n);
                else
                    return nPow(-a, n);
            }
            else {
                double y = 1.0;
                for (int i = 0; i < n; i++) {
                    y *= a;
                }
                return y;
            }
        }
    }
    else if (n == 0) {
        return 1.0;
    }
    else {      //  when n < 0
        if (a == 0.0)
            // throw "Negative powering exception of zero.";
            throw cannotCalcEx;
        else {
            if (n == -1)
                return 1.0/a;
            else
                return 1.0/nPow(a, -n);
        }
    }
}

 

/**
  * Compute the n-th root of x to a given scale, x > 0.
  */
double gPow(double a, int n)
{
    if (n > 0) {
        if (n == 1)
            return a;
        else {
            if (a == 0.0 || a == 1.0) {
                return a;
            }
            else if (a == -1.0) {
                if (n % 2 == 1)
                    return -1.0;
                else
                    return 1.0;
            }
            else if (a < 0.0) {
                if (n % 2 == 1)
                    return -gPow(-a, n);
                else
                    return gPow(-a, n);
            }
            else {


                double y = 1.0;
                double r = a;
                int m = 8*sizeof(int) - 1;   // ignore the most significant bit which means the +/- sign.
                int one = 1;
                for (int i = 0; i < m; i++) {
                    if ((n & one) == 0) {
                        y *= 1.0;
                    }
                    else {
                        y *= r;
                    }
                    r = r*r;
                    one <<= 1;
                    if (one > n)
                        break;
                }
                return y;
            }
        }
    }
    else if (n == 0) {
        return 1.0;
    }
    else {      //  when n < 0
        if (a == 0.0)
            // throw "Negative powering exception of zero.";
            throw cannotCalcEx;
        else {
            if (n == -1)
                return 1.0/a;
            else
                return 1.0/gPow(a, -n);
        }
    }
}

 

/**
  * Compute the n-th root of x to a given scale, x > 0.
  */
double mPow(double a, int n)
{
    if (n > 0) {
        if (n == 1)
            return a;
        else {
            if (a == 0.0 || a == 1.0) {
                return a;
            }
            else if (a == -1.0) {
                if (n % 2 == 1)
                    return -1.0;
                else
                    return 1.0;
            }
            else if (a < 0.0) {
                if (n % 2 == 1)
                    return -mPow(-a, n);
                else
                    return mPow(-a, n);
            }
            else {


                double y = 1.0;
                double r = a;
                int m = n;
                while (m > 0) {
                    if ((m & 0x1) != 0) {
                        y *= r;
                    }
                    r = r*r;
                    m >>= 1;
                }
                return y;
            }
        }
    }
    else if (n == 0) {
        return 1.0;
    }
    else {      //  when n < 0
        if (a == 0.0)
            // throw "Negative powering exception of zero.";
            throw cannotCalcEx;
        else {
            if (n == -1)
                return 1.0/a;
            else
                return 1.0/mPow(a, -n);
        }
    }
}

 

/**
  * Compute the square root of x to a given scale, x > 0.
  */
double heronSqrt(double a)
{
    if (a < 0.0) {
        // throw "Cannot find the sqrt of a negative number.";
        throw notDefinedEx;
    }
    else if (a == 0.0 || a == 1.0) {
        return a;
    }
    else {
        double x1 = a;
        double x2 = (x1 + a/x1)/2.0;
        double er = x1 - x2;
        int counter = 0;
        while (x1 + er != x1) {
            x1 = x2;
            x2 = (x1 + a/x1)/2.0;
            er = x1 - x2;
            if (abs(er) < abs(M_EPSILON*x1))
                break;
            counter++;
            if (counter > MAX_ITER)
                break;
        }
        if (counter >= MAX_ITER)
            // throw "Inaccurate sqrt exception by too many iterations.";
            throw inaccurateApproxEx;
        return x2;
    }
}

/**
  * Compute the cubic root of x to a given scale, x > 0.
  */
double newtonCbrt(double a)
{
    if (a == 0.0 || a == 1.0 || a == -1.0) {
        return a;
    }
    else if (a < 0.0) {
        return -newtonCbrt(-a);
    }
    else {
        double x1 = a;
        double x2 = (2.0*x1 + a/(x1*x1))/3.0;
        double er = x1 - x2;
        int counter = 0;
        while (x1 + er != x1) {
            x1 = x2;
            x2 = (2.0*x1 + a/(x1*x1))/3.0;
            er = x1 - x2;
            if (abs(er) < abs(M_EPSILON*x1))
                break;
            counter++;
            if (counter > MAX_ITER)
                break;
        }
        if (counter >= MAX_ITER)
            // throw "Inaccurate cbrt exception by too many iterations.";
            throw inaccurateApproxEx;
        return x2;
    }
}

/**
  * Compute the n-th root of x to a given scale, x > 0.
  */
double newtonNthRoot(int n, double a)
{
    if (n == 0) {
        return 1.0;
    }
    else if (n == 1) {
        return a;
    }
    else if (n > 0) {
        if (a == 0.0 || a == 1.0) {
            return a;
        }
        else if (a == -1.0) {
            if (n % 2 == 1)
                return a;
            else
                // throw "Cannot find the even n-th root of a negative number.";
                throw notDefinedEx;
        }
        else if (a < 0.0) {
            if (n % 2 == 1)
                return -newtonNthRoot(n, -a);
            else
                // throw "Cannot find the even n-th root of a negative number.";
                throw notDefinedEx;
        }
        else if (a < 1.0) {
            return 1.0/newtonNthRoot(n, 1.0/a);
        }
        else {
            double x1 = a;
            double xn = mPow(x1, n - 1);
            double x2 = ((n - 1)*x1 + a/xn)/n;
            double er = x1 - x2;
            int counter = 0;
            while (x1 + er != x1) {
                x1 = x2;
                xn = mPow(x1, n - 1);
                x2 = ((n - 1)*x1 + a/xn)/n;
                er = x1 - x2;
                if (abs(er) < abs(M_EPSILON*x1))
                    break;
                counter++;
                if (counter > MAX_ITER)
                    break;
            }
            if (counter >= MAX_ITER)
                // throw "Inaccurate n-th root exception by too many iterations.";
                throw inaccurateApproxEx;
            return x2;
        }
    }
    else {
        if (a == 0.0) {
            // throw "Cannot find the negative n-th root of zero.";
            throw cannotCalcEx;
        }
        else {
            return 1.0/newtonNthRoot(-n, a);
        }
    }
}

 

int main()
{
    std:cout.precision(16);

    double x = 16.0;
    double u = sqrt(x);


    cout << "[ Testing heronSqrt(double) ]--------------------" << endl;
    cout << "x = " << x << endl;
    cout << "u = sqrt(" << x << ") = " << u << endl;
    double y = heronSqrt(x);
    cout << "y = heronSqrt(" << x << ") = " << y << endl;
    cout << "y*y = " << y*y << endl;
    cout << endl;

    cout << "[ Testing newtonCbrt(double) ]--------------------" << endl;
    x = -216.0;
    cout << "x = " << x << endl;
    cout << "-exp(log(-x)/3.0) = " << -exp(log(-x)/3.0) << endl;
    double w = newtonCbrt(x);
    cout << "w = newtonCbrt(" << x << ") = " << w << endl;
    cout << "w*w*w = " << w*w*w << endl;
    cout << endl;

    x = 729000000000.0;
    cout << "x = " << x << endl;
    cout << "exp(log(x)/3.0) = " << exp(log(x)/3.0) << endl;
    w = newtonCbrt(x);
    cout << "w = newtonCbrt(" << x << ") = " << w << endl;
    cout << "w*w*w = " << w*w*w << endl;
    cout << endl;

    cout << "[ Testing newtonNthRoot(int, double) ]--------------------" << endl;
    double z = newtonNthRoot(3, x);
    cout << "x = " << x << endl;
    cout << "z = newtonNthRoot(3, " << x << ") = " << z << endl;
    cout << "z*z*z = " << z*z*z << endl;
    cout << endl;

    x = 12960000000000000000.0;
    z = newtonNthRoot(4, x);
    cout << "x = " << x << endl;
    cout << "z = newtonNthRoot(4, x) = newtonNthRoot(4, " << x << ") = " << z << endl;
    cout << "z*z*z*z = " << z*z*z*z << endl;
    cout << endl;

    x = 1.0/12960000000000000000.0;
    z = newtonNthRoot(4, x);
    cout << "x = " << x << endl;
    cout << "exp(log(x)/4.0) = " << exp(log(x)/4.0) << endl;
    cout << "z = newtonNthRoot(4, x) = newtonNthRoot(4, " << x << ") = " << z << endl;
    cout << "z*z*z*z = " << z*z*z*z << endl;
    cout << endl;


    try {
        x = -4.0;
        cout << "[ Test Exception heronSqrt(double) ]--------------------" << endl;
        cout << "x = " << x << endl;
        cout << "Calculating heronSqrt(" << x << ")" << endl;
        y = heronSqrt(x);
        cout << "y = heronSqrt(" << x << ") = " << y << endl;
        cout << "y*y = " << y*y << endl;
        cout << endl;
    }
    // catch( char *str )    {
    catch( exception& ex )    {
        cout << ex.what() << endl << "Caught some exception in calculating heronSqrt(" << x << ")" << endl;
        cout << endl;
    }

    try {
        x = -4.0;
        cout << "[ Test Exception in newtonCbrt(double) ]--------------------" << endl;
        cout << "x = " << x << endl;
        cout << "Calculating newtonCbrt(" << x << ")" << endl;
        y = newtonCbrt(x);
        cout << "y = newtonCbrt(" << x << ") = " << y << endl;
        cout << "y*y*y = " << y*y*y << endl;
        cout << endl;
    }
    // catch( char *str )    {
    catch( exception& ex )    {
        cout << ex.what() << endl << "Caught some exception in calculating newtonCbrtrt(" << x <<  ")" << endl;
        cout << endl;
    }

    cout << "[ Test calculations by powering ]-----------------------------" << endl;
    x = 200.0;
    z = newtonNthRoot(10, x);
    cout << "x = " << x << endl;
    cout << "exp(log(x)/10.0) = " << exp(log(x)/10.0) << endl;
    cout << "z = newtonNthRoot(10, x) = newtonNthRoot(10, " << x << ") = " << z << endl;
    cout << "pow(z, 10) = " << pow(z, 10) << endl;
    cout << endl;

    x = 3001.0;
    z = newtonNthRoot(99, x);
    cout << "x = " << x << endl;
    cout << "exp(log(x)/99.0) = " << exp(log(x)/99.0) << endl;
    cout << "z = newtonNthRoot(99, x) = newtonNthRoot(99, " << x << ") = " << z << endl;
    cout << "pow(z, 99) = " << pow(z, 99) << endl;
    cout << endl;

    x = 3001.0;
    z = newtonNthRoot(-99, x);
    cout << "x = " << x << endl;
    cout << "exp(log(x)/-99.0) = " << exp(log(x)/-99.0) << endl;
    cout << "z = newtonNthRoot(-99, x) = newtonNthRoot(-99, " << x << ") = " << z << endl;
    cout << "1.0/pow(z, 99) = " << 1.0/pow(z, 99) << endl;
    cout << endl;

    cout << "2.1**2.1 = pow(2.1, 2.1) = "  << pow(2.1, 2.1) << endl;
    cout << "2.1**(-2.1) = pow(2.1, -2.1) = "  << pow(2.1, -2.1) << endl;
    cout << "2.1**2.1 * 2.1**(-2.1) = pow(2.1, 2.1) * pow(2.1, -2.1) = "  << pow(2.1, 2.1)*pow(2.1, -2.1) << endl;
    cout << "2.1**2.1 = exp(2.1*log(2.1)) = "  << exp(2.1*log(2.1)) << endl;
    cout << "2.1**(-2.1) = exp(-2.1*log(2.1)) = "  << exp(-2.1*log(2.1)) << endl;
    cout << "2.1**2.1 * 2.1**(-2.1) = exp(2.1*log(2.1)) * exp(-2.1*log(2.1)) = "  << exp(2.1*log(2.1)) * exp(-2.1*log(2.1)) << endl;
    cout << endl;


    int k = 301;
    x = -1.029;
    double t1 = nPow(x, k);
    double t2 = gPow(x, k);
    double t3 = mPow(x, k);
    cout << "t1 = nPow(" << x << ", " << k << ") = " << t1 << endl;
    cout << "t2 = gPow(" << x << ", " << k << ") = " << t2 << endl;
    cout << "t3 = mPow(" << x << ", " << k << ") = " << t3 << endl;
    cout << "t1 / t2 = " << (t1 / t2) << endl;
    cout << "t1 - t2 = " << (t1 - t2) << endl;
    cout << "t1 == t2 ? " << ((t1 == t2) ? "yes" : "no") << endl;
    cout << "t1 / t3 = " << (t1 / t3) << endl;
    cout << "t1 - t3 = " << (t1 - t3) << endl;
    cout << "t1 == t3 ? " << ((t1 == t3) ? "yes" : "no") << endl;
    cout << "t2 / t3 = " << (t2 / t3) << endl;
    cout << "t2 - t3 = " << (t2 - t3) << endl;
    cout << "t2 == t3 ? " << ((t2 == t3) ? "yes" : "no") << endl;
    cout << endl;

    cout << "Done." << endl;
}

/*
Output:
[ Testing heronSqrt(double) ]--------------------
x = 16
u = sqrt(16) = 4
y = heronSqrt(16) = 4
y*y = 16

[ Testing newtonCbrt(double) ]--------------------
x = -216
-exp(log(-x)/3.0) = -6.000000000000001
w = newtonCbrt(-216) = -6
w*w*w = -216

x = 729000000000
exp(log(x)/3.0) = 9000.000000000004
w = newtonCbrt(729000000000) = 9000
w*w*w = 729000000000

[ Testing newtonNthRoot(int, double) ]--------------------
x = 729000000000
z = newtonNthRoot(3, 729000000000) = 9000
z*z*z = 729000000000

x = 1.296e+019
z = newtonNthRoot(4, x) = newtonNthRoot(4, 1.296e+019) = 60000
z*z*z*z = 1.296e+019

x = 7.716049382716049e-020
exp(log(x)/4.0) = 1.666666666666666e-005
z = newtonNthRoot(4, x) = newtonNthRoot(4, 7.716049382716049e-020) = 1.666666666
666667e-005
z*z*z*z = 7.716049382716052e-020

[ Test Exception heronSqrt(double) ]--------------------
x = -4
Calculating heronSqrt(-4)
Not defined calculation exception
Caught some exception in calculating heronSqrt(-4)

[ Test Exception in newtonCbrt(double) ]--------------------
x = -4
Calculating newtonCbrt(-4)
y = newtonCbrt(-4) = -1.587401051968199
y*y*y = -3.999999999999999

[ Test calculations by powering ]-----------------------------
x = 200
exp(log(x)/10.0) = 1.698646464634247
z = newtonNthRoot(10, x) = newtonNthRoot(10, 200) = 1.698646464634247
pow(z, 10) = 199.9999999999999

x = 3001
exp(log(x)/99.0) = 1.084236189325881
z = newtonNthRoot(99, x) = newtonNthRoot(99, 3001) = 1.084236189325881
pow(z, 99) = 3000.999999999987

x = 3001
exp(log(x)/-99.0) = 0.9223082662659932
z = newtonNthRoot(-99, x) = newtonNthRoot(-99, 3001) = 0.9223082662659932
1.0/pow(z, 99) = 3001.000000000008

2.1**2.1 = pow(2.1, 2.1) = 4.749638091742242
2.1**(-2.1) = pow(2.1, -2.1) = 0.2105423572668848
2.1**2.1 * 2.1**(-2.1) = pow(2.1, 2.1) * pow(2.1, -2.1) = 0.9999999999999999
2.1**2.1 = exp(2.1*log(2.1)) = 4.749638091742242
2.1**(-2.1) = exp(-2.1*log(2.1)) = 0.2105423572668848
2.1**2.1 * 2.1**(-2.1) = exp(2.1*log(2.1)) * exp(-2.1*log(2.1)) = 1

t1 = nPow(-1.029, 301) = -5457.92801577163
t2 = gPow(-1.029, 301) = -5457.928015771692
t3 = mPow(-1.029, 301) = -5457.928015771692
t1 / t2 = 0.9999999999999887
t1 - t2 = 6.184563972055912e-011
t1 == t2 ? no
t1 / t3 = 0.9999999999999887
t1 - t3 = 6.184563972055912e-011
t1 == t3 ? no
t2 / t3 = 1
t2 - t3 = 0
t2 == t3 ? yes

Done.
*/

 

 

Posted by Scripter
,