Mostrando entradas con la etiqueta primos. Mostrar todas las entradas
Mostrando entradas con la etiqueta primos. Mostrar todas las entradas

jueves, noviembre 26, 2009

La fórmula preferida del profesor


Buenas noches,
Llevo varios días leyendo este libro que ha sido un gran éxito en Japón. Hablamos de un millón de ejemplares vendidos en dos meses y otro millón vendido en formato bolsillo.
Es una novelita corta para lo que se lleva ahora, apenas 298 hojas. Voy por la 189 y la verdad es que todavía no he encontrado demasiado contenido matemático. Hace ya bastante, me di cuenta de que la historia que se cuenta es otra.
Los más observadores ya habréis visto la ecuación de la portada. Probablemente la más bella de todas las imaginables que consigue relacionar los conceptos más significativos de todas la matemáticas: las constantes pi, e, el 0, el 1, y el número imaginario i.
Todavía no sé si realmente esta ecuación se llegará a analizar en el libro :-(
Lo que quería escribir aquí era un par de párrafos que me han parecido realmente buenos. Hoy escribiré uno y mañana la continuación para que no resulte pesado:
pag. 187.
"Aun cuando se aclare la naturaleza de los números primos, no digo que la vida se vuelva más fácil o agradable ni que se gane más dinero.
Por supuesto, por más que nos empecinemos en darle la espalda al mundo, muchos son los casos en los que un descubrimiento matemático acaba por aplicarse, en la práctica, a la realidad.
Del estudio de la elipse resultó la órbita planetaria, y de la geometría no euclidiana, la forma del universo mostrada por Einstein.
Los números primos fueron incluso cómplices de la guerra pues sirvieron de base para los mensajes en clave. Resulta horrendo. Pero ése no es el propósito de las matemáticas. Su objetivo es únicamente desvelar la verdad."
de Yoko Ogawa.
Saludos.

martes, octubre 27, 2009

La transformada de Fourier. Parte V. Interpretación gráfica de la derivada


Y hoy: La interpretación gráfica de la derivada

Esto es lo que una vez supiste y luego se te olvidó. Ocurrió cuando tuviste que memorizar un montón de formulines para aprobar el examen. Eras las derivadas precocinadas de un montón de funciones. Aprobaste el examen y al día siguiente te olvidaste de los formulines. Luego el mal trago hizo que olvidaras lo bonito que es el concepto de la derivada.

Pues es esto:

El eje de coordenadas y la curva las he dibujado con el color negro.

Definimos la curva como y = f(x).

Luego hemos definido dos punto bastante próximos de la curva: M0 y M1. Se han pintado en verde. Si las coordenadas de M0 son (x,y), las de M1 son (x + delta x, y + delta y)

La recta que corta la curva en dos puntos se llama secante (secante viene del latín secare que es cortar). La he pintado de azul.

Por último, con color amarillo he dibujado la tangente de la curva en el punto M0.

Si delta x tiende a cero, el punto M1 se va aproximando al punto M0 siguiendo la curva definida por la función.

La secante también cambiará modificando el ángulo que forma con el eje X.
La tangente del ángulo de la secante es igual a delta y / delta x.

A medida que la variación de x tienda a cero, la tangente del ángulo de la secante se va aproximando a la tangente de la curva en el punto M0.


Pues este tipo de gráficas es el que trató Fermat al estudiar máximos y mínimos de funciones. Lógicamente en esos puntos (máximos y mínimos) la tangente del punto es igual a cero. O sea, la derivada de la función en el punto es cero. Por eso Laplace le daba la autoría del descubrimiento a Fermat (¡por su trabajo publicado en 1629!), 120 años antes de nacer Laplace.

Se conoce que el método de las tangentes es el que utilizó Newton para definir la derivada. Aquel trabajo fue entregado a su profesor sobre 1669.

Saludos.

miércoles, octubre 21, 2009

La transformada de Fourier. Parte IV. Historia de la Derivada.

Antes de pasar a desvelar la interpretación gráfica, quería hacer un inciso sobre...

Los creadores de las derivadas e integrales:

Hoy en día se considera que dos personas dieron con las derivadas por métodos diferentes: Leibniz y Newton.

Resulta que Leibnitz fue el primero en publicar, pero parece que Newton había entregado un borrador completo a su profesor unos diez años antes (1669). Luego hubo mucha discusión sobre la autoría, más que nada porque se lo disputaban entre los Británicos y Alemanes.

Newton llego al concepto a partir de las tangentes (lo veremos cuando veamos la interpretación gráfica) y en cambio Leibnitz analizó la velocidad de un móvil (lo que vimos el otro día).

La verdad es que no hay nada claro. Podéis echar un vistazo a la interesante vida de Leibnitz en wikipedia y comprobaréis que en su época en la casa de Hanover pudo llegar a conocer los manuscritos de Newton.

De todas formas, aunque llegara a conocer ese trabajo (que nadie lo sabe seguro), lo que Leibnitz publicó era un método totalmente diferente. Además, según dicen los estudiosos de sus escritos, toda la simbología y terminología actual proviene de su mano y es mucho más intuitiva que la de Newton.

Otros grandes matemáticos como Laplace (1749-1827) han proclamado que el verdadero precursor del cálculo diferencial fue el gran Fermat en 1629 por los métodos que publicó para el estudio de máximos y mínimos de funciones.

Saludos.

lunes, octubre 19, 2009

La transformada de Fourier. Parte III. Derivadas.


Si entendiste la parte dos entonces no hace falta que sigas. Lo que viste es la definición de la derivada.

Sea una función definida en un intervalo.

y = f(x)

Para cada valor de x en el intervalo le corresponde un valor determinado de la función.

Si el argumento x toma un incremento delta x, entonces la función tomará un incremento delta y. Siguiendo un poco más allá obtenemos lo siguiente:

Ahora viene lo bonito.

En matemáticas los números racionales son aquellos que se pueden poner en forma de fracciones o quebrado:

5/4 = 1,25

La razón en matemáticas es una proporción.

En nuestro caso vamos a ver la proporción en la que varía delta y respecto a delta x. Para ello dividimos la dos partes de la igualdad entre delta x:



Por fin, viene la definición de la derivada:

Es hacia donde tiende la razón (la proporción entre el incremento de y, y el incremento de x), cuando el incremento de x tiende a un valor tremendamente pequeño aunque superior a 0.

O sea,

Es el límite de la razón, cuando delta x tiende a 0.


Pero al final, ¿qué significa todo esto?

Con tanto símbolo hemos perdido un poco la perspectiva y ya es hora de verle la utilidad práctica.

En el próximo post veremos lo que es la derivada viendo la gráfica de una función.

Saludos.

miércoles, octubre 14, 2009

La transformada de Fourier. Parte II. Introducción a la Derivada.


Para ver la parte I.

Empecemos por Jean-Baptiste Joseph Fourier, nacido en 1768.

Este gran matemático y aventurero estudió la propagación del calor, en un trozo de hierro. En su estudio llega a transformar una función encontrando un equivalente compuesto por sumas de funciones periódicas continuas. Se trato de una revolución tal que los matemáticos de la época tardaron 30 años en aceptarlo.

La asombrosa definición de las derivadas:

Seguro que lo estudiasteis en su día y lo teníais olvidado. Intentaré explicarlo de una forma amena:

Os han regalado el Ferrari F430 que se ve en la imagen.
El coche acelera de 0 a 100 en 3,7 segundos.

Antes de que existieran las derivadas el problema era la expresión matemática de la velocidad a lo largo de los 100 metros.

Esta claro que el espacio recorrido (s) depende del tiempo transcurrido (t).
O sea que s=f(t)




En un momento concreto de la carrera el coche habrá recorrido s metros en t segundos. Un instante más tarde habrá recorrido un poquito más. La velocidad media en ese pequeño intervalo será: delta s / delta t.

Ahora bien, la velocidad media en un intervalo delta t no será igual de precisa al principio de la carrera (momento en el que el coche se mueve lento) como al final cuando va a toda velocidad.

Para obtener mayor precisión, hay que coger un intervalo delta t pequeñísimo.

El límite hacia el cual tiende la velocidad media cuando delta t tiende a 0 es la forma precisa de determinar la velocidad del movimiento en cada instante.

Teniendo en cuenta lo siguiente...
Hemos llegado a la definición de la velocidad del movimiento no uniforme. El concepto de límite ha sido necesario para llegar a él.

Mañana la definición de la derivada.

Saludos.

viernes, octubre 09, 2009

La transformada de Fourier. Parte I.

Entre otras miles de aplicaciones la transformada de Fourier sirve también para la multiplicación rápida de números gigantes y este tipo de cosas sirven para ciertas manipulaciones con números primos.

Pero bueno, empecemos por el principio... Es un camino largo pero merece la pena.

Por cierto, me he comprado una tableta Bamboo Fun de Wacom que me va a facilitar la entrada de gráficos a mano. Esto......, veréis que todavía no controlo demasiado el lápiz electrónico este :-)

