domingo, 13 de septiembre de 2020

Números automórficos- parte 4

Éste es el cuarto y último post relativo a los números automórficos, cuyo cuadrado termina con el propio número, como 25 (porque 252 = 625, que termina de nuevo en 25). ¡Por fin encontraremos todos los números automórficos de 15 cifras o menos!

Después de haber probado un algoritmo poco eficaz, vimos algunos patrones que se generaban en la lista que íbamos obteniendo y demostramos un importante resultado:

Corolario: sea x un número NO automórfico. Entonces, a¯x tampoco es automórfico, para cualquier dígito a.

Sabemos que 8 no es automórfico, por lo que tampoco lo será 18, ni 628, ni 7145168. De hecho, conocemos muchísimos números que no son automórficos. ¿Por qué deberíamos ir comprobando cada número como en nuestro anterior algoritmo, cuando podemos saltárnoslos?

Sea {x1, x2,..., xn} la lista de todos los números automórficos de m cifras o menos. Si tiene menos de m cifras, le añadimos al principio tantos 0's como sea necesario (por ejemplo, 00376 tiene 5 cifras). Entonces, TODOS los números automórficos de exactamente m+1 cifras están incluidos (junto con otros muchos que no lo son) en la lista:

{1¯x1, 2¯x1,..., 9¯x1, 1¯x2,..., 9¯xn}

Es decir, en total hay 9*n candidatos. ¡Muchísimos menos de los que teníamos antes! Este algoritmo seguro que es más rápido.

Veamos un ejemplo: tenemos la lista de los números automórficos de 4 cifras o menos, que es {0001, 0005, 0006, 0025, 0076, 0376, 0625, 9376}. Según lo de arriba, para encontrar los de 5 cifras, los únicos candidatos que deberíamos mirar son {10001, 20001, 30001,..., 90001, 10005, 20005,..., 90005, 10006, 20006,..., 90006, 10025, 20025,..., 90025, 10076, 20076,..., 90076, 10376, 20376,..., 90376, 10625, 20625,..., 90625, 19376, 29376,..., 99376}, que son 9*8 = 72 posibilidades (frente a las 90000 que teníamos que comprobar en el algoritmo de fuerza bruta). La mayoría de ellas no cumplen la condición de automorfismo, así que las descartamos tras unos cálculos y nos quedamos únicamente con 90625, que es el único número automórfico de 5 cifras.

Ahora que ya conocemos este algoritmo, el problema se vuelve más sencillo. De la lista de números automórficos de hasta 8 cifras que habíamos obtenido al final de este post, deducimos que tan solo tenemos que probar 9*15 = 135 candidatos para obtener los de 9 cifras. Y, así, poco a poco, vamos aumentando la lista hasta llegar a nuestro objetivo, las 15 cifras. Como hacerlo a mano puede resultar bastante engorroso, de nuevo emplearemos un programa en JavaScript. Al igual que en el otro post, si no sabes de programación, no te preocupes, sáltate esta parte y ve directo al final, a probar el programa tú mismo.

En esta ocasión, necesitaremos tener un vector con todos los números automórficos de menos cifras que los que se están buscando, por lo que crearemos un vector solution, formado por otros vectores, uno para cada longitud. Por ejemplo, para 3 cifras, solution es [[1, 5, 6], [25, 76], [625, 376]]. En cada paso, iremos añadiendo otro vector con más números. Al principio del programa, lo inicializamos a simplemente los números de 1 cifra:

var solution = [[1, 5, 6]];

Al igual que en nuestro anterior programa, éste también funcionará con un parámetro, maxCifras, que es el número de dígitos hasta el cual queremos encontrar números automórficos. Así, pues, comenzamos un bucle para encontrar los números desde 2 cifras (recuerda que los de 1 cifra ya están en solution) hasta maxCifras.

for(cifras = 2; cifras <= maxCifras; cifras++){
  ...
}

