• Publicidad

Números mayores de 16 dígitos

¿Apenas comienzas con Perl? En este foro podrás encontrar y hacer preguntas básicas de Perl con respuestas aptas a tu nivel.

Re: Números mayores de 16 dígitos

Notapor FluiD » 2010-05-02 07:36 @358

Pero, ¿no lo consigues con el printf "%.0f"?

Sí, con números de 16 dígitos va perfecto, pero para números más grandes si el incremento del bucle es 1 no aumenta la salida:

Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
  1. #!/usr/bin/perl
  2. for (my $i=10000000000000000; $i <= 20000000000000000; $i++) {
  3.     printf "%.0f\n", $i;
  4. }
  5.  
  6. __END__
  7. 10000000000000000
  8. 10000000000000000
  9. 10000000000000000
  10. 10000000000000000
  11. 10000000000000000
  12.  
Coloreado en 0.002 segundos, usando GeSHi 1.0.8.4


Cuando el incremento es mayor de 1 funciona correctamente para números de longitud 17. Para que funcione correctamente para números de longitud 18 el incremento del for() tiene que ser como mínimo 9 (cosas muy raras que escapan a mi entender :) ).

Respecto al código que has colgado, antes de nada muchas gracias por tu tiempo...

Te comento:
Los módulos common::sense; y use open 'locale'; no los tengo. No sé si serán importantes, y adaptándolo un poquito, cambiando los say() por print() y quitando los acentos que mi intérprete no reconoce parece que funciona, pero cuando en la línea
Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
$número_gordo = Math::BigInt->new('1746545455554699123456');
Coloreado en 0.001 segundos, usando GeSHi 1.0.8.4

en vez del número entero, le paso la variable con el número que se está procesando en ese momento en el bucle, la salida vuelve a darla incorrecta, te pongo un ejemplo de tu código modificado (simplificado) para que me funcione en mi intérprete pasándole una variable:

Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
my $num = 1746545455554699123456;
my $largo = length $num;

print  "$num, $largo\n";

if ($largo > 16  or  $num =~ /e/) {
    require Math::BigInt;
    print "SI\n";

    $a = 99999999999999999;
    #$num = Math::BigInt->new('1746545455554699123456');
    $num = Math::BigInt->new($a);
}
print  "$num\n";

__END__
1.7465454555547e+21, 19
SI
100000000000000000
 
Coloreado en 0.001 segundos, usando GeSHi 1.0.8.4


A propósito, en la documentación de Math::BigInt comentan que existe la versión con librería en C, para obtener mayor velocidad, instalando el módulo Math::BigInt::GMP.

Gracias pero si esto se me queda grande junto con mi pobre nivel de inglés imagina si me tengo que poner con la librería en C; además luego quiero convertir a exe con la aplicación perl2exe y me da a mí que también me daría guerra.

¿No puedes poner un ejemplo de lo que quieres hacer?

Sí, claro que puedo, vamos a ver, si con esto consigo solucionarlo y si no ya cuelgo el código. Lo que pasa es que tengo que adaptarlo porque usa variables globales y valores de entrada de la función, y me va ha llevar un rato :D

Bueno, de nuevo gracias por vuestro tiempo, que es una de las cosas que más valoro.

Saludos.

MiG.G
Última edición por explorer el 2010-05-02 08:29 @395, editado 1 vez en total
Razón: Tildes
FluiD
Perlero nuevo
Perlero nuevo
 
Mensajes: 14
Registrado: 2009-10-16 14:38 @651

Publicidad

Re: Números mayores de 16 dígitos

Notapor explorer » 2010-05-02 09:25 @434

A mí sí que me funciona, pero es porque yo estoy en una máquina con un Linux de 64 bits. Por eso tengo una precisión mayor. Además, uso Perl v5.10. El módulo common::sense activa todas las nuevas mejoras que trae Perl v5.10 (poder escribir con acentos, la función say(), etc.). Instalarlo, en tu Ubuntu, te llevará unos segundos, con la ayuda del comando cpanp.

Aún así, llega un momento en que empiezan a salir errores:

