web-dev-qa-db-ja.com

C ++で立方根を見つけますか?

数の立方根を見つけようとすると、奇妙なことが起こります。

次のコードは私に未定義を返します。 cmdの場合:-1。#IND

cout<<pow(( double )(20.0*(-3.2) + 30.0),( double )1/3)

これは完全に正常に動作しますが。 cmdの場合:4.93242414866094

cout<<pow(( double )(20.0*4.5 + 30.0),( double )1/3)

負の数から立方根を持つことができるので、数学的な方法からそれは機能しなければなりません。 Powは、Visual C++ 2010math.hライブラリからのものです。何か案は?

11
ilcredo

_<cmath>_のpow(x, y)は、xが負で、yが非整数の場合は機能しません。

これは、C標準および cppreference に記載されているように、_std::pow_の制限です。

エラー処理

  • エラーはmath_errhandlingで指定されたとおりに報告されます
  • baseが有限で負で、expが有限で非整数の場合、ドメインエラーが発生し、範囲エラーが発生する可能性があります。
  • Baseがゼロでexpがゼロの場合、ドメインエラーが発生する可能性があります。
  • 底がゼロでexpが負の場合、ドメインエラーまたは極エラーが発生する可能性があります。

この制限を回避するには、いくつかの方法があります。

  • キューブルート化は、何かを1/3の累乗にすることと同じであるため、std::pow(x, 1/3.)を実行できます。

  • C++ 11では、 _std::cbrt_ を使用できます。 C++ 11では、平方根関数と立方根関数の両方が導入されましたが、_std::pow_の制限を克服する一般的なn乗根関数はありませんでした。

16
wkl

パワー_1/3_は特別な場合です。一般に、負の数の非整数乗は複雑です。 powが整数の根のような特殊なケースをチェックすることは実用的ではありません。さらに、doubleとしての_1/3_は正確に1/3ではありません!

Visual C++のパワーについてはわかりませんが、私のマニュアルページにはエラーが表示されています。

EDOM引数xは負であり、yは整数値ではありません。これにより、複素数になります。

負の数の立方根が必要な場合は、より特殊な立方根関数を使用する必要があります。または、角を切り、絶対値を取り、次に立方根を取り、次に符号を乗算します。

コンテキストによっては、負の数xの_1/3_乗は、必ずしも期待する負の立方根ではないことに注意してください。簡単に最初の複素根x^(1/3) * e^(pi*i/3)になる可能性があります。これは数学が使用する慣習です。未定義だと言うのも合理的です。

8
Cascabel

(-1)^ 3 = -1の場合、単純に負の数の有理数を取り、実際の応答を期待することはできません。これは、この有理指数には、本質的に虚数である他の解決策があるためです。
http://www.wolframalpha.com/input/?i=x^(1/3)、+ x + from + -5 + to +

同様に、x ^ xをプロットします。 x = -1/3の場合、これには解決策があります。ただし、この関数は、x <0のRでは未定義と見なされます。

したがって、math.hが非効率になるような魔法を実行することを期待せず、自分で記号を変更するだけです。

7
buddhabrot

ネガを取り出して、後で入れないといけないと思います。本当に必要な場合は、ラッパーにこれを実行させることができます。

function yourPow(double x, double y)
{
    if (x < 0)
        return -1.0 * pow(-1.0*x, y);
    else
        return pow(x, y);
}
3
jon_darkstar

pow( x, y )exp( y * log( x ) )と同じです(つまり、同等です)

log(x)が無効な場合、pow(x、y)も無効です。

同様に、数学的には0である必要がありますが、0の累乗を実行することはできません。

2
CashCow

(double)を使用してdoubleにキャストしないでください。代わりに、二重数値定数を使用してください。

double thingToCubeRoot = -20.*3.2+30;
cout<< thingToCubeRoot/fabs(thingToCubeRoot) * pow( fabs(thingToCubeRoot), 1./3. );

トリックを行う必要があります!

また、C++プロジェクトに<math.h>を含めないでください。代わりに、<cmath>を使用してください。

または、buddhabrotが述べた理由により、<complex>ヘッダーからpowを使用します

2
rubenvb