Ahora, vamos mirando uno a uno los conjuntos de soluciones que tenemos para números menores y vamos tomando cada uno de sus números. Por ejemplo, para hallar los de 4 cifras, deberíamos mirar primero los de 1, [1, 5, 6], e ir chequeando el 1, el 5 y el 6; después iríamos a los de 2 cifras, [25, 76], y comprobaríamos el 25 y el 76; y, por último, tomaríamos el vector de los de 3 cifras, [625, 376], y comprobaríamos el 625 y el 376.

  for(cifrasMenores = 1; cifrasMenores < cifras; cifrasMenores++){
    len = solution[cifrasMenores-1].length;
    for(i = 0; i < len; i++){
      temp = solution[cifrasMenores-1][i];
      ...
    }
  }

Con cada uno de estos números, hacemos la comprobación que decíamos al principio del post: tenemos que precederlo por cada uno de los dígitos del 1 al 9 y ver si se cumple la condición de automórfico. Si así es, sólo tenemos que añadirlo a un vector temporal que, cuando terminemos de encontrar todos los números de estas cifras, añadiremos a la solución. Para la condición de automórfico, mantenemos la que encontramos en el otro programa, que funcionaba bien.

      for(j = 1; j <= 9; j++){
        temp += (10**(cifras-1));
        if(BigInt(temp)**BigInt(2)%BigInt(10**(cifras)) == temp){
          solutionTemp.push(temp);
        }
      }

Además, hay que tener en cuenta que para cada número de cifras, debemos "vaciar" el vector temporal al inicio de la iteración. El programa completo es:

function automorficosHasta(maxCifras){
  var i, j, solution = [[1, 5, 6]], cifras, cifrasMenores, solutionTemp, len, temp;
  for(cifras = 2; cifras <= maxCifras; cifras++){
    solutionTemp = [];
    for(cifrasMenores = 1; cifrasMenores < cifras; cifrasMenores++){
      len = solution[cifrasMenores-1].length;
      for(i = 0; i < len; i++){
        temp = solution[cifrasMenores-1][i];
        for(j = 1; j <= 9; j++){
          temp += (10**(cifras-1));
          if(BigInt(temp)**BigInt(2)%BigInt(10**(cifras)) == temp){
            solutionTemp.push(temp);
          }
        }
      }
    }
    solution.push(solutionTemp);
  }
  console.log(solution);
}

Como este algoritmo es mucho más eficiente, este programa funciona mejor que el otro. No requiere más que de unas décimas de segundo para calcular todos los números automórficos de hasta 16 cifras, cosa inimaginable con lo que teníamos hasta ahora.

Si quieres probar tú mismo la eficacia de este programa, escribe el número de cifras hasta las que quieras que busque números automórficos y dale al botón.

 


Por fin podemos resolver nuestro problema. Todos los números automórficos de 15 cifras o menos son (aquí están bien ordenados):

1, 5, 6, 25, 76, 376, 625, 9376, 90625, 109376, 890625, 2890625, 7109376, 12890625, 87109376, 212890625, 787109376, 1787109376, 8212890625, 18212890625, 81787109376, 918212890625, 9918212890625, 40081787109376, 59918212890625, 259918212890625, 740081787109376

Indagando un poco en Internet, podemos llegar a esta página con la lista de los primeros números automórficos y comprobamos que nuestro resultado es correcto.

No ha sido fácil, pero hemos llegado al final. Espero que te hayan parecido interesantes estos cuatro posts divulgativos y que te haya entrado el gusanillo por la teoría de números. Hay muchos números con propiedades muy curiosas esperándote.




Aunque el algoritmo que tenemos nos sirve para lo que queríamos hacer, a continuación, puedes probar otro algoritmo aún más rápido, según la discusión en los comentarios. Con éste, puedes hallar muchos más números.

 