Sintáxis: [ Descargar ] [ Ocultar ]
Using text Syntax Highlighting
10000000000845112
10000000000845112
10000000000845114
10000000000845116
10000000000845116
10000000000845116
10000000000845118
10000000000845120
Coloreado en 0.000 segundos, usando GeSHi 1.0.8.4


Es por la precisión de las rutinas de punto flotante, desde luego. Aunque sean enteros, los números grandes los almacenará como punto flotante más un exponente.

Si se quiere trabajar con números grandes, no queda más remedio que hacerlo con Math::BigInt. El pragma 'use bignum;' se encarga de manejarlo de forma transparente, e incluso, para números pequeños, cargará el Math::BigInt::Lite. Este módulo, por ejemplo, no lo tenía, y me lo he instalado. Veo que lo carga si el número es de menos de 8 cifras. Para más grandes, usará Math::BigInt o Math::BigInt::GMP si está disponible.

Si solo vas a realizar operaciones de enteros, entonces puedes usar bigint en lugar de bignum (más información en el pragma bignum).

Leyendo con un poco más de calma el módulo Math::BigInt, lo que hay que hacer es indicar la exactitud con la que queremos trabajar. Por ejemplo, 40 cifras:
Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
  1. #!/usr/bin/perl
  2. use 5.010;
  3. use Math::BigInt;
  4. use Time::HiRes 'sleep';
  5.  
  6. my $l = 40;
  7.  
  8. Math::BigInt->accuracy($l);
  9.  
  10. # Creamos un supernúmero de $l cifras aleatorias
  11. my $i = Math::BigInt->new( join q[], map { int rand 10 } 1 .. $l );
  12.  
  13. # El primer incremento: 1
  14. my $j = Math::BigInt->bone();
  15.  
  16. # Bucle sin fin, de demostración
  17. while ("para siempre") {
  18.     if ( rand() < 0.1 ) {                                # Tiramos una moneda de diez caras al aire
  19.         $j = Math::BigInt->new( '1' . ('0' x rand $l) ); # Si sale el '1', cambiamos el incremento
  20.     }
  21.  
  22.     $i += $j;                                            # Hacemos la suma (lo mismo que $i->badd($j);)
  23.  
  24.     say $i;                                              # Pintamos
  25.  
  26.     sleep .2;                                            # Esperamos
  27. }
  28.  
  29. __END__
  30. 8012673663648712793353160758663526921638
  31. 8012673663648712803353160758663526921638
  32. 8012673663648712813353160758663526921638
  33. 8012673663648712823353160758663526921638
  34. 8012673663648712833353160758663526921638
  35. 8012673663648712843353160758663526921638
  36. 8012673663648712853353160758663526921638
  37. 8012673663648712863353160758663526921638
  38. 8012673673648712863353160758663526921638
  39. 8012673683648712863353160758663526921638
  40. 8012673693648712863353160758663526921638
  41. 8012673703648712863353160758663526921638
  42. 8012673713648712863353160758663526921638
  43. 8012673723648712863353160758663526921638
  44. 8012673733648712863353160758663526921638
  45. 9012673733648712863353160758663526921638
  46.  
Coloreado en 0.001 segundos, usando GeSHi 1.0.8.4

He hecho pruebas con números de 120 cifras, y sin problemas, aunque no he medido la velocidad. Si el tamaño que vas a manejar varía mucho, instala Math::BigInt::Lite, y usa bigint en lugar de Math::BigInt. Y no te olvides de indicar la exactitud.

Lo que me parece curioso es que quieras empaquetar luego el programa con perl2exe... ¿qué razón hay?

P.D. Acabo de leer que Math::BigInt usará los módulos Math::BigInt::FastCalc y Math::BigInt::Pari si también están instalados. Compruébalo.
JF^D Perl programming & Raku programming. Grupo en Telegram: https://t.me/Perl_ES
Avatar de Usuario
explorer
Administrador
Administrador
 
Mensajes: 14480
Registrado: 2005-07-24 18:12 @800
Ubicación: Valladolid, España

Re: Números mayores de 16 dígitos

