2010-07-10 8 views
8

Dados dos números a, b tales que 1 < = a, b < = 10000000000 (10^10). Mi problema es verificar si los dígitos en ellos son permutación uno del otro o no. ¿Cuál es la forma más rápida de hacerlo? Se pensó en usar hash pero no en encontrar ninguna función hash adecuada. ¿Alguna sugerencia?Comprobando si dos números son permutaciones entre sí?

Para por ejemplo - 123 es una permutación válido de 312

Además no quiero ordenar los dígitos de los números.

+3

¿cómo puede un número ser una permutación de otro? ¿Estamos hablando de la cadena de dígitos en la base 10? Los dígitos 1-4-1 no son lo mismo que el número 141. – jalf

+2

también se puede pensar de esa manera. –

+0

Esto es esencialmente un chequeo de anagrama. – polygenelubricants

Respuesta

31

Si se refiere a los caracteres de los números (como 1927 y 9721), hay (al menos) un par de enfoques.

Si se le permitió ordenar, un enfoque es simplemente sprintf en dos búfers, ordenar los caracteres en los búferes, luego ver si las cadenas son iguales.

Sin embargo, dada su deseo de no Ordenar los dígitos, otra alternativa es la creación de un conjunto de diez elementos, con todos los elementos establecidos inicialmente a cero, entonces el proceso cada dígito en el primer número, incrementando el elemento relevante .

Luego haga lo mismo con el segundo número pero decrementando.

Si, al final, sigue siendo todo ceros, los números eran una permutación entre sí.

Esto es eficiente ya que es un algoritmo O(n) donde n es el número de dígitos en los dos números. El pseudo-código para una bestia tan sería algo así como:

def arePermutations (num1, num2): 
    create array count, ten elements, all zero. 
    for each digit in num1: 
     increment count[digit] 
    for each digit in num2: 
     decrement count[digit] 
    for each item in count: 
     if item is non-zero: 
      return false 
    return true 

En C, el siguiente programa completo ilustra cómo se puede hacer esto:

#include <stdio.h> 
#include <stdlib.h> 

#define FALSE (1==0) 
#define TRUE (1==1) 

int hasSameDigits (long num1, long num2) { 
    int digits[10]; 
    int i; 

    for (i = 0; i < 10; i++)  // Init all counts to zero. 
     digits[i] = 0; 

    while (num1 != 0) {   // Process all digits. 
     digits[num1%10]++;  // Increment for least significant digit. 
     num1 /= 10;    // Get next digit in sequence. 
    } 

    while (num2 != 0) {   // Same for num2 except decrement. 
     digits[num2%10]--; 
     num2 /= 10; 
    } 

    for (i = 0; i < 10; i++) 
     if (digits[i] != 0)  // Any count different, not a permutation. 
      return FALSE; 

    return TRUE;     // All count identical, was a permutation. 
} 

 

int main (int c, char *v[]) { 
    long v1, v2; 

    if (c != 3) { 
     printf ("Usage: %s <number1> <number2>\n", v[0]); 
     return 1; 
    } 

    v1 = atol (v[1]); 
    v2 = atol (v[2]); 
    if (hasSameDigits (v1, v2)) { 
     printf ("%d and %d are permutations\n", v1, v2); 
    } else { 
     printf ("%d and %d are not permutations\n", v1, v2); 
    } 

    return 0; 
} 

Simplemente pase dos números (positivos) y, suponiendo que encajen en un long, le dirá si tienen el mismo número de dígitos.

+0

¿es posible hacerlo sin hacer ningún tipo de clasificación? –

+0

Sí, @Ravi, mira la actualización. – paxdiablo

+0

¿También puede sugerir algo en la línea de hash también? –

3

¿Es la tarea?

Calcula el número de apariciones de cada dígito y compáralos, si son iguales, un número se puede convertir a otro utilizando la permutación.

+2

no, no lo es .... soy un desarrollador de software e intento aprender cosas nuevas. –

+1

Artyom: suena más como un problema de aperitivo de un concurso de programación como ICPC. – Joey

+0

Esto también se ve a menudo en las preguntas de Project Euler, p. Ej. # 49. – ComputerScientist

