Je dois écrire une méthode de puissance en Java. Il reçoit deux ints et peu importe qu’ils soient des nombres positifs ou négatifs. Il devrait avoir la complexité de O(logN)
. Il doit également utiliser la récursivité. Mon code actuel a deux nombres, mais le résultat que je continue à produire est zéro et je ne comprends pas pourquoi.
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;
}
}
Commençons par quelques faits mathématiques:
Commençons donc par le cas positif n et travaillons à partir de là.
Puisque nous voulons que notre solution soit récursive, nous devons trouver un moyen de définir une méthode basée sur un n plus petit et de travailler à partir de là. La façon habituelle de penser à la récursivité est d'essayer de trouver une solution pour n-1 et de travailler à partir de là.
Et en effet, puisqu'il est mathématiquement vrai que aⁿ = a⨯ (aⁿ⁻¹), l'approche naïve serait très similaire à ce que vous avez créé:
public static int pow( int a, int n) {
if ( n == 0 ) {
return 1;
}
return ( a * pow(a,n-1));
}
Cependant, la complexité de ceci est O (n). Pourquoi? Parce que pour n = 0, il ne fait aucune multiplication. Pour n = 1, cela fait une multiplication. Pour n = 2, il appelle pow (a, 1) que nous savons être une multiplication, et le multiplie une fois, nous avons donc deux multiplications. Il y a une multiplication dans chaque étape de récursion et il y a n étapes. Donc c'est O (n).
Pour faire cela O (log n), il faut que chaque étape soit appliquée à une fraction de n plutôt qu'à n-1. Là encore, il existe un fait mathématique qui peut nous aider:n₁ + n₂ = unn⨯an₂.
Cela signifie que nous pouvons calculer aⁿ en tant quen/2⨯an/2.
Mais que se passe-t-il si n est impair? quelque chose comme a⁹ sera un4,5⨯a4,5. Mais nous parlons ici de puissances entières. La manipulation des fractions est une chose complètement différente. Heureusement, nous pouvons simplement formuler cela comme aa⁴⨯a⁴.
Donc, pour un nombre pair, utilisez unn/2⨯an/2et pour un nombre impair, utilisez a⨯ an/2⨯an/2 (division entière, nous donnant 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 );
}
}
Cela nous donne les bons résultats (pour un n positif, c’est-à-dire). Mais en réalité, la complexité ici est encore une fois O(n) plutôt que O (log n). Pourquoi? Parce que nous calculons les pouvoirs deux fois. Cela signifie que nous l’appelons 4 fois au niveau suivant, 8 fois au niveau suivant, et ainsi de suite. Le nombre d'étapes de récursivité est exponentiel, donc cela annule les économies supposées que nous avons effectuées en divisant n par deux.
Mais en réalité, seule une petite correction est nécessaire:
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;
}
}
Dans cette version, nous appelons la récursion une seule fois. Nous obtenons donc, disons, une puissance de 64, très rapidement, passant à 32, 16, 8, 4, 2, 1 et ainsi de suite. Seulement une ou deux multiplications à chaque étape, et il n'y a que six étapes. C'est O (log n).
La conclusion de tout cela est:
Enfin, nous sommes prêts à nous occuper des nombres négatifs. Nous devons simplement obtenir la réciproque ⅟a. Il y a deux choses importantes à noter:
throws
à votre méthode. Ce serait bien si vous le détectiez ou si vous empêchiez qu'une telle situation se produise, dans votre méthode main
, lors de la lecture des arguments.long
car nous rencontrons un dépassement d'entier pour des puissances assez faibles avec int
), car le résultat peut être fractionnaire.Nous définissons donc la méthode pour qu'elle retourne double. Ce qui signifie que nous devons également corriger le type de powerOfHalfN
. Et voici le résultat:
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;
}
}
}
Notez que la partie qui traite un n négatif n'est utilisée que dans le niveau supérieur de la récursivité. Une fois que nous appelons pow()
récursivement, les nombres sont toujours positifs et le signe ne change pas tant qu'il n'a pas atteint 0.
Cela devrait être une solution adéquate à votre exercice. Cependant, personnellement, je n'aime pas la if
à la fin, voici donc une autre version. Pouvez-vous dire pourquoi cela fait la même chose?
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;
}
}
faire attention à :
float result = 0;
et
result = result * pow( a, n+1);
C'est pourquoi vous avez obtenu un résultat égal à 0. Il est suggéré de travailler comme suit:
result = a * pow( a, n+1);
Outre l'erreur d'initialisation de result
à 0, il existe d'autres problèmes:
n
négatif est faux. Rappelez-vous que a^n == 1/(a^(-n))
.O(log n)
, vous devez utiliser une stratégie de division et de conquête. c'est-à-dire a^n == a^(n/2)*a^(n/2)
.Voici une façon de le faire beaucoup moins déroutante, du moins si vous n'êtes pas préoccupé par les multiplications supplémentaires. :
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;
}
}
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;
}
}
essaye ça:
public int powerN(int base, int n) {return n == 0 ? 1 : (n == 1 ? base : base*(powerN(base,n-1)));
Une bonne règle est de s’éloigner du clavier jusqu’à ce que l’algorithme soit prêt. Ce que vous avez fait est évidemment O (n).
Comme Eran l'a suggéré, pour obtenir une complexité O(log(n)), vous devez diviser n par 2 à chaque itération.
Conditions finales:
Cas particulier :
Cas normal:
Cet algorithme est dans O(log(n)) - C’est à vous d’en écrire le code Java correct
Mais comme on vous l’avait dit: n must be integer (négatif de ok positif, mais integer )