Notapor FluiD » 2010-05-02 11:22 @515

explorer, si ya estaba acojonado, con el último código que has publicado me has terminado de acojonar. Se me queda muy grande. Estoy a puntito de desistir.

Te comento: el script que quiero hacer es para generar diccionarios para pruebas de fuerza bruta. El motivo por el cual quiero usar los menos módulos posibles (además de que no sé) es porque en las distribuciones donde se van a usar no sé si tienen esos módulos, y la idea es que se usen con los módulos por defecto que trae el intérprete.

El motivo de usar perl2exe es para eso mismo, para que se pueda usar en Win32, como es lógico sin necesidad de instalar un intérprete.

Con el use Math::BigInt; volvemos a lo mismo, a ralentizar los cálculos para números más pequeños. No he probado los tiempos pero si con use bignum así era (de forma muy notable) y dices que bignum lo usa de forma transparente...

Lo único que quiero hacer es manejar números mayores de 16 dígitos sea o no con Math::BigInt, y si es necesario cargar este módulo, que sea de forma selectiva para no ralentizar los números menores. Con el código que me has puesto me han dado ganas de llorar al verlo, seguramente en él está la solución a lo que te comento, pero como te digo, está a 2 años luz de mi nivel :cry:

Te pego el código real modificado, sin variables globales, valores de entrada de la función, etc...

Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
  1. my ($primer,$ultimo,$caracteres,$ceros);
  2. while ($primer !~ /\b\d+\b/)
  3.     {
  4.       print " Numero de comienzo: ";
  5.       $primer = <STDIN>;
  6.       chomp $primer;
  7.     }
  8. while ($ultimo !~ /\b\d+\b/)
  9.    {
  10.       print " Numero final: ";
  11.       $ultimo = <STDIN>;
  12.       chomp $ultimo;
  13.    }
  14.  
  15. # AQUÍ ES DONDE SEGÚN LA LONGITUD DEL NÚMERO DEBERÍA DE CARGAR
  16. # EL MÓDULO PARA MANEJAR LOS NÚMEROS GRANDES
  17.  
  18. if ($primer < $ultimo)
  19.    {
  20.       $caracteres = length($ultimo);
  21.       for ($i=$primer;$i != $ultimo+1;$i++)
  22.          {
  23.             $ceros = $caracteres - length($i);
  24.  
  25.             if ($ceros > 0)
  26.                {
  27.                   for($e=1;$e != $ceros+1;$e++)
  28.                      {
  29.                         $i = "0".$i;
  30.                      }
  31.                   print  "$i\n";
  32.                }
  33.             else
  34.                {
  35.                   print "$i\n";
  36.                }
  37.          }
  38.    }
  39.  
Coloreado en 0.001 segundos, usando GeSHi 1.0.8.4


Saludos

MiG.G
Última edición por explorer el 2010-05-02 14:19 @638, editado 2 veces en total
Razón: Ortografía, formateo del código
FluiD
Perlero nuevo
Perlero nuevo
 
Mensajes: 14
Registrado: 2009-10-16 14:38 @651

Re: Números mayores de 16 dígitos

Notapor explorer » 2010-05-02 20:05 @878

FluiD escribiste:explorer, si ya estaba acojonado, con el último código que has publicado me has terminado de acojonar. Se me queda muy grande. Estoy a puntito de desistir.
Al final de este mensaje te diré porqué debes desistir. Pero no porque mi programa sea muy complicado, que no lo es, sino por otra razón.

Si algo no se entiende, pues se pregunta :)

Mi programa usa algunos trucos, pero solo para generar cifras muy grandes. El resto, es usar el módulo Math::BigInt.

FluiD escribiste:Te comento: el script que quiero hacer es para generar diccionarios para pruebas de fuerza bruta. El motivo por el cual quiero usar los menos módulos posibles (además de que no sé) es porque en las distribuciones donde se van a usar no sé si tienen esos módulos, y la idea es que se usen con los módulos por defecto que trae el intérprete.
Todos los módulos indicados en el último programa, están en la distribución base de los Perl modernos.