4 comentarios:

  1. Desde la Asociación para la Eficiencia Máxima de los Algoritmos le congratulamos por su serie de posts, y le solicitamos una demostración de que los automórficos distintos de 0 y 1 en base 10 se obtienen tomando los últimos dígitos de dos series:

    ...740081787109376
    ...259918212890625

    cuyas cifras siempre suman 9 (a excepción de las últimas). Creemos que tal aseveración beneficiaría enormemente a los fines de la asociación al implicar que para cada cifra adicional sólo se han de elevar al cuadrado, como mucho, nueve números.

    Atte.

    Darío Canfrán
    Secretario General de la AEMA

    ResponderEliminar
    Respuestas
    1. Muchas gracias por tu comentario, Darío (¿o debería decir Adrián? jeje)
      En primer lugar, demostraremos que, para cada una de esas series, existe como máximo 1 dígito que genera el siguiente número, por lo que tiene sentido hablar de dicha serie y puede ser construida (si no existe ningún dígito, se pone un 0 y trivialmente se cumple que es automórfico, como sucede con 09376, por ejemplo).
      -----------------------------------------------------------------------
      Sea x un número automórfico de m cifras. Supongamos que existen dos números a y b distintos y positivos tales que a¯x y b¯x son automórficos.
      Por lo tanto, se cumplen las siguientes equivalencias: x^2 ≡ x (mod 10^m), a*10^m + x ≡ a*10^m + x (mod 10^(m+1)) y b*10^m + x ≡ b*10^m + x (mod 10^(m+1)).
      Desarrollando las dos últimas y restándolas, llegamos a (2x-1)(a-b)*10^m ≡ 0 (mod 10^(m+1)).
      Por propiedades de aritmética modular, podemos "dividir" por 10^m cambiando el módulo y llegamos a (2x-1)(a-b) ≡ 0 (mod 10). Es decir, (2x-1)(a-b) es múltiplo de 10.
      Obviamente, 2x-1 no es múltiplo de 2. Por lo tanto, a-b tiene que ser múltiplo de 2.
      Ahora, o bien a-b es múltiplo de 5, o bien 2x-1 es múltiplo de 5.
      En el primer caso, tendríamos que a-b sería múltiplo de 10. Como son dígitos, llegaríamos a que a=b. Contradicción.
      En el segundo caso, tenemos 2x-1 ≡ 0 (mod 5), por lo que 2x ≡ 1 (mod 5). Multiplicando por el inverso de 2, x ≡ 3 (mod 5). Así, x^2 ≡ 3^2 (mod 5).
      Si retomamos la condición de automórfico y simplificamos la igualdad ya que 5 divide a 10^m, tenemos que x^2 ≡ x (mod 5). Utilizamos lo que acabamos de calcular y 9 ≡ 3 (mod 5). Es decir, 4 ≡ 3 (mod 5), lo cual es una clara contradicción.
      Por lo tanto, no existe dos números distintos a y b.
      -----------------------------------------------------------------------
      Por lo tanto, podrían existir 4 series distintas, generadas por 0, 1, 5 y 6, respectivamente.
      En seguida vemos que las del 0 y el 1 no son tales:
      Supongamos que existe un número a¯0¯...¯0 automórfico. Entonces a^2*10^(2m-2) ≡ 0 (mod 10^m), por lo que para que fuera igual a a*10^(m-1), el dígito a tendría que ser 0. Contradicción.
      Supongamos que existe un número a¯0¯...¯0¯1 automórfico. Entonces, a^2*10^(2m-2) + 2a*10^(m-1) + 1 ≡ 2a*10^(m-1) + 1 (mod 10^m). Esto último es equivalente al número original si y sólo si a*10^(m-1) ≡ 0 (mod 10^m) y tenemos la misma contradicción de antes.
      -----------------------------------------------------------------------
      Así, pues, las dos únicas series que existen son las generadas por el 5 y el 6.
      Además, existe una propiedad muy conocida de los números automórficos:
      Sean x e y los dos números automórficos de m cifras (*) con m>1. Entonces, x + y = 10^m + 1.
      (*)Nota: si sólo existe un número de m cifras, como es el caso cuando m=4, por ejemplo, se toma el anterior de la otra secuencia precedido por un 0 (así, tendríamos 9376 y 0625).
      Utilizaremos este hecho para probar que la suma de la primera cifra de los dos números de las secuencias es siempre 9.
      -----------------------------------------------------------------------
      Sean a¯x y b¯y los dos números automórficos de m cifras.
      Por la propiedad anterior, a*10^(m-1) + x + b*10^(m-1) + y = 10^m.
      Sabemos, como probamos en el tercer post, que x e y también son automórficos, por lo que x + y = 10^(m-1).
      Esto nos simplifica la igualdad: (a + b)*10^(m-1) + 10^(m-1) + 1 = 10^m + 1. Así, (a + b + 1)*10^(m-1) = 10^m.
      Por lo que a + b + 1 = 10. Es decir, a + b = 9.

      Eliminar
    2. -----------------------------------------------------------------------
      Por lo tanto, dado que, para cada longitud m, existen únicamente 2 números, uno para cada serie (siendo alguno de ellos, posiblemente, precedido por 0), con hallar uno ya nos basta.
      Si x e y son los números de m-1 cifras y hemos hallado a¯x, entonces (9-a)¯y es el otro número.
      De esta forma, efectivamente, el algoritmo se simplifica muchísimo. Sólo tenemos que ir siguiendo los números de una secuencia, comprobamos la definición si los precedemos por los dígitos del 1 al 9 (si no funciona para ninguno, ponemos un 0), y calculamos de la forma anterior el otro número. Así, sólo tenemos que hacer 9 comprobaciones como máximo (se acorta el algoritmo si lo paramos en cuanto encontremos un dígito que funcione).
      -----------------------------------------------------------------------
      Traducido a JavaScript, el algoritmo es:

      function automorficosHasta(maxCifras){
      var solution = [1, 5, 6], serie1 = BigInt(5), serie2 = BigInt(6), temp, found;
      for(cifras = 2; cifras <= maxCifras; cifras++){
      temp = serie1;
      found = false;
      for(i = 1; i <= 9; i++){
      temp += BigInt(10)**(BigInt(cifras)-BigInt(1));
      if(BigInt(temp)**BigInt(2)%BigInt(10)**BigInt(cifras) == temp){
      solution.push(Number(temp));
      serie1 = temp;
      if(BigInt(i) != BigInt(9)){
      serie2 += (BigInt(9)-BigInt(i))*BigInt(10)**(BigInt(cifras)-BigInt(1));
      solution.push(Number(serie2));
      }
      found = true;
      break;
      }
      }
      if(!found){
      serie2 += BigInt(9)*BigInt(10)**(BigInt(cifras)-BigInt(1));
      solution.push(Number(serie2));
      }
      }
      console.log(solution);
      }

      -----------------------------------------------------------------------
      Como nuestro objetivo era encontrar números de hasta 15 cifras, el algoritmo propuesto en el post no iba mal, pero para encontrar números de miles de cifras, éste es mejor.
      No obstante, y siguiendo tu juego, ¿es efectivamente el mejor, el de eficiencia máxima? Si no, tal vez no le interese a tu asociación... :D
      Además, dado que reduce tanto las comprobaciones, es algo más factible ahora utilizar este algoritmo para resolver el problema a mano. Un método para reducir las operaciones en tal caso y evitar calcular todo el cuadrado de los números aparece aquí: https://oeis.org/A003226/a003226.pdf.

      Eliminar
    3. Acabo de añadir este algoritmo al final del post, para que se pueda probar. ¡Muchas gracias, Adrián, por la sugerencia para mejorarlo!

      Eliminar