web-dev-qa-db-ja.com

C ++の整数の累乗

pow(a,b)から結果を整数として取得する必要があります(aとbは両方とも整数です)。現在、_(int) pow( (double)a, (double)b)_が含まれている計算は間違っています。たぶん誰かが整数でpow(a、b)を実行し、整数も返す関数を手伝ってくれるでしょうか?

しかし、ここに奇妙な部分があります。私は、Geany(およびg ++/gccコンパイラ)を使用してLinuxでスクリプトを作成し、スクリプトをpow(a,b)だけコンパイルして正常に動作させました。しかし、大学にはDev-C++(およびMS Windows)があります。 Dev-C++では、スクリプトは_[Warning] converting to_からのエラー_double'_ int 'でコンパイルされませんでした

このscrpitをWindows(およびMingwコンパイラ)でも機能させる必要があります。

15
skazhy

自慢できる素敵な再帰的アプローチ:

int myPow(int x, int p) {
  if (p == 0) return 1;
  if (p == 1) return x;
  return x * myPow(x, p-1);
}
0
Zed

Zedよりも優れた再帰的アプローチ。

int myPow(int x, int p)
{
  if (p == 0) return 1;
  if (p == 1) return x;

  int tmp = myPow(x, p/2);
  if (p%2 == 0) return tmp * tmp;
  else return x * tmp * tmp;
}

O(p)の代わりにO(log²(p))の方がはるかに複雑です。

57
Matthieu M.

または、テンプレートメタプログラミングを少し使用することもできます:)

template<int X, int P>
struct Pow
{
    enum { result = X*Pow<X,P-1>::result };
};
template<int X>
struct Pow<X,0>
{
    enum { result = 1 };
};
template<int X>
struct Pow<X,1>
{
    enum { result = X };
};

int main()
{
    std::cout << "pow(3,7) is " << Pow<3,7>::result << std::endl;
    return 0;   
}

評価はコンパイル時に行われるため、このコードは最も複雑ですO(1)。もちろん、これは整数値でのみ機能します。ただし、この機能は完全性(および楽しみ)のためにのみ提供されています。

18
fmuecke

主にZedsの単純な再帰への応答...

再帰が反復よりも優れていると想定されるのはなぜですか?特にC++では。どうしたの...

int myPow (int x, int p) {
  int i = 1;
  for (int j = 1; j <= p; j++)  i *= x;
  return i;
}

私はあなたの答えが間違っているとか、何らかの形で悪いと言っているのではありません-それはあなたがそれが良いと思う印象を受けたというだけですなぜならそれは再帰的です。 IMO、特にC++では、そのバイアスにより、プログラムが遅くなり、壊れることさえあります。巨大なスタックを増やしているため、プログラムが遅くなり、キャッシュと仮想メモリのページングが発生します。反復ソリューションが機能するスタックオーバーフローが発生するため、プログラムが壊れています。

一部の人はあなたの答えを見て、それが末尾再帰であり、とにかく反復に最適化されると思います。もちろん、それは真実ではありません-各再帰呼び出しが終了した後、まだ実行する乗算があるので、末尾再帰ではありません。重要なのは、C++では、コンパイラーがそれらを実行したとしても、末尾再帰の最適化を妨げるより微妙なことがたくさんあるということです。例えば...

void myrecurse (plan *p)
{
  plan i;
  i.prev = p;
  //  more plan setup, checks, and special case handling

  myrecurse (&i);
}

この場合、すべての「計画」インスタンスはスタックに残っている必要があります。したがって、スタックフレームを破棄することはできません。したがって、再帰呼び出しの後に実行される操作が正確にゼロであっても、これは反復に最適化できません。計画はPOD構造体であると想定されているため、デストラクタのクリーンアップのような隠された操作すらありません。