1

crear una matriz:

int digitOccurances[2][10]; 

En digitOccruances[X][N] tienda el número de veces que el dígito N aparece en el número X. Así que si estás comparando 8675309 a 9568733, la matriz terminaría pareciéndose:

{ { 1, 0, 0, 1, 0, 1, 1, 1, 1, 1 } , { 0, 0, 0, 2, 0, 1, 1, 1, 1, 1 } } 

Si los dos conjuntos son iguales, entonces los números son permutaciones.

Este es un O (n) algoritmo, de manera asintótica hablando, esto es el más eficiente que va a conseguir (no se puede resolver este problema sin examinar todos los dígitos al menos una vez.

Puede inmediato devuelve falso si los números tienen diferentes longitudes, así que suponga que ambos son de longitud n. Se necesitarán 2n operaciones para llenar el conjunto, y luego exactamente 10 comparaciones para leer el conjunto. 2n + 10 es O (n).

+0

¿hay alguna solución O (1)? –

+1

Aquí 'n' en' O (n) 'es el número de dígitos. Mucha gente podría llamar a eso 'O (log x)' donde 'x' es el número. No hay forma de hacer esto en menos tiempo que 'O (log x)', pero en la práctica si 'x' cabe en un tipo de variable entera,' O (log x) 'es constante. De todos modos, si 'n' es grande (hablando de cadenas de enteros no variables de C individuales) definitivamente no hay algoritmo mejor que' O (n) 'ya que tienes que examinar cada dígito al menos una vez ... –

+1

@R ..: Lo que es más, cualquier O (f (n)) se convierte en O (1) si n es fijo. –

17

ayb son anagramas si tienen el mismo número de cada dígito. Así que, básicamente, la manera más rápida parece ser, contando los dígitos para ayb:

int c[10]={0,0,0,0,0,0,0,0,0,0} 

while (a) { c[a%10]++; a/=10; } 
while (b) { c[b%10]--; b/=10; } 

int res=1; 
for (int i=0;i<10;i++) res &= c[i]==0; 
printf(res?"yes":"no"); 
+4

Es por eso que dicen que C tiene toda la potencia del lenguaje ensamblador con toda la legibilidad de, bueno, lenguaje ensamblador :-) – paxdiablo

+0

+1 como mejor respuesta. Contando los dígitos es óptimo. La clasificación es lenta. –

+0

Por cierto, 'int c [10] = {0};' es igual de bueno para inicializar la matriz. Y puede hacer memcmp con una matriz constante-0 para verificar los resultados con menos código. :-) –

0

bien si se puede construir una tabla de 80 GB, siempre se puede hacer:

int64 table[10000000000] = {0, blah blah..., 9999999999}; 

if (table[a] == table[b]) ... 
+0

Creo que hay pocas clases de equivalencia suficientes para las que no necesita 64 bits. Debería darte -1 por escribir 'int64' (alguna rareza no estándar ...) en lugar de' int64_t' (ISO C) ... –

+1

@R ..: Dios, disculpa por no ser estándar (y mucho menos raro). BillG no lo quiera! De todos modos, tienes razón sobre menos clases de equivalencia. De hecho, acabo de hacer un recuento de fuerza bruta: 92378. –

-1

{Editado para añadir prueba adicional)

Suponiendo que usted está en el dominio de dígitos, ¿qué hay de

if 
(
    ('1'^'2'^'3' == '3'^'1'^'2') && 
    ('1' + '2' + '3' == '3' + '1' + '2') 
) 
{ 
    cout << "Yes\n"; 
} 
else 
{ 
    cout << "No\n"; 
} 
+0

No creo que esto funcione ... –

+1

No. Tenga en cuenta que '0'^'3' == '1'^'2' por ejemplo. –

+1

Buena idea, sin embargo. Si las sumas de los dígitos son diferentes, entonces no son equivalentes. –

0

Si lo que entendí de su pregunta correctamente una permutación es una combinación de los elementos, que no se repiten. Así que si 123 es una permutación válido de 312 entonces también lo hace

123, 
213, 
132, 
321, 
213, 

