web-dev-qa-db-ja.com

再帰を使用したべき関数

Javaでパワーメソッドを書かなければなりません。 2つの整数を受け取り、それらが正の数であるか負の数であるかは関係ありません。 O(logN)の複雑さを持っている必要があります。また、再帰を使用する必要があります。私の現在のコードは2つの数値を取得しますが、出力し続ける結果はゼロであり、その理由を理解できません。

import Java.util.Scanner;

public class Powers {

    public static void main(String[] args) {
        float a;
        float n;
        float res;

        Scanner in = new Scanner(System.in);
        System.out.print("Enter int a ");
        a = in.nextFloat();

        System.out.print("Enter int n ");
        n = in.nextFloat();

        res = powers.pow(a, n);

        System.out.print(res);
    }

    public static float pow(float a, float n) {
        float result = 0;

        if (n == 0) {
            return 1;
        } else if (n < 0) {
            result = result * pow(a, n + 1);
        } else if (n > 0) {
            result = result * pow(a, n - 1);
        }

        return result;
    }
}
8
Yaboy93

いくつかの数学の事実から始めましょう:

  • 正のnの場合、aⁿ=a⨯a⨯…⨯an回
  • 負のnの場合、aⁿ=⅟a⁻ⁿ=⅟(a⨯a⨯…⨯a)。つまり、aをゼロにすることはできません。
  • aがゼロまたは負であっても、n = 0の場合、aⁿ= 1です。

正のnケースから始めて、そこから作業しましょう。

ソリューションを再帰的にしたいので、小さいnに基づいてaⁿを定義し、そこから作業する方法を見つける必要があります。再帰について考える一般的な方法は、n-1の解決策を見つけてそこから作業することです。

実際、aⁿ=a⨯(aⁿ⁻¹)であることは数学的に真実であるため、単純なアプローチは作成したものと非常に似ています。

_public static int pow( int a, int n) {
    if ( n == 0 ) {
        return 1;
    }
    return ( a * pow(a,n-1));
}
_

ただし、これの複雑さはO(n)です。どうして? n = 0の場合、乗算は行われません。 n = 1の場合、1つの乗算を行います。 n = 2の場合、1回の乗算であることがわかっているpow(a、1)を呼び出し、1回乗算するため、2回の乗算があります。すべての再帰ステップで1つの乗算があり、nステップあります。つまり、O(n)です。

このO(log n)を作成するには、n-1だけでなく、nのfractionにすべてのステップを適用する必要があります。ここでも、私たちを助けることができる数学の事実があります:an₁+n₂ = an₁⨯an₂

これは、aⁿをaとして計算できることを意味しますn/2⨯an/2

しかし、nが奇数の場合はどうなりますか? a⁹のようなものは4.5⨯a4.5。しかし、ここでは整数のべき乗について話しています。分数の扱いはまったく異なります。幸い、私たちはそれをa⨯a⁴⨯a⁴として定式化できます。

したがって、偶数の場合はn/2⨯an/2、奇数の場合はa⨯aを使用しますn/2⨯an/2 (整数除算、9/2 = 4を与えます)。

_public static int pow( int a, int n) {
    if ( n == 0 ) {
        return 1;
    }
    if ( n % 2 == 1 ) {
        // Odd n
        return a * pow( a, n/2 ) * pow(a, n/2 );
    } else {
        // Even n
        return pow( a, n/2 ) * pow( a, n/2 );
    }
}
_

これにより、実際には正しい結果が得られます(nが正の場合)。しかし、実際、ここでの複雑さは、ここでも、O(log nではなくO(n))です。なぜですか?パワーを2回計算しているためです。実際にそれを4と呼ぶことを意味します。再帰ステップの数は指数関数的であるため、これは、nを2で除算することによって行った節約と相殺されます。

しかし、実際には、ほんの少しの修正が必要です:

_public static int pow( int a, int n) {
    if ( n == 0 ) {
        return 1;
    }
    int powerOfHalfN = pow( a, n/2 );
    if ( n % 2 == 1 ) {
        // Odd n
        return a * powerOfHalfN * powerOfHalfN;
    } else {
        // Even n
        return powerOfHalfN * powerOfHalfN;
    }
}
_

このバージョンでは、再帰を1回だけ呼び出しています。したがって、たとえば64の累乗から、32、16、8、4、2、1を非常に迅速に取得して完了します。各ステップで1つまたは2つの乗算のみで、6つのステップしかありません。これはO(log n)です。

このすべてからの結論は次のとおりです。

  1. O(log n)を取得するには、n-1またはn-だけではなく、各ステップでnの小数部で機能する再帰が必要です。
  2. しかし、割合はストーリーの一部にすぎません。再帰を2回以上呼び出さないように注意する必要があります。1つのステップで複数の再帰呼び出しを使用すると、指数の複雑さが作成され、nの小数を使用するとキャンセルされます。