¿Recordáis las funciones trigonométricas? Sí, esas que utilizábamos con triángulos en matemáticas y rampas en física:

En un triángulo rectángulo, siendo x la hipotenusa:



Vaya, me he dejado la rayita de la base del triángulo.

Si nos centramos en el seno y el coseno y pensamos en una circunferencia de radio 1, y grados de 0 a 360 que tiene toda una circunferencia tenemos lo siguiente:

Pues ahora en el circulito ya se ve que:

1.-Con 0 grados el seno vale 0 y el coseno 1.
2.-Con 45 grados el seno es 0,707... (por cierto, hecho de menos una calculadora científica en mac osx. En esto gana Windows). El coseno lógicamente vale lo mismo.
3.-Con 90º el seno vale 1 y el coseno 0.
4.-Con 180º el seno vale 0 y el coseno -1.
5.-Con 270º el seno vale -1 y el coseno 0.
6.-Con 360 grados se vuelve a la situación inicial: seno 0 y coseno 1.
7.-A partir de una vuelta el comportamiento tanto del seno como del coseno se repite.
8.-Y así hasta el infinito.

Ahora ya pasamos a lo interesante. Las curvas del seno y el coseno. En los puntos 7 y 8 ya vemos que van a ser funciones periódicas cuya curva se repite indefinidamente.

Si calculáramos un número suficiente de valores tendríamos las siguientes curvas:


Sí, unos gráficos desastrosos...

Imaginaros esas dos curvas hasta el infinito. Realmente una empieza valiendo 0 y la otra 1, pero si no vemos el comienzo como curvas son iguales.

