2010-12-13 22 views
26

Esto es un interview question: "Dado 2 enteros xey, verifique si x es una potencia entera de y" (por ejemplo, para x = 8 yy = 2 la respuesta es " verdadero ", y para x = 10 ey = 2" falso ").Compruebe si un entero es una potencia entera de otro

La solución obvia es:

int n = y; while(n < x) n *= y; return n == x

Ahora estoy pensando en cómo mejorarlo.

Por supuesto, puedo verificar algunos casos especiales: p. ambos x y y deben ser números pares o impares, es decir, podemos verificar el bit menos significativo de x y y. Sin embargo, me pregunto si puedo mejorar el algoritmo central en sí mismo.

+2

En realidad, pensé que la solución obvia es dividir x por y luego dividir el resultado por y continuamente hasta que llegue a un número que no sea divisible por y. Si ese número es 1, x es un poder de y. – JeremyP

+0

Desafortunado que ni un solo usuario aquí notó que cada pieza del código publicado falla miserablemente para x = ± 1 – Nabb

+0

No, la mía funciona para x = +1 (y un ABS trivial corrige los números negativos). Ahora y == 0, sin embargo. –

Respuesta

25

Haría mejor en dividir y en x. La primera vez que obtienes un resto distinto de cero sabes que x no es una potencia entera de y.

while (x%y == 0) x = x/y 
return x == 1 

Esto se ocupa de su punto par/impar en la primera iteración.

+1

¿Por qué es la división mejor que la multiplicación? – Michael

+3

@Michael: Supongamos que x es 123456789 yy es 2. Calcula cuántas iteraciones de multiplicar por y tendrías que hacer para obtener la respuesta y luego calcula cuántas divisiones tendrías que hacer. – JeremyP

+3

También es mejor que la multiplicación porque no puede desbordarse. Y podría ser posible que el código optimizado omita sus intentos de detectar el desbordamiento. – ruslik

20

Significa registro y (x) debería ser un número entero . No necesita ningún bucle. en O (1) tiempo

public class PowerTest { 

    public static boolean isPower(int x, int y) { 
     double d = Math.log(Math.abs(x))/Math.log(Math.abs(y)); 

     if ((x > 0 && y > 0) || (x < 0 && y < 0)) { 
      if (d == (int) d) { 
       return true; 
      } else { 
       return false; 
      } 
     } else if (x > 0 && y < 0) { 
      if ((int) d % 2 == 0) { 
       return true; 
      } else { 
       return false; 
      } 
     } else { 
      return false; 
     } 
    } 

    /** 
    * @param args 
    */ 
    public static void main(String[] args) { 

     System.out.println(isPower(-32, -2)); 
     System.out.println(isPower(2, 8)); 
     System.out.println(isPower(8, 12)); 
     System.out.println(isPower(9, 9)); 
     System.out.println(isPower(-16, 2)); 
     System.out.println(isPower(-8, -2)); 
     System.out.println(isPower(16, -2)); 
     System.out.println(isPower(8, -2)); 
    } 

} 
+8

Debe tener cuidado con los problemas de redondeo con números de coma flotante. ¿Cómo es 'isPower (-8, -2)' fair? – JeremyP

+0

Sí, tienes razón. Pensé que el entero es positivo. Entonces, debería pensar que es la versión ABS –

+0

Deberías probar isPower (1162261467, 3) x es mayor (int la pregunta) –

2

Me implementar la función de este modo:

bool IsWholeNumberPower(int x, int y) 
{ 
    double power = log(x)/log(y); 
    return floor(power) == power; 
} 

Esto no debería necesitar cheque dentro de un delta como es común con comparaciones en coma flotante, ya que' revisando números enteros.

+1

Ahora la pregunta es cómo se implementa el "registro" y por qué es mejor que el bucle. – Michael

+0

log (x) donde x es un número entero no es un número entero. Entonces, no, no estás lidiando con números enteros. También considere 'IsWholeNumberPower (-8, -2)'. La respuesta * debería * ser verdadera. – JeremyP

+1

El enfoque de registro es mejor que el ciclo, IMO, porque deja en claro su intención. Siempre que sepa que el registro de un número dividido por el registro de otro número le da el poder, el segundo número se levanta para obtener el primero, no puedo pensar en un método más claro (supongo que la mayoría de las personas aprenden esto en la escuela , pero podría estar fuera de la base). Si buscas un código más rápido, no puedo decírtelo, ya que no lo he probado en ningún idioma. –

2

Pensándolo bien, no hagas esto. No funciona para x negativo y/o y. Tenga en cuenta que todas las demás respuestas basadas en log que se presentan en este momento también se han roto exactamente de la misma manera.