y así sucesivamente.

Por lo tanto, basándonos en esta suposición digamos que tiene dos enteros 123456789 y 129837456. (Por simplicidad, también estoy asumiendo que ambos números tienen la misma longitud). Si entendiste el punto, entonces también podrías verificar diferentes combinaciones y permutaciones.

para que todo lo que necesita hacer es conseguir los números enteros de unidades con respecto al número dado, por ejemplo:

Number 123456789 is 
1 * 100000000 + 
2 * 10000000 + 
3 * 1000000 + 
4 * 100000 + 
5 * 10000  + 
6 * 1000  + 
7 * 100  + 
8 * 10  + 
9 

o

1 * power(10, 8) + 
2 * power(10, 7) + 
3 * power(10, 6) + 
4 * power(10, 5) + 
5 * power(10, 4) + 
6 * power(10, 3) + 
7 * power(10, 2) + 
8 * power(10, 1) + 
9 * power(10, 0) 

i, literalmente, le he dado indicio algorítmica de cómo hacer eso para que esto pueda hacerse fácilmente. una vez hecho esto se va a terminar con números enteros separados (mejor guardar estos valores en una matriz)

1, 2, 3, 4, 5, 6, 7, 8, 9 

Ahora

hacer lo mismo para el otro entero dado para que el resultado final será con otra matriz de enteros

ahora todo lo que necesita comprobar es que si todos los enteros de la segunda matriz están presentes en la primera matriz de enteros, si es así, son una permutación de los enteros de la primera matriz o el primer número .

Espero que esto ayude.

+0

sus soluciones es una de las posibles soluciones, pero no es eficiente, ya que se compara con otras soluciones publicadas en este problema. –

+0

Bueno, mi solución no fue en realidad una solución sino más bien una pista que indicará la solución o cómo podemos lograrla. Como soy un defensor de aprender de tu propia práctica y de la experiencia de los demás. Entonces, si proporciono la solución completa, lo forzaré a no usar su mente en absoluto. Y sí, definitivamente puede haber una cantidad de optimización para el cálculo. Pero las optimizaciones llegan solo después de saber qué hacer. – Orochi

1

He encontrado esta solución bastante eficiente en rossetacode.org. Espero que me perdones por escribirlo en Java (no me siento cómodo con C) pero la sintaxis debería ser más o menos la misma.

El código primero verifica si los números tienen el mismo número de dígitos, luego suma los dígitos por bit y los convierte en un total.Excepto que la distancia de desplazamiento se multiplica por un factor 6. Esto hace imposible que los dígitos más pequeños compongan el mismo valor que un dígito más grande. Por ejemplo, un '9' requeriría 64 veces '8' para coincidir con su valor, lo que obviamente no es posible.

Este código asume una entrada no negativa.

boolean haveSameDigits(long n1, long n2) { 
    long nn1 = n1, nn2 = n2; 
    while (nn1 > 0 && nn2 > 0) { 
     nn1 /= 10; 
     nn2 /= 10; 
    } 
    if (nn2 != nn1) // not the same length 
     return false; 

    long total1 = 0, total2 = 0; 
    while (n1 != 0) { 
     total1 += 1L << ((n1 % 10) * 6); 
     total2 += 1L << ((n2 % 10) * 6); 
     n1 /= 10; 
     n2 /= 10; 
    } 
    return total1 == total2; 
} 
-1

No estoy seguro de por qué no desea ordenar, a menos que sea una condición de su tarea. Para cualquiera de tropezar sobre esta cuestión sólo en busca de la manera más rápida (! Y lo más Pythonic) para probar si dos números enteros son permutaciones en Python:

def arePermutations(a, b): 
    return sorted([d for d in str(a)]) == sorted([d for d in str(b)]) 

Esta solución se ejecuta un poco más rápido en Python, dependiendo, por supuesto, en los números probados para ser enteros relativamente pequeños. Funciona bastante bien para el problema 52 de Project Euler.

+1

La pregunta se refiere a C, no a python. Además, una clasificación hash/radix como se muestra en las otras respuestas es más rápida que una clasificación estándar. – dbush

Cuestiones relacionadas