En general se dice que son curvas sinusoidales. En Wikipedia tienen un gráfico decente.

En este tipo de curvas sinusoidales hay unas propiedades interesantes:

La amplitud (A) es un multiplicador que hace que se supere el límite de "altura" máxima en 1.
Se define el período de oscilación (T) que para entendernos es cada cuantos valores de x (tiempo) se repite la curva.
La inversa (1/T) es la frecuencia. Si la curva se repite cada menos tiempo (x), la frecuencia es mayor.

Lo más importante de todo es la frecuencia porque se ha demostrado que un nuevo tipo de gráfico basado en las frecuencias de curvas sinusoidales tiene unas increíbles aplicaciones prácticas.

Continuará...

Saludos.

martes, septiembre 29, 2009

Un rayo de luz en la cueva de los primos gigantes

Hola,
Alguno recordará un tema que quedó pendiente respecto a los primos gigantes.

No pude explicar el funcionamiento de un algoritmo que básicamente convertía un número binario gigante (que se sabía que era primo) en decimal. Lo extraño era que lo hacía terriblemente rápido. Todo gracias a la transformada rápida de Fourier.

Hace unos meses fui a una biblioteca universitaria y encontré un libro sobre aplicaciones de la transformada de Fourier pero en ningún sitio ponía que se pudiera utilizar para una conversión de este tipo. Sobre todo se utiliza en temas como conversión analógica-digital de señales.

Hoy me he pasado un buen rato buscando entre mis libros de matemáticas de la uni, e internet a partes iguales. Al final he encontrado en Internet la explicación de todo el misterio. La utilidad de la transformada para la conversión se la debemos a un trabajo de los año 70 de Arnold Schonage de la Universidad de Bonn.

Ahora ya es tarde y no me da tiempo a explicarlo.

En el próximo post el destripe del programa C más curioso: http://aitoreus.blogspot.com/2009/05/programas-c-curiosos.html

Por cierto, que el premio se lo deberían dar a Arnold Schonage, o como mínimo debería ser compartido. O ya que no es compartido lo podían haber incluido como comentario en el programa. Trabajar con la mejor máquina no es tan meritorio como encontrar el algoritmo más rápido.

Saludos.

domingo, julio 19, 2009

Casi mejor no tocar la afinidad

Hola a tod@s,
Por fin de vacaciones, pero antes de marchar de viaje me gustaría saber si lo que ocurrió con el programa C le ha pasado a más gente, y por qué.

Recordar que teníamos un programita que ejecutamos marcando la afinidad a un procesador concreto y tardó 11 segundos más que sin toquetear la afinidad.

Primero una pequeña aclaración que analizaremos otro día:
El ordenador que utilicé en esas pruebas es un ADM Phenon x3, el cual tiene un único procesador con 3 cores (o nucleos en castellano). Windows nos dirá que hay tres procesadores y que podemos determinar la afinidad para forzar la ejecución en los procesadores que queramos, pero sólo hay un procesador y los núcleos comparten ancho de banda en el acceso a la caché L3 (memoria compartida por todos los cores). La caché L2 es la memoria que tiene cada core.

Bueno, ahora vamos a leer lo que otro (Iñaki Ayucar) ha analizado:

Artículo original en inglés: http://www.codeproject.com/Articles/35679/Parallel-computing-and-processor-affinity-Never-underestimate-the-Windows-Vista-Scheduler.aspx?display=PrintAll

Y en castellano: http://graphicdna.blogspot.com/2009/01/programacion-paralela-y-processor.html

¿Cual es el resumen?

1.-Al menos habría que abril un hilo por core físico en la máquina. Ya, pero el programa no es mio y el autor metió programación paralelizada en el programa original Fortran, pero no en el C. Esto será una mejora para el después de vacaciones.

2.-Este punto lo voy a poner tal cual está, porque lo explica de maravilla. El autor nos dice: "En un mundo ideal, una tarea simple, que no va a ser paralelizada más y que vive solita (no con las docenas de vecinos que un proceso tiene en un SO moderno), se gestiona mejor con una sola CPU, porque esto incrementa los aciertos de cache y elimina el consumo de la infraestructura de cambio entre hilos. Pero en la vida real, los procesos son interrumpidos por las operaciones del sistema, IO, otros procesos y muchas otras cosas. Una maquina multi-núcleo es perfecta para manejar todas esas interrupciones, porque puede repartirlas por los núcleos existentes, pero si comenzamos a fijar nuestras aplicaciones a CPUs específicas, la capacidad del sistema para evitar bloqueos y esperas se reduce considerablemente".

Uff, este punto número 2 lo podemos separar en otros cuantos:

2.1.-Cuidado, porque puede ocurrir que la ejecución en tu viejo portátil con una CPU pueda ir mejor, ya que hay más aciertos de cache y hay menor consumo por cambio de hilos.

2.2.-El hecho de que hay un montonazo de procesos extra en ejecución con sus respectivas interrupciones, etc. etc. da pie a que en tu nuevo pc multinucleo la cosa funcione mejor.

2.3.-En el caso de que estemos en nuestro pc nuevo multinucleo, fijar las CPU-s puede perjudicar ya que el sistema tiene menos opciones para evitar los bloqueos, esperas, etc.

En el artículo pasa a describir el tema de cómo fijar la afinidad y luego viene lo interesante:

