web-dev-qa-db-ja.com

どうすれば自分でべき乗関数を書くことができますか?

私はいつも、電力を計算する関数をどのように作ることができるのだろうと思っていました(例えば、23) 私自身。ほとんどの言語では、これらは主にpow(double x, double y)として標準ライブラリに含まれていますが、どのように自分で記述できますか?

私はfor loops、しかし、私の脳はループに入ったと思う(5のような非整数指数で力を出したいとき)4.5 またはネガ2-21)そして私は夢中になった;)

それで、実数のべき乗を計算する関数をどのように書くことができますか?ありがとう


ああ、おそらく重要なことに注意してください:パワーを使用する関数(たとえば、exp)は使用できません。

49
user142019

負の力は問題ではなく、正の力の逆(_1/x_)にすぎません。

浮動小数点のべき乗はもう少し複雑です。ご存じのように、分数の累乗はルート(例:x^(1/2) == sqrt(x))と同等であり、同じ基数で累乗することは指数を加算することと同等であることも知っています。

上記のすべてで、次のことができます。

  • 整数部と有理部の指数を分解する
  • ループを使用して整数のべき乗を計算します(因子で分解し、部分計算を再利用して最適化できます)。
  • 任意のアルゴリズムでルートを計算します(二分法やニュートン法などの反復近似が機能します)。
  • 結果を掛けます。
  • 指数が負の場合、逆を適用します。

例:

_2^(-3.5) = (2^3 * 2^(1/2)))^-1 = 1 / (2*2*2 * sqrt(2))
_
48
fortran

AB =ログ-1(ログ(A)* B)

編集:はい、この定義は本当に有用なものを提供します。たとえば、x86では、ほぼ直接_FYL2X_(Y * Log2(X))および_F2XM1_(2バツ-1):

_fyl2x
fld st(0)
frndint
fsubr st(1),st
fxch st(1)
fchs
f2xmi
fld1
faddp st(1),st
fscale
fstp st(1) 
_

主に_F2XM1_が-1.0..1.0の範囲の数値でのみ機能するため、コードは予想より少し長くなります。 fld st(0)/frndint/fsubr st(1),stの部分は整数部分を減算するため、分数のみが残ります。それに_F2XM1_を適用し、1を追加し直し、FSCALEを使用してべき乗の整数部分を処理します。

22
Jerry Coffin

通常、数学ライブラリでのpow(double, double)関数の実装は、アイデンティティに基づいています。

pow(x,y) = pow(a, y * log_a(x))

このIDを使用すると、単一の数値aを任意の指数に累乗する方法と、対数基底aを取得する方法を知るだけで済みます。複雑な多変数関数を事実上、単一変数の2つの関数と、実装が非常に簡単な乗算に変換しました。 aの最も一般的に選択される値は、eまたは2-e^xlog_e(1+x)には非常に優れた数学的プロパティがあるため、eまたは2です。

この方法で行うことの難点は、log_a(x)項(およびyの積)を、xおよびyの浮動小数点表現よりも高い精度で計算する必要があることです(完全な精度を取得する場合)。たとえば、xyがdoubleで、高精度の結果を取得したい場合は、中間結果をより精度の高い形式で格納(および算術)するための何らかの方法を考え出す必要があります。 Intel x87形式は、64ビット整数と同様に一般的な選択です(ただし、本当に最高品質の実装が必要な場合は、96ビット整数の計算をいくつか行う必要があります。言語)。 powf(float,float)を実装すると、これに対処するのがはるかに簡単になります。これは、中間計算にdoubleを使用するだけで済むためです。このアプローチを使用する場合は、それから始めることをお勧めします。


私が概説したアルゴリズムは、powを計算する唯一の方法ではありません。これは、固定のapriori精度の範囲を満たす高速な結果を配信するのに最も適しているだけです。他のコンテキストではあまり適していないため、他の人が提案している繰り返し平方[ルート]アルゴリズムよりも実装がはるかに困難です。

繰り返しsquare [root]アルゴリズムを試したい場合は、繰り返しの2乗のみを使用する符号なし整数べき関数を書くことから始めます。その縮小されたケースのアルゴリズムを十分に理解したら、小数指数を処理するためにアルゴリズムを拡張することはかなり簡単です。

19
Stephen Canon

対処すべき2つの異なるケースがあります。整数指数と小数指数です。

整数の指数の場合、2乗によるべき乗を使用できます。