La siguiente es una solución general rápida (en Java):

static boolean isPow(int x, int y) { 
    int logyx = (int)(Math.log(x)/Math.log(y)); 
    return pow(y, logyx) == x || pow(y, logyx + 1) == x; 
} 

Dónde pow() es una función de número entero exponenciación como la siguiente en Java:

static int pow(int a, int b) { 
    return (int)Math.pow(a, b); 
} 

(Esto funciona debido a la siguiente garantía provista por Math.pow: "Si ambos argumentos son enteros, entonces el resultado es exactamente igual al resultado matemático de elevar el primer argumento a la potencia del segundo argumento ... ")

La razón para ir con logaritmos en lugar de división repetida es el rendimiento: mientras que log is slower than division, es más lento por un pequeño múltiplo fijo. Al mismo tiempo, elimina la necesidad de un bucle y, por lo tanto, le proporciona un algoritmo de tiempo constante.

+0

Este funciona para isPow (1162261467,3) –

7

Esto se ve para el exponente en O (log N) pasos:

#define MAX_POWERS 100 

int is_power(unsigned long x, unsigned long y) { 
    int i; 
    unsigned long powers[MAX_POWERS]; 
    unsigned long last; 
    last = powers[0] = y; 

    for (i = 1; last < x; i++) { 
    last *= last; // note that last * last can overflow here! 
    powers[i] = last; 
    } 
    while (x >= y) { 
    unsigned long top = powers[--i]; 
    if (x >= top) { 
     unsigned long x1 = x/top; 
     if (x1 * top != x) return 0; 
     x = x1; 
    } 
    } 
    return (x == 1); 
} 

Los números negativos no son manejados por este código, pero se puede hacer facilmente con un poco de código condicional cuando i = 1

+0

¡Gracias por la idea de precalcular los poderes de y! – Michael

+0

Esta solución se debe subir la apuesta más. Por cierto, hay * no * manera en que las potencias [99] caben en un largo sin signo si x> 1. Por ejemplo, si x == 2 entonces las potencias 99 son aproximadamente una seguida de mil millones de billones de ceros. – jonderry

2

En los casos en que y es 2, hay un enfoque rápido que evita la necesidad de un bucle. Este enfoque puede extenderse a los casos en y es un poder mayor de 2.

Si x es una potencia de 2, la representación binaria de x tiene un único conjunto de bits. Existe un algoritmo bastante simple para el conteo de bits para contar los bits en un entero en el tiempo O (log n) donde n es el ancho de bits de un entero. Muchos procesadores también tienen instrucciones especializadas que pueden manejar esto como una operación única, aproximadamente tan rápido como (por ejemplo) una negación entera.

Sin embargo, para extender el enfoque, primero tome un enfoque ligeramente diferente para verificar un solo bit. Primero determine la posición del bit menos significativo. De nuevo, existe un algoritmo simple de manipulación de bits, y muchos procesadores tienen instrucciones especializadas rápidas.

Si este bit es el único, entonces (1 << pos) == x. La ventaja aquí es que si está probando una potencia de 4, puede probar pos % 2 == 0 (el bit único está en una posición pareja). Si prueba una potencia de cualquier potencia de dos, puede realizar la prueba para pos % (y >> 1) == 0.

En principio, podría hacer algo similar para probar potencias de 3 y potencias de potencias de 3. El problema es que necesitaría una máquina que funcione en la base 3, lo cual es un poco improbable. Sin duda puede probar cualquier valor x para ver si su representación en la base y tiene un solo dígito distinto de cero, pero estaría haciendo más trabajo que ya está haciendo. Lo anterior explota el hecho de que las computadoras funcionan en binario.

Probablemente no vale la pena hacerlo en el mundo real, sin embargo.

+0

la misma idea se puede aplicar a cualquier base. No es tan fácil como cambiar los bits, pero se puede hacer usando solo operaciones aritméticas básicas ('+', '*', '/'). Vea mi propia respuesta al OP para una implementación en C. – salva

+2

. Hay una verificación más rápida para power-of-2. '(x & (x-1)) == 0' – Axn

+0

@Axn - He visto ese truco antes, pero soy muy propenso a olvidarlo. Por supuesto, no es más rápido que un intrínseco, pero es portátil. – Steve314

3

Esto parece ser bastante rápido para números positivos ya que encuentra los límites inferior y superior de la potencia deseada y luego aplica la búsqueda binaria.

#include <iostream> 
#include <cmath> 
using namespace std; 