Las pruebas:

Las pruebas se componen por 5 partes:

Parte I: Programa NO multihilo (como el nuestro).
Si se establece la afinidad el programa tarda 28 segundos más que si no se hace nada.
Mejor dejar hacer a Windows.

Parte II: Dos hilos de ejecución (principal + secundario con algún cálculo).
El programa tarda 3 segundos menos si no se establece la afinidad.
Mejor dejar hacer a Windows.

Parte III: Tres hilos de ejecución (principal + dos hilos de cálculo).
El programa tarda 2 segundos menos si no se establece la afinidad.
Mejor dejar hacer a Windows.

Parte IV: Cinco hilos de ejecución (principal + cuatro hilos de cálculo).
Empate técnico (bueno, por décimas gana la afinidad).
Windows ha perdido por la mínima.

Parte V: Nueve hilos de ejecución (principal + ocho hilos de cálculo).
El programa tarda 3 segundos menos si no se establece la afinidad.
Mejor dejar hacer a Windows.

Los resultados cuadran totalmente con lo que nos ocurría en el programita.

Parece que cuando la programación es multihilo es mejor no tocar la afinidad, aunque la diferencia es pequeña. Lo que está claro es que si el programa no es multihilo hay grandes diferencias en tiempo de ejecución siendo más rápido sin tocar la afinidad.

Saludos.

lunes, julio 13, 2009

Siguiendo los consejos recibidos el otro día la semana pasada hice la prueba de ejecución "con afinidad".

No fijé la afinidad por programación. Simplemente lancé la ejecución, y a toda leche modifiqué la afinidad con el Administrador de tareas de Windows.

Estoy con mi viejo portátil con un solo procesador por lo que no os puedo enseñar la opción, pero la explico...



Ahí arriba está el Administrador de tareas. He pinchado en una tarea y tengo opción de subir o bajar la prioridad de un proceso.

Si tuviera más de un procesador, debajo de la opción de prioridad tendríamos algo así como "afinidad". Al pinchar aparecen un check box donde podemos elegir uno o varios procesadores. Yo elegí el procesador 0.

Así fue la ejecución:

El PC estaba ligerito de trabajo:


Lanzo el proceso, pongo en marcha el cronometro y establezco la afinidad:

En la imagen de arriba ya se ve que un procesador se pone al 100% mientras que los otros dos prácticamente vuelven a la situación inicial.

Al final termina el proceso y todo vuelve a su ser:


Ahora lo importante, ¿qué ha pasado con el tiempo de ejecución?

Pues ha tardado 18 minutos 42 segundos. Once segundos más que sin tocar la afinidad.

Creo que vamos a tener que enterarnos de la forma de funcionamiento de estos procesadores AMD64 para entender algo.

Saludos.

lunes, julio 06, 2009

Could faster chips translate into slower computers?

Ya que el otro día mejoramos la ejecución en el super PC-1 (18 minutos 31 segundos), se me ha ocurrido compilar y ejecutar el programa Visual C++ en mi viejo portátil.

Con el Dev C++ sin optimizar tardaba 21 minutos 9 segundos y tenía curiosidad por saber lo que iba a mejorar.

Han sido ni más ni menos 14 minutos 20 segundos, lo que significa que ¿es la máquina más rápida? ¡Imposible!

Algo raro está ocurriendo... el resultado no es lógico, o tal vez sí...

Así es la utilización de la CPU al comenzar la ejecución. 100% de uso del único procesador hasta el final.



Y así la relajación final:


El AMD64 es multinucleo y se comporta diferente. El XP lo ve como si fueran 3 procesadores. El caso es que el uso de la CPU se mantiene bastante constante en un 34% de uso.


Cuando termina:


Esto hay que estudiarlo un poco pero el punto de partida puede ser este:

Could faster chips translate into slower computers?

Esta es la pregunta que se hicieron en este artículo de la revista Fortune e indica una posibilidad que puede estar ocurriendo, o tal vez no.

Saludos.

jueves, julio 02, 2009

Prueba con Visual C++

Seguimos con el programa de generación del primo más grande (del año 1994).

Hicimos una prueba de compilación con Dev C++ en un PC decente. El resultado fue un poco decepcionante porque los 27 minutos que tardaba eran 6 minutos más que en un viejo portátil XP.

Es verdad que no me preocupé demasiado por ajustar los parámetros del compilador. Pero he preferido pasar directamente al compilador comercial por excelencia de Windows XP: Visual C++.

1.-Instalo Visual Studio 2005 (en los próximos días probaremos el Visual Studio 2008).

2.-Iniciamos un nuevo proyecto Visual Studio. Seleccionamos el lenguaje Visual C++ y "Aplicación de consola Win32"



3.-Así tendremos el proyecto vacío:


4.-Añado el programa C original de Slowinski y lo adapto ligeramente para el Visual C++. Concretamente hay que:
  • Modificar la sintaxis de main().
  • Comentar las líneas para el control de tiempos.
  • Comentar la cabecera que Visual C++ ha añadido por defecto: stdafx.h
  • Añadir la cabecera stdlib.h para poder usar la función atoi.

Al final de este post añado el código fuente entero para el que lo quiera probar.

5.-Lanzamos la compilación-ejecución y a esperar. En total ha tardado 23 minutos 45 segundos. Mejor que el Dev C++ pero un resultado bastante pobre. Ahora toca hacer unos pequeños ajustes.