def pow(base, exponent):
    if exponent == 0:
        return 1
    Elif exponent < 0:
        return 1 / pow(base, -exponent)
    Elif exponent % 2 == 0:
        half_pow = pow(base, exponent // 2)
        return half_pow * half_pow
    else:
        return base * pow(base, exponent - 1)

2番目の「Elif」は、これをナイーブパウ関数と区別するものです。これにより、関数はO(n)の代わりにO(log n)の再帰呼び出しを行うことができます。

分数指数の場合、アイデンティティa ^ b = C ^(b * log_C(a))を使用できます。 C = 2を取ると便利なので、a ^ b = 2 ^(b * log2(a))になります。これにより、2 ^ xおよびlog2(x)の関数を記述する問題が軽減されます。

C = 2を使用するのが便利な理由は、浮動小数点数が基数2の浮動小数点に格納されるためです。 log2(a * 2 ^ b)= log2(a)+ bこれにより、log2関数の作成が簡単になります。間隔[1、2)だけで、すべての正数に対して正確である必要はありません。同様に、2 ^ xを計算するには、2 ^(xの整数部)* 2 ^(xの小数部)を掛けます。最初の部分は浮動小数点数に格納するのは簡単ですが、2番目の部分では、区間[0、1)で2 ^ x関数が必要です。

難しいのは、2 ^ xとlog2(x)の適切な近似値を見つけることです。簡単なアプローチは、 Taylor series を使用することです。

9
dan04

定義ごと:

a ^ b = exp(b ln(a))

ここでexp(x) = 1 + x + x^2/2 + x^3/3! + x^4/4! + x^5/5! + ...

ここで_n! = 1 * 2 * ... * n_。

実際には、_1/n!_の最初の10個の値のarrayを保存してから、おおよそ

exp(x) = 1 + x + x^2/2 + x^3/3! + ... + x^10/10!

なぜなら10!膨大な数なので、1/10!非常に小さい(2.7557319224⋅10^ -7)。

7

正の整数の累乗については、 二乗によるべき乗 および 加算連鎖べき乗 を参照してください。

4
Richie Cotton

Wolfram関数 は、べき乗を計算するためのさまざまな式を提供します。それらのいくつかは、実装が非常に簡単です。

3つの自己実装関数iPow(x, n)Ln(x)およびExp(x)を使用すると、fPow(x, a)、xおよびaを計算できますdoubles。以下の関数はどちらもライブラリ関数を使用せず、反復のみを使用します。

実装された機能に関する説明:

(1)iPow(x, n):xはdouble、nはintです。 nは整数であるため、これは単純な反復です。

(2)Ln(x):この関数は、テイラー級数の反復を使用します。反復で使用されるシリーズはΣ (from int i = 0 to n) {(1 / (2 * i + 1)) * ((x - 1) / (x + 1)) ^ (2 * n + 1)}です。シンボル^は、単純な反復を使用する1番目の関数に実装されているべき関数Pow(x, n)を示します。

(3)Exp(x):この関数も、テイラー級数の反復を使用します。反復で使用されるシリーズはΣ (from int i = 0 to n) {x^i / i!}です。ここで、^はべき乗関数を示しますが、1番目のPow(x, n)関数を呼び出して計算されたnotです。代わりに、d *= x / iを使用して、階乗と同時に3番目の関数内に実装されます。 私はこのトリックを使用しなければならなかったと感じました。なぜなら、この関数では、反復が他の関数に比べてさらにいくつかのステップを取り、階乗(i!)がほとんどの場合オーバーフローするからです。反復がオーバーフローしないようにするために、この部分のべき関数は階乗と同時に反復されます。この方法で、私はオーバーフローを克服しました。

(4)fPow(x, a)xとaは両方ともdouble。この関数は、上記で実装した他の3つの関数を呼び出すだけです。この関数の主なアイデアは、いくつかの計算に依存します:fPow(x, a) = Exp(a * Ln(x))。そして今、私はすべての関数iPowLn、およびExpをすでに繰り返し処理しています。

n.b。繰り返しを停止するステップを決定するためにconstant MAX_DELTA_DOUBLEを使用しました。私はそれを1.0E-15に設定しましたが、これはダブルにとっては妥当と思われます。したがって、(delta < MAX_DELTA_DOUBLE)の場合は反復が停止します。さらに精度が必要な場合は、long doubleを使用して、MAX_DELTA_DOUBLEの定数値を、たとえば1.0E-18に減らすことができます(1.0E-18が最小です)。

ここにコードがあります。

#define MAX_DELTA_DOUBLE 1.0E-15
#define EULERS_NUMBER 2.718281828459045

double MathAbs_Double (double x) {
    return ((x >= 0) ? x : -x);
}

int MathAbs_Int (int x) {
    return ((x >= 0) ? x : -x);
}

double MathPow_Double_Int(double x, int n) {
    double ret;
    if ((x == 1.0) || (n == 1)) {
        ret = x;
    } else if (n < 0) {
        ret = 1.0 / MathPow_Double_Int(x, -n);
    } else {
        ret = 1.0;
        while (n--) {
            ret *= x;
        }
    }
    return (ret);
}

double MathLn_Double(double x) {
    double ret = 0.0, d;
    if (x > 0) {
        int n = 0;
        do {
            int a = 2 * n + 1;
            d = (1.0 / a) * MathPow_Double_Int((x - 1) / (x + 1), a);
            ret += d;
            n++;
        } while (MathAbs_Double(d) > MAX_DELTA_DOUBLE);
    } else {
        printf("\nerror: x < 0 in ln(x)\n");
        exit(-1);
    }
    return (ret * 2);
}

double MathExp_Double(double x) {
    double ret;
    if (x == 1.0) {
        ret = EULERS_NUMBER;
    } else if (x < 0) {
        ret = 1.0 / MathExp_Double(-x);
    } else {
        int n = 2;
        double d;
        ret = 1.0 + x;
        do {
            d = x;
            for (int i = 2; i <= n; i++) {
                d *= x / i;
            }
            ret += d;
            n++;
        } while (d > MAX_DELTA_DOUBLE);
    }
    return (ret);
}

double MathPow_Double_Double(double x, double a) {
    double ret;
    if ((x == 1.0) || (a == 1.0)) {
        ret = x;
    } else if (a < 0) {
        ret = 1.0 / MathPow_Double_Double(x, -a);
    } else {
        ret = MathExp_Double(a * MathLn_Double(x));
    }
    return (ret);
}
2
ssd

次のようなpow関数を見つけることができます:

static double pows (double p_nombre, double p_puissance)
{
    double nombre   = p_nombre;
    double i=0;
    for(i=0; i < (p_puissance-1);i++){
          nombre = nombre * p_nombre;
       }
    return (nombre);
}

次のようなfloor関数を見つけることができます:

static double floors(double p_nomber)
{
    double x =  p_nomber;
    long partent = (long) x; 

    if (x<0)
    {
        return (partent-1);
    }
    else
    {
        return (partent);
    }
}

宜しくお願いします

1
che.moor

これは興味深い演習です。以下にいくつかの提案を示します。この順序で試してください。

  1. ループを使用します。
  2. 再帰を使用する(より良くはありませんが、それでもなお興味深い)
  3. 分割統治法を使用して再帰を大幅に最適化する
  4. 対数を使用する
1
Wouter Lievens

正の整数のべき乗を効率的に計算するためのより良いアルゴリズムは、余剰の被乗数を追跡しながら基数を繰り返し二乗することです。以下にPythonのサンプルソリューションを示します。これは比較的簡単に理解でき、好みの言語に翻訳できるはずです。

def power(base, exponent):
  remaining_multiplicand = 1
  result = base

  while exponent > 1:
    remainder = exponent % 2
    if remainder > 0:
      remaining_multiplicand = remaining_multiplicand * result
    exponent = (exponent - remainder) / 2
    result = result * result

  return result * remaining_multiplicand

負の指数を処理するには、正のバージョンを計算し、結果で1を除算するだけです。したがって、上記のコードを簡単に修正する必要があります。分数指数は、基本的にnの根の計算(n = 1/abs(exponent % 1))を意味し、その結果に整数部のべき乗計算の結果を乗算することを意味するため、かなり困難です。

power(base, exponent - (exponent % 1))

ニュートン法を使用して、希望する精度レベルまで根を計算できます。 アルゴリズムに関するウィキペディアの記事 をご覧ください。

1
Kent

私は固定小数点の長い演算を使用しており、私のパワーはlog2/exp2ベースです。番号の構成:

  • _int sig = { -1; +1 }_ signum
  • _DWORD a[A+B]_番号
  • Aは、数値の整数部のDWORDsの数です
  • Bは小数部のDWORDsの数です

私の簡略化されたソリューションはこれです:

_//---------------------------------------------------------------------------
longnum exp2 (const longnum &x)
{
    int i,j;
    longnum c,d;
    c.one();
    if (x.iszero()) return c;
    i=x.bits()-1;
    for(d=2,j=_longnum_bits_b;j<=i;j++,d*=d)
    if (x.bitget(j))
    c*=d;
    for(i=0,j=_longnum_bits_b-1;i<_longnum_bits_b;j--,i++)
    if (x.bitget(j))
    c*=_longnum_log2[i];
    if (x.sig<0) {d.one(); c=d/c;}
    return c;
}
//---------------------------------------------------------------------------
longnum log2 (const longnum &x)
{
    int i,j;
    longnum c,d,dd,e,xx;
    c.zero(); d.one(); e.zero(); xx=x;
    if (xx.iszero()) return c; //**** error: log2(0) = infinity
    if (xx.sig<0) return c; //**** error: log2(negative x) ... no result possible
    if (d.geq(x,d)==0) {xx=d/xx; xx.sig=-1;}
    i=xx.bits()-1;
    e.bitset(i); i-=_longnum_bits_b;
    for (;i>0;i--,e>>=1) // integer part
    {
        dd=d*e;
        j=dd.geq(dd,xx);
        if (j==1) continue; // dd> xx
        c+=i; d=dd;
        if (j==2) break; // dd==xx
    }
    for (i=0;i<_longnum_bits_b;i++) // fractional part
    {
        dd=d*_longnum_log2[i];
        j=dd.geq(dd,xx);
        if (j==1) continue; // dd> xx
        c.bitset(_longnum_bits_b-i-1); d=dd;
        if (j==2) break; // dd==xx
    }
    c.sig=xx.sig;
    c.iszero();
    return c;
}
//---------------------------------------------------------------------------
longnum pow (const longnum &x,const longnum &y)
{
    //x^y = exp2(y*log2(x))
    int ssig=+1; longnum c; c=x;
    if (y.iszero()) {c.one(); return c;} // ?^0=1
    if (c.iszero()) return c; // 0^?=0
    if (c.sig<0)
    {
        c.overflow(); c.sig=+1;
        if (y.isreal()) {c.zero(); return c;} //**** error: negative x ^ noninteger y
        if (y.bitget(_longnum_bits_b)) ssig=-1;
    }
    c=exp2(log2(c)*y); c.sig=ssig; c.iszero();
    return c;
}
//---------------------------------------------------------------------------
_

ここで:

__longnum_bits_a = A*32
_longnum_bits_b = B*32
_longnum_log2[i] = 2 ^ (1/(2^i))  ... precomputed sqrt table 
_longnum_log2[0]=sqrt(2)  
_longnum_log2[1]=sqrt[tab[0]) 
_longnum_log2[i]=sqrt(tab[i-1])
longnum::zero() sets *this=0
longnum::one() sets *this=+1
bool longnum::iszero() returns (*this==0)
bool longnum::isnonzero() returns (*this!=0)
bool longnum::isreal() returns (true if fractional part !=0)
bool longnum::isinteger() returns (true if fractional part ==0)
int longnum::bits() return num of used bits in number counted from LSB
longnum::bitget()/bitset()/bitres()/bitxor() are bit access
longnum.overflow() rounds number if there was a overflow X.FFFFFFFFFF...FFFFFFFFF??h  -> (X+1).0000000000000...000000000h
int longnum::geq(x,y)  is comparition |x|,|y| returns 0,1,2 for (<,>,==)
_

このコードを理解するために必要なのは、バイナリ形式の数値が2の累乗の合計で構成されることです。2^ numを計算する必要がある場合は、次のように書き換えることができます。

  • 2^(b(-n)*2^(-n) + ... + b(+m)*2^(+m))

ここで、nは小数ビットであり、mは整数ビットです。バイナリ形式の_2_による乗算/除算は単純なビットシフトであるため、すべてをまとめると__に似た_exp2_のコードが得られます。 _log2_は、binaru検索に基づいています...検索された値と一致するまで結果ビットをMSBからLSBに変更します(高速sqrt計算と非常に類似したアルゴリズム)。これが物事を明確にするのに役立つことを願っています...

0
Spektre

他の回答には多くのアプローチが示されています。積分力の場合に役立つと思ったものを以下に示します。

nの整数累乗xの場合バツ、単純なアプローチではx-1の乗算が必要です。これを最適化するために、動的プログラミングを使用して以前の乗算結果を再利用し、すべてのx乗算を回避できます。たとえば、59、たとえば、3のバッチを作成できます。つまり、5を計算します。3 一度、同じロジックを使用して125を取得し、次にキューブ125を取得します。プロセスでは、単純な方法で8回乗算するのではなく、4回の乗算を実行します。

問題は、乗算の数が最小になるように、バッチbの理想的なサイズはどれくらいかということです。それでは、この方程式を書きましょう。 f(x、b)がnの計算に伴う乗算の数を表す関数である場合バツ 上記の方法を使用して、

> f(x,b</a loading= (x/b - 1) + (b-1)">)==

説明:p個の数字のバッチの積は、p-1個の乗算を取ります。 x個の乗算をb個のバッチに分割すると、各バッチ内で(x/b)-1個の乗算が必要になり、すべてのb個のバッチにb-1個の乗算が必要になります。

これで、bに関するこの関数の1次導関数を計算し、0に等しくして、乗算の最小数でbを取得できます。

f'(x,b</a loading= -x/b<sup>2</sup> + 1 = 0">)==

enter image description here

次に、このbの値を関数f(x、b)に戻し、乗算の数を最小にします。

enter image description here

すべての正のxについて、この値は単純な方法による乗算よりも小さくなります。

0
zafar142003