FluiD escribiste:Con el use Math::BigInt; volvemos a lo mismo, a ralentizar los cálculos para números más pequeños. No he probado los tiempos pero si con use bignum así era (de forma muy notable) y dices que bignum lo usa de forma transparente...
A ver... como dice la documentación... con estos módulos se pueden manejar números muy grandes, pero eso ralentiza a los programas.

FluiD escribiste:Lo único que quiero hacer es manejar números mayores de 16 dígitos sea o no con Math::BigInt, y si es necesario cargar este módulo, que sea de forma selectiva para no ralentizar los números menores. Con el código que me has puesto me han dado ganas de llorar al verlo, seguramente en él está la solución a lo que te comento, pero como te digo, está a 2 años luz de mi nivel :cry:
En un código anterior ya te he puesto un ejemplo de cómo llamar a Math::BigInt en ese caso, pero yo no correría riesgos...

Si pudiera, instalaría Math::BigInt::Lite, y entonces sí que tendría la mejor velocidad en el caso de números pequeños.

FluiD escribiste:Te pego el código real modificado, sin variables globales, valores de entrada de la función, etc...

A ver... dices que se trata de generar diccionarios para ataques de fuerza bruta. Bien. La cuestión es ¿por qué es necesaria la velocidad? Si los diccionarios que vamos generando los vamos guardando a disco, solo necesitamos hacerlo una vez. Necesitamos velocidad si esos diccionarios los necesita otro proceso, claro.

Por lo que veo en tu código, tratas de generar toda la secuencia de números entre el primero y el último, y rellenando con ceros. Veo que, además de generar la secuencia con números grandes, tienes problemas a la hora de rellenar con ceros, enlenteciendo mucho el programa.

Aquí tienes una variante:
Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
  1. #!/usr/bin/perl
  2. use 5.010;
  3. use Math::BigInt;
  4.  
  5. Math::BigInt->accuracy(20);
  6.  
  7. my $primero;
  8. my $ultimo;
  9.  
  10. ## Leemos los números
  11. do {
  12.     do {
  13.         print 'Introduzca el primer valor: ';
  14.         chomp($primero = <>);
  15.         $primero = Math::BigInt->new($primero);
  16.     }
  17.     until ($primero =~ /^\d+$/);
  18.  
  19.     do {
  20.         print 'Introduzca el último valor: ';
  21.         chomp($ultimo = <>);
  22.         $ultimo = Math::BigInt->new($ultimo);
  23.     }
  24.     until ($ultimo =~ /^\d+$/);
  25. }
  26. until ($primero <= $ultimo);
  27.  
  28.  
  29. ## Tamaño de los números
  30. my $largo_primero = $primero->length;
  31. my $largo_ultimo  = $ultimo ->length;
  32.  
  33.  
  34. ## Ceros de relleno
  35. my $largo_ceros = $largo_ultimo - $largo_primero;
  36. my $ceros = '0' x $largo_ceros;
  37.  
  38.  
  39. ## Bucle para el caso de ser de distinta longitud
  40. while ($largo_primero < $largo_ultimo) {
  41.  
  42.     ## Cálculo de cuántos números hay hasta que exista cambio de número de ceros
  43.     my $resto = Math::BigInt->new(
  44.         substr(('1' . ('0' x $largo_primero)) - $primero, - length $primero)
  45.     );
  46.  
  47.     ## Bucle que genera un rango de números con el mismo número de ceros
  48.     while ($resto) {
  49.         print $ceros;
  50.         say $primero;
  51.  
  52.         $primero->binc;
  53.         $resto->bdec;
  54.     }
  55.  
  56.     ## Reducimos un cero menos
  57.     $ceros = '0' x --$largo_ceros;
  58.  
  59.     ## Volvemos a comprobar la longitud de los números
  60.     $largo_primero = $primero->length;
  61.     $largo_ultimo  = $ultimo ->length;
  62. }
  63.  
  64.  
  65. ## Bucle para el caso de ser de la misma longitud
  66. ## Del $primero al $ultimo
  67. while ($primero->bcmp($ultimo) < 1) {
  68.     say $primero;
  69.  
  70.     $primero->binc();
  71. }
  72.  
  73. __END__
  74.  