6.-Priorizamos el rendimiento frente al tamaño del ejecutable:

Para ello vamos al Menú Proyecto, propiedades del proyecto, propiedades de configuración, C/C++, Entramos en la sección "Optimización" y seleccionamos "Maximizar velocidad" que se corresponde con la opción /O2.



Ahora intentamos la compilación y...SORPRESA. Se produce un error que dice lo siguiente: "las opciones de líneas de comandos '/O2' y '/RTC1' son incompatibles".


Bueno, pues hay que volver a Proyecto, propiedades del proyecto, propiedades de configuración, C/C++, General. Vamos a "formato de la información de depuración" y seleccionamos "Deshabilitado".

Además de esto, también hay que ir a C/C++, Generación de Código, "Comprobaciones básicas en tiempo de ejecución" y marcarlo como PREDETERMINADO.



Pues aunque volvamos a intentar compilar nos sale un nuevo error: '/Gm' requiere '/Zi' o '/ZI'.


Jeje, ya estaremos un poco aburridos tanto toquetear, pero esta es la última vez (de momento). Volvemos a Proyecto, propiedades del proyecto, propiedades de configuración, C/C++, Generación de código. Vamos a "habilitar regeneración mínima" y ponemos que NO.


7.-Ahora sí, ya podemos compilar y ejecutar tranquilamente. Y el rendimiento mejora bastante. El programa tarda ahora 18 minutos 31 segundos. Nuevo record, aunque todavía insuficiente.

Saludos.

Ahora el código fuente... (cuidado, algunos símbolos se han podido modificar. Cosas de blogger):


// Programa visualizador. 2009-07-02.

//#include "stdafx.h"

#include
#include
#include
#include

//Para que funcione la función atoi es necesario incluir stdlib.h.
#include

// Pequeña modificación en main:
//main(argc, argv)
//int argc;
//char *argv[];

int main(int argc, char* argv[])
{
#define EXP 859433 /* will calculate 2^859433 - 1, the 33rd Mersenne prime */
//Quitamos el cálculo de tiempos:
//double t, ftp0, ftp1;
double c0 = 0, c1 = 0, c2 = 0;
int n;
int *a, *b, *c;
int ddigit = 4; /* number decimal digits per part */
int vdig = (int)pow((double)10, (double)ddigit); /* decimal value of part */
int i, j, k, maxpart, mx;
unsigned int size;
char format[64];

n = EXP;
if (argc == 2) /* command-line param for new n? */
n = (atoi(argv[1]) > 0) ? atoi(argv[1]) : EXP;
printf("calculating 2^%d - 1\n", n);

size = (n+1)*sizeof(int);
printf ("allocating %d bytes for working arrays\n", 3*size);

if ((a = (int *)malloc(size)) == NULL) {
fprintf(stderr, "cannot allocate %d bytes for a\n", size);
exit(1);
}
if ((b = (int *)malloc(size)) == NULL) {
fprintf(stderr, "cannot allocate %d bytes for b\n", size);
exit(1);
}
if ((c = (int *)malloc(size)) == NULL) {
fprintf(stderr, "cannot allocate %d bytes for c\n", size);
exit(1);
}

for (i = 1; i <= n; i++) { a[i] = 0; /* set results array to null */ b[i] = 2; /* load up multiplicand array with n 2s */ c[i] = 0; /* set carry array to null */ } a[1] = 1; /* set initial intermediate result to 1 */ mx = 1; //#ifdef FTIME // ftime(&tp0); //#else // gettimeofday (&tp0, (struct timezone *)NULL); //#endif //#pragma _CRI parallel shared(n, a, b, c, mx, vdig) private(i, j) for (j = 1; j <= n; j++) { //#pragma _CRI taskloop vector for (i = 1; i <= mx; i++) { a[i] *= b[j]; } //#pragma _CRI taskloop vector for (i = 2; i <= mx+1; i++) c[i] = a[i-1]/vdig; //#pragma _CRI guard if (c[mx+1] > 0)
mx++;
//#pragma _CRI endguard

//#pragma _CRI taskloop vector
for (i = 1; i <= mx; i++) { a[i] = (a[i] % vdig) + c[i]; } } //#pragma _CRI endparallel /* propogate carry through one last time */ j = 0; for (i = 1; i <= n; i++) { k = a[i] + j; a[i] = k % vdig; j = k/vdig; } //#ifdef FTIME //ftime(&tp1); //ftp0 = (double)tp0.time+(double)tp0.millitm/(double)1000000; //ftp1 = (double)tp1.time+(double)tp1.millitm/(double)1000000; //#else //gettimeofday (&tp1, (struct timezone *)NULL); //ftp0 = (double)tp0.tv_sec+(double)tp0.tv_usec/(double)1000000; //ftp1 = (double)tp1.tv_sec+(double)tp1.tv_usec/(double)1000000; //#endif //t = (ftp1-ftp0 == 0) ? 0.000001 : ftp1-ftp0; //printf("wall time for main loop: %.5lf seconds\n", t); maxpart = 0; for (i = 0; i <= n; i++) if (a[i] != 0) maxpart = i; printf ("max parts used: %d (each part is %d decimal digits)\n", maxpart, ddigit); /* subtract one from answer and see if we match Slow's number */ a[1]--; if (a[1] < i =" 2;" j =" 0;" i =" maxpart;">= 1; i--) {
printf (format, a[i]);
j++;
if (j % 15 == 0)
printf ("\n");
}
printf ("\n");