C++ 11にはcbrt関数があります(たとえば http://en.cppreference.com/w/cpp/numeric/math/cbrt を参照)。あなたは次のようなものを書くことができます

_#include <iostream>
#include <cmath>

int main(int argc, char* argv[])
{
   const double arg = 20.0*(-3.2) + 30.0;
   std::cout << cbrt(arg) << "\n";
   std::cout << cbrt(-arg) << "\n";
   return 0;
}
_

私はC++標準にアクセスできないので、負の引数がどのように処理されるのかわかりません... ideone http://ideone.com/bFlXYs でのテストは_x<0_の場合、C++(gcc-4.8.1)がこのルールcbrt(x)=-cbrt(-x)で立方根を拡張することを確認します。この拡張機能については、 http://mathworld.wolfram.com/CubeRoot.html を参照してください。

2

キュビットルートを探していたところ、このスレッドが見つかりました。次のコードが機能する可能性があります。

#include <cmath>
using namespace std;

function double nth-root(double x, double n){
    if (!(n%2) || x<0){
        throw FAILEXCEPTION(); // even root from negative is fail
    }

    bool sign = (x >= 0);

    x = exp(log(abs(x))/n);

    return sign ? x : -x;
}
1
guinny

これが私がノックアップした小さな関数です。

#define uniform() (Rand()/(1.0 + Rand_MAX))

double CBRT(double Z)
{
    double guess = Z;
    double x, dx;
    int loopbreaker;

retry:
    x = guess * guess * guess;
    loopbreaker = 0;
    while (fabs(x - Z) > FLT_EPSILON)
    {
        dx = 3 * guess*guess;
        loopbreaker++;
        if (fabs(dx) < DBL_EPSILON || loopbreaker > 53)
        {
            guess += uniform() * 2 - 1.0;
            goto retry;
        }
        guess -= (x - Z) / dx;
        x = guess*guess*guess;
    }

    return guess;
}

ニュートンラプソン法を使用して立方根を見つけます。

ニュートン-ラフソンがスタックすることがあります。ルートが0に非常に近い場合、導関数が大きくなり、振動する可能性があります。そのため、それが発生した場合は、クランプして強制的に再起動しました。より高い精度が必要な場合は、FLT_EPSILONを変更できます。

0
Malcolm McLean

べき乗と数値のn乗根を混同しないでください。古き良き時代を見る ウィキペディア

0
Luca Martini

数学ライブラリがない場合は、次の方法で立方根を計算できます。

立方根

double curt(double x) {
  if (x == 0) {
    // would otherwise return something like 4.257959840008151e-109
    return 0;
  }
  double b = 1; // use any value except 0
  double last_b_1 = 0;
  double last_b_2 = 0;
  while (last_b_1 != b && last_b_2 != b) {
    last_b_1 = b;
    // use (2 * b + x / b / b) / 3 for small numbers, as suggested by  willywonka_dailyblah
    b = (b + x / b / b) / 2;
    last_b_2 = b;
    // use (2 * b + x / b / b) / 3 for small numbers, as suggested by  willywonka_dailyblah
    b = (b + x / b / b) / 2;
  }
  return b;
}

これは、以下のsqrtアルゴリズムから派生しています。アイデアは、bx / b / bxの立方根からどんどん大きくなります。したがって、両方の平均はxの立方根に近くなります。

平方根と立方根(Pythonの場合)

def sqrt_2(a):
    if a == 0:
        return 0
    b = 1
    last_b = 0
    while last_b != b:
        last_b = b
        b = (b + a / b) / 2
    return b

def curt_2(a):
    if a == 0:
        return 0
    b = a
    last_b_1 = 0;
    last_b_2 = 0;
    while (last_b_1 != b and last_b_2 != b):
        last_b_1 = b;
        b = (b + a / b / b) / 2;
        last_b_2 = b;
        b = (b + a / b / b) / 2;
    return b

平方根とは対照的に、last_b_1およびlast_b_2は、bがちらつくため、立方根に必要です。これらのアルゴリズムを変更して、4番目のルート、5番目のルートなどを計算できます。

sqrtのこのアルゴリズムを教えてくれた11年生の数学の先生HerrBrennerに感謝します。

パフォーマンス

私は16MHzのクロック周波数でArduinoでそれをテストしました:

0
User

1/3は整数と見なされるため、常に0を返すため... 1.0/3.0で試してください...それは私が思うことですが、実装してみてください...そして1.0と3.0を含む変数を宣言することを忘れないでくださいダブルとして...

0
karan