Coloreado en 0.002 segundos, usando GeSHi 1.0.8.4

Pero, atención, este programa es bastante complejo: está lleno de técnicas y trucos para llevar a Perl al máximo de rendimiento.

No pretendo que lo entiendas, pero para un problema de este tipo, es necesario un análisis detallado de cada operación, y buscar la forma más eficiente de ejecutarla. En el programa, por ejemplo, se usa un cálculo muy elaborado para reducir las operaciones de pintado y relleno de los ceros que van por delante del número. Y, naturalmente, usar todas las operaciones matemáticas de Math::BigInt que podamos.

Desde luego, este problema es típico de los que hay que resolver en C, ya que se quiere obtener la respuesta de la forma más rápida. Estaría muy bien, por ejemplo, hacerlo dentro de Perl, con Inline::C, pero no es cuestión de complicarlo más todavía.

Quizás esa sea la razón principal para tirar la toalla: si Perl no consigue superar tus expectativas de velocidad, deberás usar otro lenguaje.

En mi ordenador, se tardan diez segundos en generar 125.000 números. Seguro que haciéndolo en C se tarda mucho menos.

Ahora bien... hasta ahora nos has dicho que quieres generar la secuencia, pero no nos dices cuándo y cómo vas a probar la secuencia. Porque según como lo quieras hacer, no es necesario preocuparse tanto por la velocidad. Si, por ejemplo, quieres probar cada número nada más ser generado, entonces estoy seguro que el cuello de botella es la propia prueba, y no la generación del número.

Incluso la opción de guardar una secuencia numérica lineal en un fichero, no tiene sentido... pues la posición N de la secuencia es igual al valor de la primera, más N. El programa de prueba no necesita leer ningún fichero, pues su contenido es predecible.

Actualización: Después de estar programando en Perl durante diez años, no deja de sorprenderme...
Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
  1. #!/usr/bin/perl
  2. use 5.010;
  3. use Math::BigInt;
  4.  
  5. Math::BigInt->accuracy(20);
  6.  
  7. my $primero = Math::BigInt->new('1');
  8. my $ultimo  = Math::BigInt->new('125000');
  9.  
  10. my $largo_primero = $primero->length;
  11. my $largo_ultimo  = $ultimo ->length;
  12.  
  13. my $largo_ceros = $largo_ultimo - $largo_primero;
  14. my $ceros = '0' x $largo_ceros;
  15.  
  16. while ($primero->bcmp($ultimo) < 1) {
  17.     say substr "$ceros$primero", -$largo_ultimo;
  18.  
  19.     $primero->binc();
  20. }
Coloreado en 0.001 segundos, usando GeSHi 1.0.8.4

Este programa genera la misma secuencia que el anterior... 125.000 números, pero en apenas ¡¡7 segundos!!

Y yo que pensaba que la operación de añadir los ceros retrasaría al programa...

Nada, nada... he tenido un momento de desconfianza hacia Perl. Espero no tener más ;)
JF^D Perl programming & Raku programming. Grupo en Telegram: https://t.me/Perl_ES
Avatar de Usuario
explorer
Administrador
Administrador
 
Mensajes: 14480
Registrado: 2005-07-24 18:12 @800
Ubicación: Valladolid, España

Re: Números mayores de 16 dígitos

Notapor FluiD » 2010-05-03 16:50 @743

Es un pasote de rápido y con muy poquitas líneas de código, eres un crack.

Funciona perfecto.

He estado trasteando para intentar hacerlo de forma inversa (si el primero es mayor que el último), manteniendo los ceros también y me pierdo en el while(). Tengo en cuenta la resta del largo de los ceros y demás, pero...

¿Qué son las líneas?
Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
$primero->bcmp($ultimo)
$primero->binc();
Coloreado en 0.001 segundos, usando GeSHi 1.0.8.4


Por cierto, te parecerá una absurdez pero estoy flipando la manera tan sencilla en como has hecho:
Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
  1. say substr "$ceros$primero", -$largo_ultimo;