return 0;
}

lunes, junio 29, 2009

Pequeña decepción

Os comento la prueba que quería hacer esta tarde.

Tenía dos equipos idénticos. Dos PC-s que le dan mil vueltas a este portátil en el que estoy escribiendo. Voy a poner unos datos para verlo:

1.-Portátil donde realicé la prueba del visualizador.exe:

2.-PC1 y PC2:

  • AMD Phenom X3 64.
  • HP Compaq DC5850.
  • Triple-Core Processor
  • 2,29 GHz,
  • 3,23 Gb Ram.

El PC1 tenía XP instalado y ahí he instalado el Dev C++ 5.0 beta 9.2 con el compilador Mingw/GCC 3.4.2

En el PC2 tenía Ubuntu 9.04 que trae instalado un compilador FORTRAN.

La idea era esa... Compilar el programa C en PC1 y el equivalente FORTRAN en PC2, comparar y quedarnos con el más rápido. Aquí están los dos enlaces a los programas

El problema ha sido que la ejecución en el programa C recién compilado en el super PC1 ha tardado 27 minutos 05 segundos. Cuando el mismo programa en el viejo portátil tardaba 21 minutos 09 segundos.

¿¿¿¿¿??????
¿Los viejos cacharros son mejores? No.
¿Windows XP no es capaz de sacar provecho de la máquina? Tampoco.

No, no es nada de eso. Tiene más que ver con el compilador, más concretamente si es capaz de utilizar los recursos de la máquina, o los está infrautilizando.

El Dev C++ 5.0, tal y como lo descargas lleva debajo el compilador MinGw 3.4.2. Realmente la última versión disponible es la 5.1.4 como se puede ver en http://sourceforge.net/project/showfiles.php?group_id=2435

Ahora lo que toca es buscar el mejor compilador C posible para el AMD64 con Windows.

Saludos.

domingo, junio 28, 2009

Creación del primo más grande

Continuando con el programita del amigo Slowinski que comentábamos el otro día, es una buena idea compilarlo y descubrir así el primo más grande de 1994: es el número de Mersenne número 33, 2^859433 - 1
Este dato podemos comprobarlo en la lista oficial: http://primes.utm.edu/mersenne/index.html

De los dos programas que había he elegido el de C. Nos descargamos un buen compilador libre como Dev C++, y abrimos un nuevo proyecto C. Lo he llamado "Visualizador" por ponerle un nombre cualquiera, aunque el programa hace más que visualizar: construye el primo desde 0.



Directamente copiamos el programa fuente al editor:



Comentamos todas las líneas que han añadido para el cálculo de tiempos y añadimos una línea al final que es bastante importante:

getchar();

Sin esta línea, tal y como está el programa original, el primo se escribe y después el programa termina sin que podamos verlo. Sólo notaríamos que termina el proceso "visualizador" sin dejarnos comprobar si realmente escribe el primo de forma correcta.

Le damos a compilar y ejecutar:


El pobre portátil a punto de explotar al 100% de CPU:

Después de 21 minutos y 9 segundos (en mi máquina) tendremos el super primo:

Son 258.716 dígitos ni más ni menos.

Mañana si hay tiempo la explicación del programa.

Saludos.

martes, junio 16, 2009

Un pequeño avance...

Hola,
Escribo a toda leche que es tarde y mañana es día de escuela.

Creo que he encontrado algo importante que puede servir para entender el programa aquel C que servía para visualizar un número primo de Mersenne concreto.

En agosto del año pasado explicaba lo que era un número de Mersenne, y por qué era una fuente importante de números primos.

Bueno, vayamos al grano que como he dicho es bastante tarde.

En un primer momento lo que hice fue poner el programita C de una forma legible e intentar entender lo que hacía. Craso error. No hay forma de entenderlo.

El método es al revés. El autor dice que ha utilizado la Transformada Rápida de Fourier para convertir el número de Mersenne binario a decimal. Hay que estudiar la transformada para entender el programa y no al revés. La verdad es que no da ninguna pista y no explica el programa en ningún lugar.

Pero buscando y buscando, me encuentro con esta página de enlaces: http://www.mersenne.org/various/freeware.htm

Y precisamente en ese lugar hay un enlace a "FORTRAN and C versions of a program to compute Mersenne numbers in decimal so that they may be printed. The algorithm is credited to Slowinski of CRI."

Aaaamigo!!! Así que un tal Slowinski del Cray Research, Inc. (CRI), ha desarrollado un algoritmo para la conversión rápida a decimal.

Este es el amigo Slowinski que aparece como colaborador del proyecto:

Mañana seguimos, a ver si éste nos explica el algoritmo con un poco de suerte.

Saludos.

domingo, junio 07, 2009

Atascado

Hola amig@s,

Siento no haber escrito nada en una semana, pero estoy atascado.

En el post anterior, vimos el programa C que había utilizado la transformada rápida de Fourier para convertir de binario a decimal el super primo (según palabras del autor).

Pues bien, no he sido capaz de encontrar en Internet la aplicación de la transformada para realizar la conversión. He ido a la feria del libro de mi ciudad y en los libros de matemáticas nada. Hoy mismo que es domingo he pasado por una librería que no cierra nunca y aunque he ojeado bastantes libros nada de nada.

