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
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:
Using perl Syntax Highlighting
#!/usr/bin/perl
use 5.010;
use Math::BigInt;
Math::BigInt->accuracy(20);
my $primero;
my $ultimo;
## Leemos los números
do {
do {
print 'Introduzca el primer valor: ';
chomp($primero = <>);
$primero = Math::BigInt->new($primero);
}
until ($primero =~ /^\d+$/);
do {
print 'Introduzca el último valor: ';
chomp($ultimo = <>);
$ultimo = Math::BigInt->new($ultimo);
}
until ($ultimo =~ /^\d+$/);
}
until ($primero <= $ultimo);
## Tamaño de los números
my $largo_primero = $primero->length;
my $largo_ultimo = $ultimo ->length;
## Ceros de relleno
my $largo_ceros = $largo_ultimo - $largo_primero;
my $ceros = '0' x $largo_ceros;
## Bucle para el caso de ser de distinta longitud
while ($largo_primero < $largo_ultimo) {
## Cálculo de cuántos números hay hasta que exista cambio de número de ceros
my $resto = Math::BigInt->new(
substr(('1' . ('0' x $largo_primero)) - $primero, - length $primero)
);
## Bucle que genera un rango de números con el mismo número de ceros
while ($resto) {
print $ceros;
say $primero;
$primero->binc;
$resto->bdec;
}
## Reducimos un cero menos
$ceros = '0' x --$largo_ceros;
## Volvemos a comprobar la longitud de los números
$largo_primero = $primero->length;
$largo_ultimo = $ultimo ->length;
}
## Bucle para el caso de ser de la misma longitud
## Del $primero al $ultimo
while ($primero->bcmp($ultimo) < 1) {
say $primero;
$primero->binc();
}
__END__
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...
Using perl Syntax Highlighting
#!/usr/bin/perl
use 5.010;
use Math::BigInt;
Math::BigInt->accuracy(20);
my $primero = Math::BigInt->new('1');
my $ultimo = Math::BigInt->new('125000');
my $largo_primero = $primero->length;
my $largo_ultimo = $ultimo ->length;
my $largo_ceros = $largo_ultimo - $largo_primero;
my $ceros = '0' x $largo_ceros;
while ($primero->bcmp($ultimo) < 1) {
say substr "$ceros$primero", -$largo_ultimo;
$primero->binc();
}
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