最後に、負の数を処理する準備が整いました。逆数の⅟a⁻ⁿを取得するだけです。注意すべき重要な点が2つあります。

  • ゼロによる除算を許可しないでください。つまり、a = 0を取得した場合、計算を実行しないでください。 Javaでは、このような場合に例外をスローします。最も適切な既製の例外はIllegalArgumentExceptionです。これはRuntimeExceptionなので、メソッドにthrows句を追加する必要はありません。引数を読み取るときにmainメソッドで、それをキャッチするか、そのような状況が発生しないようにするとよいでしょう。
  • もう整数を返すことはできません(実際、longを使用して整数のオーバーフローが発生するため、intを使用する必要がありました)-結果が小数になる可能性があるためです。

したがって、メソッドがdoubleを返すように定義します。つまり、powerOfHalfNのタイプも修正する必要があります。そしてここに結果があります:

_public static double pow(int a, int n) {
    if (n == 0) {
        return 1.0;
    }
    if (n < 0) {
        // Negative power.
        if (a == 0) {
            throw new IllegalArgumentException(
                    "It's impossible to raise 0 to the power of a negative number");
        }
        return 1 / pow(a, -n);
    } else {
        // Positive power

        double powerOfHalfN = pow(a, n / 2);
        if (n % 2 == 1) {
            // Odd n
            return a * powerOfHalfN * powerOfHalfN;
        } else {
            // Even n
            return powerOfHalfN * powerOfHalfN;
        }
    }
}
_

負のnを処理する部分は、再帰の最上位でのみ使用されることに注意してください。 pow()を再帰的に呼び出すと、常に正の数になり、符号は0に達するまで変化しません。

それはあなたの運動への適切な解決策になるはずです。ただし、個人的には最後のifが気に入らないので、ここに別のバージョンがあります。これが同じことをしている理由がわかりますか?

_public static double pow(int a, int n) {
    if (n == 0) {
        return 1.0;
    }
    if (n < 0) {
        // Negative power.
        if (a == 0) {
            throw new IllegalArgumentException(
                    "It's impossible to raise 0 to the power of a negative number");
        }
        return 1 / pow(a, -n);
    } else {
        // Positive power
        double powerOfHalfN = pow(a, n / 2);
        double[] factor = { 1, a };
        return factor[n % 2] * powerOfHalfN * powerOfHalfN;
    }
}
_
29
RealSkeptic

注意を払う :

float result = 0;

そして

result = result * pow( a, n+1);

そのため、結果はゼロです。代わりに、次のように動作することが推奨されています。

result = a * pow( a, n+1);
1
Chiron

resultを0に初期化するエラーのほかに、いくつかの問題があります。

  1. 負のnの計算が間違っています。 a^n == 1/(a^(-n))を思い出してください。
  2. Nが整数でない場合、計算ははるかに複雑になり、サポートされません。あなたがそれをサポートする必要がないとしても、私は驚かないでしょう。
  3. O(log n)のパフォーマンスを実現するには、分割統治戦略を使用する必要があります。つまり、a^n == a^(n/2)*a^(n/2)です。
1
Eran

これは、少なくとも余分な乗算について心配していない場合は、これを行う方法の混乱が少ない方法です。 :

public static double pow(int base,int exponent) {
        if (exponent == 0) {
            return 1;
        }
        if (exponent < 0) {
            return 1 / pow(base, -exponent);
        }
        else {
            double results = base * pow(base, exponent - 1);
            return results;
        }
    }
0
Jameson
import Java.io.*;
import Java.util.*;
public class CandidateCode {
    public static void main(String args[] ) throws Exception {

    Scanner sc = new Scanner(System.in);
    int m = sc.nextInt();
    int n = sc. nextInt();
     int result = power(m,n);
     System.out.println(result);
    }
     public static int power(int m, int n){
         if(n!=0)
            return (m*power(m,n-1));
        else 
            return 1;
     }

}
0
malik arman

これを試して:

    public int powerN(int base, int n) {return n == 0 ? 1 : (n == 1 ? base : base*(powerN(base,n-1)));
0
Milze

適切な規則は、アルゴリズムの準備ができるまでキーボードから離れることです。あなたがしたことは明らかにO(n)です。

Eranが提案したように、O(log(n))の複雑さを得るには、各反復でnを2で除算する必要があります。

終了条件:

  • n == 0 => 1
  • n == 1 => a

特別なケース :

  • n <0 => 1./pow(a、-n)// 1.に注意してdoubleを取得します...

通常の場合:

  • m = n/2
  • 結果= pow(a、n)
  • result = resul * resul // 2回の計算を避ける
  • nが奇数の場合(n%2!= 0)=> resul * = a

このアルゴリズムはO(log(n))にあります-正しいJavaからコードを書くのはあなた次第です

しかし、あなたが言われたように:n must整数であること(正のokの負、ただしinteger

0
Serge Ballesta