Sí que he visto que hay libros que sólo tratan de la transformada de Fourier y tengo la esperanza de encontrar alguno en la biblioteca de la UPV (Universidad del País Vasco), pero hasta mañana no lo puedo saber.

Si alguien sabe del tema le agradecería alguna pista, pero recordad que no se trata de los típicos ejemplos de la transformada, sino de la conversión entre bases diferentes.

Saludos.

lunes, septiembre 08, 2008

Importante aclaración sobre el programa de Mersenne

Volviendo al tema del programa de Mersenne, es conveniente que explique un par de cosas sobre como se almacenan los números.

En el programa los números se almacenan en un array de caracteres donde el dígito de más peso se encuentra a la derecha y el de menos peso a la izquierda (justo al revés de como escribimos los números).

Además de ello, indico el final del número con el caracter "f" de FIN.

El número 1.024 se almacena como 4201f.

Como el programa no da la vuelta a los dígitos hay que interpretar correctamente el resultado.

Saludos.

jueves, septiembre 04, 2008

Eureka



Por fin he podido cantar eureka. Hoy 4-9-2008 a las 16:32 mientras veía un programa que creo que se llama "Pasalo".


Después de unas cuantas películas y series vistas mientras programaba he podido terminar el programita que calcula los númerso de Mersenne. Todo gracias a los anuncios :-)


Ha tenido su complicación y todavía no está garantizado que funcione en el 100% de los casos. El problema es que pequeños errores no se notan con números de Mersenne pequeños y el programa puede estar mal y dar un montón de resultados buenos. Algún errorcillo ha ocasionado que fallara un número relativamente pequeño como es M20, aunque no fallaban los grandes como M67.


Es un ejercicio interesantísimo ejecutar paso a paso las instrucciones viendo los valores que van tomando las variables. El algoritmo es bastante elegante.


En la imagen de arriba tenemos el flujo del algoritmo para el número de Mersenne 13. Esto es, para n=13. Recomiendo seguirlo para entenderlo.


Se han probado correctamente los números siguientes:

M1=1, M2=3, M3=7, M5=31, M11=2047, M15=32.767, M20=1.048.575, M67=147.573.952.589.676.412.927, M75=37.778.931.862.957.161.709.567.


De todas formas, hay que mantenerlo en cuarentena hasta que consiga los 95 primeros números en un fichero y los compare con los del programa anterior.



Module Module1
Function exponenciacionBinaria(ByVal numero() As Char) As Char()
If (numero(0) = "1" And numero(1) = "f") Or numero(0) = Nothing Then
numero(0) = "2"
numero(1) = "f"
Return numero
End If

If numero(0) = "0" Or numero(0) = "2" Or numero(0) = "4" Or _
numero(0) = "6" Or numero(0) = "8" Then
numero = dividirEntreDos(numero)
numero = exponenciacionBinaria(numero)
numero = multiplicarPorSiMismo(numero)
Return numero
End If

Return multiplicarPorDos(exponenciacionBinaria(restarUno(numero)))
End Function

Function multiplicarPorSiMismo(ByVal n As Char()) As Char()
If n(0) = "f" Then
Return "f"
End If
Dim resultadoParcial(n.Length + 2, n.Length + 100) As Char
Dim resultadoParcialNumerico(n.Length + 2, n.Length + 100) As Integer
Dim resultadoFinal(n.Length + 100) As Char
Dim resultadoFinalNumerico(n.Length + 100) As Integer
Dim contadorColumnasNumeroArriba As Integer = 0
Dim contadorColumnasNumeroAbajo As Integer = 0
Dim contadorColumnasResultados As Integer = 0
Dim llevada = 0
Dim contador As Integer
Dim contadorFilasResultados As Integer

Dim maxDigitos As Integer = 0
Dim longitudN, longitudResultadoParcial As Integer
longitudN = 0
While n(longitudN) <> "f"
longitudN += 1
End While

contadorColumnasNumeroAbajo = 0
For contadorFilasResultados = 0 To longitudN - 1
contadorColumnasResultados = 0
contadorColumnasNumeroArriba = 0
longitudResultadoParcial = longitudN
For contador = 1 To contadorFilasResultados
resultadoParcialNumerico(contadorFilasResultados, contadorColumnasResultados) = 0
resultadoParcial(contadorFilasResultados, contadorColumnasResultados) = "0"
contadorColumnasResultados += 1
longitudResultadoParcial += 1
Next contador
Do
resultadoParcialNumerico(contadorFilasResultados, contadorColumnasResultados) = Val(n(contadorColumnasNumeroAbajo)) * Val(n(contadorColumnasNumeroArriba)) + llevada
llevada = Int(Val(resultadoParcialNumerico(contadorFilasResultados, contadorColumnasResultados)) / 10)
resultadoParcial(contadorFilasResultados, contadorColumnasResultados) = (Val(resultadoParcialNumerico(contadorFilasResultados, contadorColumnasResultados)) Mod 10).ToString
contadorColumnasResultados += 1
contadorColumnasNumeroArriba += 1
Loop Until n(contadorColumnasNumeroArriba) = "f"
If llevada > 0 Then
contador = contadorColumnasResultados
While Val(resultadoParcial(contadorFilasResultados, contador)) = 9
resultadoParcial(contadorFilasResultados, contador) = "0"
contador -= 1
End While
longitudResultadoParcial = contador + 1
resultadoParcial(contadorFilasResultados, contador) = (Val(resultadoParcial(contadorFilasResultados, contador)) + llevada).ToString
llevada = 0
End If
resultadoParcial(contadorFilasResultados, longitudResultadoParcial) = "f"

