Calculando el Conjunto de Wirt

 

En la página de PuntoPeek encontré el siguiente problema propuesto: "Calcular los n primeros números del conjunto de Wirth".

El conjunto de Wirth se define de la siguiente manera:

"El conjunto de Wirth es un subconjunto de los enteros positivos que cumple con la siguiente regla: el 1 pertenece al conjunto y si un numero k pertenece al conjunto entonces los números 2*k+1 y 3*k+1 pertenecen al conjunto."

Ya antes había intentado resolver el problema con un algoritmo que, a pesar de dar la respuesta correcta, resultaba muy ineficiente.

La solución de PuntoPeek es mucho mejor. No calcula números extra.

Por mi parte, no hubiera seguido insistiendo con el problema del Conjunto de Wirth si no hubiera tenido la sensación que la solución es mucho, pero mucho más simple.

Para empezar me basé en la solución propuesta de PuntoPeek. Luego de analizarla a simple vista, noté que todo se reduce a:

1. En un array de tamaño fijo n ingreso el 1 como primer elemento,

2. Recorro todos los números naturales y le pregunto a cada uno: ¿eres el resultado de tomar un número k del array y realizar las operaciones: 3k+1 ó 2k+1? si es así, te añado al array.

3. Me detengo hasta completar todos los elementos del array.

Lo curioso es que el postulado del conjunto de Wirth hace pensar en una operación "y" (and lógica): "Si un número k es elemento del conjunto, también lo son los números 3k+1 y 2k+1", cuando en realidad es un or lógico: cada número k del conjunto genera 3k + 1 y 2k + 1. Entonces, para que otro número i sea del conjunto debe cumplir cualquiera de estas dos condiciones: i = 3k + 1 ó i = 2k + 1. O mejor dicho: (i-1)/3 ó (i-1)/2 debe darnos un número k que ya está en nuestro array..

Con el método de PuntoPeek se buscan los primeros números n preguntando si cumplen cualquiera de las dos condiciones. La variable k es conocida, pues ya está almacenada en el array.

En Python, el algoritmo es:

Uso datos de tipo float (coma flotante) para descartar los números que den resultados decimales (la variable i es en realidad un entero con representación flotante). Así evito falsos positivos o falsos rechazados por los errores que se introducen al redondear la división entre enteros.

El resultado es:

 

Lo interesante de este algoritmo es que sirve para generar cualquier serie de números que cumplan una condición. Basta cambiar los primeros elementos del array y la condición, pues lo que en realidad hace es extraer del conjunto de los números naturales todos aquellos números que cumplen una condición.

La serie Fibonacci se genera de la siguiente forma (como no implica divisiones, no necesito números de coma flotante):

 

Para las potencias de 2:

 

Para los factoriales (números que son el factorial de otro):

 

El script completo se puede descargar de aquí.

Lo malo de este algoritmo: para el caso del factorial y las potencias de dos es lentísimo. Tarda horas (literalmente) en calcular potencias de 2 mayores a 2^20, y lo mismo sucede para factoriales mayores al factorial de 10. Ir preguntando a cada número si cumple una condición es muy ineficiente para series de números cuyos valores crecen de forma geométrica o exponencial.

 

www.000webhost.com