ちなみに、これは私が実際のコードで行ったことに基づいています-再帰中に計画されたデータ構造操作ですが、再帰がルート/リーフに到達するまで元のノードでは何も変更されず、必要なすべての新しいノードは正常に完了しています割り当てられ、すべてのロックが取得され、悪化するような再帰はありません。その時点で、変更をコミットするために、プランインスタンスのリンクリストを介して反復が実行されます。ロジックは、再帰呼び出しの巻き戻しに関連するフラグメントに分割されるよりも、反復として明確でした。

ここでのポイントは、再帰が自動的に悪いと主張することではないことは明らかです。デフォルトでは再帰が反復よりも優れていると人々が思っているように見えると、私は緊張します。

14
Steve314

末尾再帰関数が最適ではないでしょうか。何かのようなもの:

int myPow_helper(int x, int p, int result) {
   if (p == 0) {
      return result;
   } else {
      return myPow_helper(x, p-1, result*x);
   }
}

int myPow(int x, int p) {
   return myPow_helper(x, p, 1);
}
3
Ian Burris

あなたの宿題は、積分指数関数を書くことだと思います。まず、指数とは何かを見てみましょう。

http://en.wikipedia.org/wiki/Exponent

次に、教科書でCで数値を乗算する方法を調べます。forループを使用することをお勧めします。

3
John Millikin

Doubleを(int) pow((double)a, (double)b)行のintにキャストする代わりに、powの結果を丸めてから、必要に応じてintにキャストしてください。

特に結果が1つずれている場合は、切り捨てるときに浮動小数点の問題の1つになる可能性があります。

2
Calyth

バイナリパワー、別名 二乗による指数化

int powi (int base, unsigned int exp)
{
    int res = 1;
    while (exp) {
        if (exp & 1)
            res *= base;
        exp >>= 1;
        base *= base;
    }
    return res;
}

これはpowi(0,0)に対して1を返すことに注意してください。

2
jdh8

C++標準にはint pow(int, int)がありません(double pow(double, int)float ...があります)。 Microsoftのcmathは、ipowを持たないCmath.hを使用します。一部のcmathヘッダーは、powのテンプレートバージョンを定義します。

$ cat main.cpp
#include <cmath>

int main() {
  std::pow(2,2);
}

$ gcc main.cpp # this cmath has template pow
...snip... std::pow<int, int>(int, int)]+0x16): undefined reference to `pow'
collect2: ld returned 1 exit status
1 ;( user@Host:
$ gcc main.cpp -lm

Google Codeで function:ipow lang:c ++ を検索します。

最初のリンクの例を次に示します。

template <typename Type1, typename Type2>
Type1 ipow(Type1 a, Type2 ex)
// Return a**ex
{
    if ( 0==ex )  return 1;
    else
    {
        Type1 z = a;
        Type1 y = 1;
        while ( 1 )
        {
            if ( ex & 1 )  y *= z;
            ex /= 2;
            if ( 0==ex )  break;
            z *= z;
        }
        return y;
    }
}

C++コードで整数の累乗(平方、立方体など)を計算する を参照してください。

1
jfs

なぜ直線的に?対数で試してみてください!

long long powx( int val, int exp )
{
    long long actual = val;
    long long prod = 1;
    int i;

    for ( i = 0; i < 32; i++ )
    { 
        if ( exp & 0x1 )
        {
            prod *= actual;
        }

        exp >>= 1;

        actual *= actual;
    }

    return prod;
}
0
Judegar

ここには2つの選択肢があります。power(a、n)をカウントする場合、非常に短く、O(logn)時間で機能しますが、再帰的であるため、作成する必要があるコードを記述できます。呼び出しごとに新しいスタックフレームがあり、ループの反復よりも少し時間が必要です。短いコードは次のとおりです。

int power(int a, int n){
    if(n == 0) return 1;
    int keep = power(a,n/2);
    if(n & 1) return keep*keep*a;   // (n & 1) is and operation of 1 and the 
    return keep*keep;               // last bit of n
}

より高速なコードについては、ここではwhileループを使用しています。

int power(int a, int n) {
    int res = 1;
    while (n) {
        if (n & 1)
            res *= a;
        a *= a;
        n >>= 1;
    }
    return res;
}
0
Mamuka