//x is the dividend, y the divisor. 
bool isIntegerPower(int x, int y) 
{ 
    int low = 0, high; 
    int exp = 1; 
    int val = y; 
    //Loop by changing exponent in the powers of 2 and 
    //Find out low and high exponents between which the required exponent lies. 
    while(1) 
    { 
     val = pow((double)y, exp); 
     if(val == x) 
      return true; 
     else if(val > x) 
      break; 
     low = exp; 
     exp = exp * 2; 
     high = exp; 
    } 
    //Use binary search to find out the actual integer exponent if exists 
    //Otherwise, return false as no integer power. 
    int mid = (low + high)/2; 
    while(low < high) 
    { 
     val = pow((double)y, mid); 
     if(val > x) 
     { 
      high = mid-1; 
     } 
     else if(val == x) 
     { 
      return true; 
     } 
     else if(val < x) 
     { 
      low = mid+1; 
     } 
     mid = (low + high)/2; 
    } 
    return false; 
} 

int main() 
{ 
    cout<<isIntegerPower(1024,2); 
} 
1

Las respuestas anteriores son correctas, la respuesta de Paul me ha gustado mejor. Es simple y limpio. Aquí es la implementación en Java de lo que sugirió:

public static boolean isPowerOfaNumber(int baseOrg, int powerOrg) { 
    double base = baseOrg; 
    double power = powerOrg; 

    while (base % power == 0) 
     base = base/power; 
    // return true if base is equal 1 
    return base == 1; 
} 
2
double a=8; 
    double b=64; 

    double n = Math.log(b)/Math.log(a); 
    double e = Math.ceil(n); 

    if((n/e) == 1){ 
     System.out.println("true"); 
    } else{ 
     System.out.println("false"); 
    } 
0

Si usted tiene acceso a la mayor potencia de y, que puede ser instalado dentro del tipo de datos requerido, esta es una manera muy resbaladizo de resolver este problema.

Digamos, para nuestro caso, y == 3. Entonces, necesitaríamos comprobar si x es una potencia de 3.

Dado que necesitamos verificar si un número entero x es una potencia de 3, comencemos a pensar sobre este problema en términos de qué información ya está en mano.

1162261467 es la mayor potencia de 3 que puede caber en una int Java.
1162261467 = 3^19 + 0

La x indicada se puede expresar como [(a power of 3) + (some n)]. Creo que es bastante elemental poder probar que si n es 0 (que ocurre iff x es una potencia de 3), 1162261467 % x = 0.

Por lo tanto, para verificar si un número entero dado x es una potencia de tres, verifique si x > 0 && 1162261467 % x == 0.

Generalización. Para verificar si un entero dado x es una potencia de un entero dado y, verifique si x > 0 && Y % x == 0: Y es la mayor potencia de y que puede caber en un tipo de datos entero.

La idea general es que si A es alguna potencia de Y, A puede expresarse como B/Ya, donde a es un entero y A < B. Sigue el mismo principio exacto para A > B. El caso A = B es elemental.

+0

Puede haber un par de posibles casos de borde, pero creo que se puede solucionar. –

+0

Una caja de borde es el exponente de que la alta potencia no es primo. – greybeard

+0

Esto solo funciona si y es primo. – Magdiragdag

0

me encontró esta solución: // Compruebe si A puede ser expresado como potencia de dos números enteros

int isPower(int A) 
    { 
    int i,a; 
    double p; 
    if(A==1) 
    return 1; 
    for(int a=1; a<=sqrt(A);++a) 
    { 
     p=log(A)/log(a); 
     if(p-int(p)<0.000000001) 
     return 1; 
    } 
    return 0; 
    } 

binarycoder.org

1

Aquí es una versión de Python, que reúne las ideas de @salva y @ Axn y se modifica para que no genere ningún número mayor que los dados y utiliza solo el almacenamiento simple (leer, "sin listas") reduciendo repetidamente el número de interés:

def perfect_base(b, n): 
    """Returns True if integer n can be expressed as b**e where 
    n is a positive integer, else False.""" 

    assert b > 1 and n >= b and int(n) == n and int(b) == b 

    # parity check 
    if not b % 2: 
     if n % 2: 
      return False # b,n is even,odd 
     if b == 2: 
      return n & (n - 1) == 0 
     if not b & (b - 1) and n & (n - 1): 
      return False # b == 2**m but n != 2**M 
    elif not n % 2: 
     return False # b,n is odd,even 

    while n >= b: 
     d = b 
     while d <= n: 
      n, r = divmod(n, d) 
      if r: 
       return False 
      d *= d 
    return n == 1 
Cuestiones relacionadas