Coloreado en 0.001 segundos, usando GeSHi 1.0.8.4

para pintar los ceros y solo extraer la parte que interesa, a veces es mejor tener una buena idea de cómo hacer algo que liarte a hacer bucles y y cálculos :-) ¡Sí, señor!

Oye, en serio, que estoy muy agradecido por tu tiempo.

Un saludo,

MiG.G
FluiD
Perlero nuevo
Perlero nuevo
 
Mensajes: 14
Registrado: 2009-10-16 14:38 @651

Re: Números mayores de 16 dígitos

Notapor explorer » 2010-05-04 07:11 @341

FluiD escribiste:He estado trasteando para intentar hacerlo de forma inversa (si el primero es mayor que el último), manteniendo los ceros también y me pierdo en el while().

Si el último es menor que el primero, puedes, o bien hacer un bucle que vaya "hacia atrás", o bien darle la vuelta a los números:
Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
if ($ultimo < $primero) {
   ($primero, $ultimo) = ($ultimo, $primero);
}
Coloreado en 0.001 segundos, usando GeSHi 1.0.8.4
y el resto del programa queda igual que antes.

FluiD escribiste:¿Qué son las líneas?
Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
$primero->bcmp($ultimo)
$primero->binc();
Coloreado en 0.001 segundos, usando GeSHi 1.0.8.4
Son dos funciones de Math::BigInt.

* $primero->binc() : incrementa en 1 el valor de $primero. Sería lo mismo que $primero++

* $primero->bcmp($ultimo) : compara el valor de $primero con el valor de $ultimo:
+ Si $primero < $ultimo, devuelve -1.
+ Si $primero == $ultimo, devuelve 0.
+ Si $primero > $ultimo, devuelve 1.
Hace la misma función que $primero <=> $ultimo.
Así que

$primero->bcmp($ultimo) < 1

sería lo mismo que probar el valor de verdad de

$primero <=> $ultimo < 1

que, en este caso, es lo mismo que decir sea cierta la expresión

$primero - $ultimo < 1

ya que nos interesa que sea cierta cuando el valor de <=> sea -1 o 0. Y esta última expresión es igual a

$primero - $ultimo <= 0

que, matemáticamente, es lo mismo que

$primero <= $ultimo

que es justo lo que queremos para el bucle while(): repetir mientras no lleguemos a superar el valor de $ultimo.

La razón de usar funciones de Math::BigInt en lugar de los operadores normales de Perl, es que se aumenta un poco más la velocidad, al llamar a la librería de forma directa.

FluiD escribiste:Por cierto, te parecerá una absurdez pero estoy flipando la manera tan sencilla en como has hecho:
Sintáxis: [ Descargar ] [ Ocultar ]
Using perl Syntax Highlighting
  1. say substr "$ceros$primero", -$largo_ultimo;
Coloreado en 0.001 segundos, usando GeSHi 1.0.8.4
Pues el caso es que es una operación muy común, para rellenar por la parte izquierda. Por estos foros lo verás a menudo.

Lo normal sería usar sprintf("%016d") para rellenar con ceros a la izquierda (en este ejemplo, 16), pero como estamos hablando de números muy grandes, pues mejor lo hacemos como una operación de cadenas de caracteres.
JF^D Perl programming & Raku programming. Grupo en Telegram: https://t.me/Perl_ES
Avatar de Usuario
explorer
Administrador
Administrador
 
Mensajes: 14480
Registrado: 2005-07-24 18:12 @800
Ubicación: Valladolid, España

Re: Números mayores de 16 dígitos

Notapor FluiD » 2010-05-04 17:29 @770

Muchas gracias, sé que todo esto es muy básico, pero aun así, para mi eres como el dios del Perl :D

Un saludo.

MiG.G
FluiD
Perlero nuevo
Perlero nuevo
 
Mensajes: 14
Registrado: 2009-10-16 14:38 @651

Anterior

Volver a Básico

¿Quién está conectado?

Usuarios navegando por este Foro: No hay usuarios registrados visitando el Foro y 5 invitados

cron