If longitudResultadoParcial > maxDigitos Then
maxDigitos = longitudResultadoParcial
End If
contadorColumnasNumeroAbajo += 1
Next contadorFilasResultados

Dim cuentaDigitos As Integer
For contador = 0 To longitudN - 1
cuentaDigitos = 0
While resultadoParcial(contador, cuentaDigitos) <> "f"
cuentaDigitos += 1
End While
While cuentaDigitos < maxDigitos
resultadoParcial(contador, cuentaDigitos) = "0"
cuentaDigitos += 1
End While
resultadoParcial(contador, cuentaDigitos) = "f"
Next contador

llevada = 0
Dim i As Integer
For contador = 0 To maxDigitos - 1
For i = 0 To contadorFilasResultados - 1
If i = 0 Then
resultadoFinalNumerico(contador) = Val(resultadoFinalNumerico(contador)) + Val(resultadoParcial(i, contador)) + llevada
Else
resultadoFinalNumerico(contador) = Val(resultadoFinalNumerico(contador)) + Val(resultadoParcial(i, contador))
End If
llevada = Int(Val(resultadoFinalNumerico(contador)) / 10)
resultadoFinal(contador) = Val(resultadoFinalNumerico(contador) Mod 10).ToString
Next i
Next contador
If llevada > 0 Then
If resultadoFinal(contador) = "9" Then
resultadoFinal(contador) = "0"
resultadoFinal(contador + 1) = "1"
resultadoFinal(contador + 2) = "f"
Else
resultadoFinal(contador) = (Val(resultadoFinal(contador)) + llevada).ToString
resultadoFinal(contador + 1) = "f"
End If
Else
resultadoFinal(maxDigitos) = "f"
End If
Return resultadoFinal
End Function

Function restarUno(ByVal numeroArestar() As Char) As Char()
If Val(numeroArestar(0)) > 0 Then
numeroArestar(0) = (Val(numeroArestar(0)) - 1).ToString
Else
Dim i As Integer = 0
Do
numeroArestar(i) = "9"
i += 1
Loop Until Val(numeroArestar(i)) > 0
numeroArestar(i) = (Val(numeroArestar(i)) - 1).ToString
End If
If numeroArestar(0) = "0" And numeroArestar(1) = "f" Then
numeroArestar = "f"
End If
Return numeroArestar
End Function

Function dividirEntreDos(ByVal numero() As Char) As Char()
Dim longitud As Integer = 0
Dim temporal() As Char
Dim dividendoActual As Integer = 0
Dim resultado As String = ""
Dim resto As Integer
Dim indiceActual As Integer = 0
Dim divisionParcialHecha As Boolean
Dim i As Integer

While numero(longitud) <> "f"
longitud += 1
End While

Do
divisionParcialHecha = False
dividendoActual = resto * 10 + Val(numero(longitud - 1 - indiceActual))
Do
If dividendoActual <> 1 Then
resultado = resultado & (Int(dividendoActual / 2)).ToString
resto = dividendoActual Mod 2
divisionParcialHecha = True
Else
If longitud - indiceActual > 1 Then
dividendoActual = 10 + Val(numero(longitud - 2 - indiceActual))
Else
resultado = resultado & "0"
resto = dividendoActual Mod 2
divisionParcialHecha = True
End If
End If
indiceActual = indiceActual + 1
Loop Until divisionParcialHecha
Loop Until indiceActual = longitud
temporal = resultado.ToCharArray
longitud = temporal.Length
For i = 0 To longitud - 1
numero(i) = temporal(longitud - 1 - i)
Next i
numero(longitud) = "f"
Return numero
End Function

Function multiplicarPorDos(ByVal n() As Char) As Char()
If n(0) = "f" Then
Return "f"
End If
Dim resultado(n.Length) As Char
Dim resultadoParcial As Integer
Dim contador As Integer = 0
Dim llevada = 0
Do
resultadoParcial = Val(n(contador)) * 2 + llevada
llevada = Int(resultadoParcial / 10)
resultado(contador) = (resultadoParcial Mod 10).ToString
contador += 1
Loop Until n(contador) = "f"
If llevada > 0 Then
resultado(contador) = llevada.ToString
contador += 1
End If
resultado(contador) = "f"
Return resultado
End Function

Sub Main()
Dim n() As Char
Dim numeroMersenne As String
Dim dosElevadoAn As String
Console.WriteLine("Los números de Mersenne se representan como Mn")
Console.Write("Introduzca el valor de n que quiere calcular: ")
n = Console.ReadLine()
Dim nAlreves(n.Length) As Char
For i As Integer = 0 To n.Length - 1
nAlreves(n.Length - 1 - i) = n(i)
Next i
nAlreves(n.Length) = "f"
dosElevadoAn = exponenciacionBinaria(nAlreves)
numeroMersenne = restarUno(dosElevadoAn)
Console.WriteLine("M" & n & " es igual a " & numeroMersenne)
Console.ReadLine()
End Sub
End Module