Verificación numérica de la hipótesis abc (sí, esa)

Hola Habr

Ya se han publicado varios artículos sobre la hipótesis abc en el Geektimes Habr (por ejemplo, en 2013 y 2018 ). La historia en sí misma sobre un teorema que al principio no se puede probar durante muchos años, y luego no se puede verificar durante el mismo número de años, ciertamente merece al menos una película. Pero a la sombra de esta maravillosa historia, el teorema en sí mismo se considera demasiado superficialmente, aunque no es menos interesante. Ya al ​​menos por el hecho de que la hipótesis ABC es uno de los pocos problemas no resueltos de la ciencia moderna, la declaración del problema que incluso un alumno de quinto grado puede entender. Si esta hipótesis es realmente cierta, entonces fácilmente sigue la prueba de otros teoremas importantes, por ejemplo, la prueba del teorema de Fermat .

Sin reclamar los laureles de Motizuki, también decidí tratar de comprobar con una computadora cuánto se cumple la igualdad prometida en la hipótesis. En realidad, ¿por qué no? Los procesadores modernos no son solo para jugar, sino por qué no usar una computadora para su propósito principal (computación) ...

A quién le importa lo que pasó, por favor, debajo del gato.

Declaración del problema.


Comencemos desde el principio. ¿De qué trata el teorema? Como dice Wikipedia (la redacción en la versión en inglés es un poco más clara), para números primos mutuamente (que no tienen divisores comunes) a, byc de modo que a + b = c, para cualquier ε> 0 hay un número limitado de triples a + b = c, de modo que:



La función rad se llama radical y denota el producto de factores primos de un número. Por ejemplo, rad (16) = rad (2 * 2 * 2 * 2) = 2, rad (17) = 17 (17 es un número primo), rad (18) = rad (2 * 3 * 3) = 2 * 3 = 6, rad (1,000,000) = rad (2 ^ 6 ⋅ 5 ^ 6) = 2 * 5 = 10.

En realidad, la esencia del teorema es que el número de triples es bastante pequeño. Por ejemplo, si tomamos al azar ε = 0.2 y la igualdad 100 + 27 = 127: rad (100) = rad (2 * 2 * 5 * 5) = 10, rad (27) = rad (3 * 3 * 3) = 3, rad (127) = 127, rad (a * b * c) = rad (a) * rad (b) * rad (c) = 3810, 3810 ^ 1.2 es claramente mayor que 127, la desigualdad no se cumple. Pero hay excepciones, por ejemplo, para la igualdad 49 + 576 = 625, se cumple la condición del teorema (aquellos que lo deseen pueden verificar por sí mismos).

El siguiente momento clave para nosotros es un número limitado de estas igualdades, según el teorema. Es decir Esto significa que simplemente puede intentar ordenarlos en una computadora. Como resultado, esto nos da al Premio Nobel una tarea de programación bastante interesante.

Entonces comencemos.

Código fuente


La primera versión fue escrita en Python, y aunque este lenguaje es demasiado lento para tales cálculos, escribir código en él es fácil y simple, lo cual es conveniente para la creación de prototipos.

Obteniendo el radical : descomponemos el número en factores primos, luego eliminamos las repeticiones, convirtiendo la matriz en un conjunto. Luego solo obtenga el producto de todos los elementos.

def prime_factors(n): factors = [] # Print the number of two's that divide n while n % 2 == 0: factors.append(int(2)) n = n / 2 # n must be odd at this point so a skip of 2 ( i = i + 2) can be used for i in range(3, int(math.sqrt(n)) + 1, 2): # while i divides n , print i ad divide n while n % i == 0: factors.append(int(i)) n = n / i # Condition if n is a prime number greater than 2 if n > 2: factors.append(int(n)) return set(factors) def rad(n): result = 1 for num in prime_factors(n): result *= num return result 

Números primos mutuos : factoriza los números y simplemente verifica la intersección de los conjuntos.

 def not_mutual_primes(a,b,c): fa, fb, fc = prime_factors(a), prime_factors(b), prime_factors(c) return len(fa.intersection(fb)) == 0 and len(fa.intersection(fc)) == 0 and len(fb.intersection(fc)) == 0 

Verificación : utilizamos funciones ya creadas, todo es simple aquí.

 def check(a,b,c): S = 1.2 # Eps=0.2 if c > (rad(a)*rad(b)*rad(c))**S and not_mutual_primes(a, b, c): print("{} + {} = {} - PASSED".format(a, b, c)) else: print("{} + {} = {} - FAILED".format(a, b, c)) check(10, 17, 27) check(49, 576, 625) 

Aquellos que lo deseen pueden experimentar de forma independiente copiando el código anterior en cualquier editor de lenguaje Python en línea. Por supuesto, el código se ejecuta como se esperaba y enumerar todos los triples a al menos un millón sería demasiado largo. Debajo del spoiler hay una versión optimizada, se recomienda usarlo.

La versión final se reescribió en C ++ utilizando subprocesos múltiples y cierta optimización (trabajar en C con conjuntos de intersección sería demasiado duro, aunque probablemente más rápido). El código fuente está debajo del spoiler, se puede compilar en el compilador g ++ gratuito, el código funciona en Windows, OSX e incluso en Raspberry Pi.

Código C ++
 // To compile: g++ abc.cpp -O3 -fopenmp -oabc #include <string.h> #include <math.h> #include <stdbool.h> #include <stdint.h> #include <stdio.h> #include <vector> #include <set> #include <map> #include <algorithm> #include <time.h> typedef unsigned long int valType; typedef std::vector<valType> valList; typedef std::set<valType> valSet; typedef valList::iterator valListIterator; std::vector<valList> valFactors; std::vector<double> valRads; valList factors(valType n) { valList results; valType z = 2; while (z * z <= n) { if (n % z == 0) { results.push_back(z); n /= z; } else { z++; } } if (n > 1) { results.push_back(n); } return results; } valList unique_factors(valType n) { valList results = factors(n); valSet vs(results.begin(), results.end()); valList unique(vs.begin(), vs.end()); std::sort(unique.begin(), unique.end()); return unique; } double rad(valType n) { valList f = valFactors[n]; double result = 1; for (valListIterator it=f.begin(); it<f.end(); it++) { result *= *it; } return result; } bool not_mutual_primes(valType a, valType b, valType c) { valList res1 = valFactors[a], res2 = valFactors[b], res3; // = valFactors[c]; valList c12, c13, c23; set_intersection(res1.begin(),res1.end(), res2.begin(),res2.end(), back_inserter(c12)); if (c12.size() != 0) return false; res3 = valFactors[c]; set_intersection(res1.begin(),res1.end(), res3.begin(),res3.end(), back_inserter(c13)); if (c13.size() != 0) return false; set_intersection(res2.begin(),res2.end(), res3.begin(),res3.end(), back_inserter(c23)); return c23.size() == 0; } int main() { time_t start_t, end_t; time(&start_t); int cnt=0; double S = 1.2; valType N_MAX = 10000000; printf("Getting prime factors...\n"); valFactors.resize(2*N_MAX+2); valRads.resize(2*N_MAX+2); for(valType val=1; val<=2*N_MAX+1; val++) { valFactors[val] = unique_factors(val); valRads[val] = rad(val); } time(&end_t); printf("Done, T = %.2fs\n", difftime(end_t, start_t)); printf("Calculating...\n"); #pragma omp parallel for reduction(+:cnt) for(int a=1; a<=N_MAX; a++) { for(int b=a; b<=N_MAX; b++) { int c = a+b; if (c > pow(valRads[a]*valRads[b]*valRads[c], S) && not_mutual_primes(a,b,c)) { printf("%d + %d = %d\n", a,b,c); cnt += 1; } } } printf("Done, cnt=%d\n", cnt); time(&end_t); float diff_t = difftime(end_t, start_t); printf("N=%lld, T = %.2fs\n", N_MAX, diff_t); } 


Para aquellos que son demasiado flojos para instalar el compilador de C ++, se proporciona una versión de Python ligeramente optimizada, que se puede ejecutar en cualquier editor en línea (utilicé https://repl.it/languages/python ).

Versión de Python
 from __future__ import print_function import math import time import multiprocessing prime_factors_list = [] rad_list = [] def prime_factors(n): factors = [] # Print the number of two's that divide n while n % 2 == 0: factors.append(int(2)) n = n / 2 # n must be odd at this point so a skip of 2 ( i = i + 2) can be used for i in range(3, int(math.sqrt(n)) + 1, 2): # while i divides n , print i ad divide n while n % i == 0: factors.append(int(i)) n = n / i # Condition if n is a prime number greater than 2 if n > 2: factors.append(int(n)) return factors def rad(n): result = 1 for num in prime_factors_list[n]: result *= num return result def not_mutual_primes(a,b,c): fa, fb, fc = prime_factors_list[a], prime_factors_list[b], prime_factors_list[c] return len(fa.intersection(fb)) == 0 and len(fa.intersection(fc)) == 0 and len(fb.intersection(fc)) == 0 def calculate(N): S = 1.2 cnt = 0 for a in range(1, N): for b in range(a, N): c = a+b if c > (rad_list[a]*rad_list[b]*rad_list[c])**S and not_mutual_primes(a, b, c): print("{} + {} = {}".format(a, b, c)) cnt += 1 print("N: {}, CNT: {}".format(N, cnt)) return cnt if __name__ == '__main__': t1 = time.time() NMAX = 100000 prime_factors_list = [0]*(2*NMAX+2) rad_list = [0]*(2*NMAX+2) for p in range(1, 2*NMAX+2): prime_factors_list[p] = set(prime_factors(p)) rad_list[p] = rad(p) calculate(NMAX) print("Done", time.time() - t1) 


Resultados


Los triples a, b, c son realmente muy pocos.

Algunos resultados se dan a continuación:
N = 10 : 1 "tres", tiempo de entrega <0.001c
1 + 8 = 9

N = 100 : 2 "triples", tiempo de ejecución <0.001c
1 + 8 = 9
1 + 80 = 81

N = 1000 : 8 "triples", tiempo de ejecución <0.01c
1 + 8 = 9
1 + 80 = 81
1 + 242 = 243
1 + 288 = 289
1 + 512 = 513
3 + 125 = 128
13 + 243 = 256
49 + 576 = 625

N = 10000 : 23 "triples", tiempo de ejecución 2s

Tres A, B, C hasta 10000
1 + 8 = 9
1 + 80 = 81
1 + 242 = 243
1 + 288 = 289
1 + 512 = 513
1 + 2400 = 2401
1 + 4374 = 4375
1 + 5831 = 5832
1 + 6560 = 6561
1 + 6655 = 6656
1 + 6859 = 6860
3 + 125 = 128
5 + 1024 = 1029
10 + 2187 = 2197
11 + 3125 = 3136
13 + 243 = 256
49 + 576 = 625
1331 + 9604 = 10935
81 + 1250 = 1331
125 + 2187 = 2312
243 + 1805 = 2048
289 + 6272 = 6561
625 + 2048 = 2673

N = 100000 : 53 triples, tiempo de ejecución 50c

Tres A, B, C hasta 100,000
1 + 8 = 9
1 + 80 = 81
1 + 242 = 243
1 + 288 = 289
1 + 512 = 513
1 + 2400 = 2401
1 + 4374 = 4375
1 + 5831 = 5832
1 + 6560 = 6561
1 + 6655 = 6656
1 + 6859 = 6860
1 + 12167 = 12168
1 + 14336 = 14337
1 + 57121 = 57122
1 + 59048 = 59049
1 + 71874 = 71875
3 + 125 = 128
3 + 65533 = 65536
5 + 1024 = 1029
7 + 32761 = 32768
9 + 15616 = 15625
9 + 64000 = 64009
10 + 2187 = 2197
11 + 3125 = 3136
13 + 243 = 256
28 + 50625 = 50653
31 + 19652 = 19683
37 + 32768 = 32805
49 + 576 = 625
49 + 16335 = 16384
73 + 15552 = 15625
81 + 1250 = 1331
121 + 12167 = 12288
125 + 2187 = 2312
125 + 50176 = 50301
128 + 59049 = 59177
169 + 58880 = 59049
243 + 1805 = 2048
243 + 21632 = 21875
289 + 6272 = 6561
343 + 59049 = 59392
423 + 16384 = 16807
507 + 32768 = 33275
625 + 2048 = 2673
1331 + 9604 = 10935
1625 + 16807 = 18432
28561 + 89088 = 117649
28561 + 98415 = 126976
3584 + 14641 = 18225
6561 + 22000 = 28561
7168 + 78125 = 85293
8192 + 75843 = 84035
36864 + 41261 = 78125

Con N = 1,000,000, tenemos solo 102 "triples", una lista completa se da debajo del spoiler.

Tres, A, B, C hasta 1,000,000
1 + 8 = 9
1 + 80 = 81
1 + 242 = 243
1 + 288 = 289
1 + 512 = 513
1 + 2400 = 2401
1 + 4374 = 4375
1 + 5831 = 5832
1 + 6560 = 6561
1 + 6655 = 6656
1 + 6859 = 6860
1 + 12167 = 12168
1 + 14336 = 14337
1 + 57121 = 57122
1 + 59048 = 59049
1 + 71874 = 71875
1 + 137780 = 137781
1 + 156249 = 156250
1 + 229375 = 229376
1 + 263168 = 263169
1 + 499999 = 500000
1 + 512000 = 512001
1 + 688127 = 688128
3 + 125 = 128
3 + 65533 = 65536
5 + 1024 = 1029
5 + 177147 = 177152
7 + 32761 = 32768
9 + 15616 = 15625
9 + 64000 = 64009
10 + 2187 = 2197
11 + 3125 = 3136
13 + 243 = 256
13 + 421875 = 421888
17 + 140608 = 140625
25 + 294912 = 294937
28 + 50625 = 50653
31 + 19652 = 19683
37 + 32768 = 32805
43 + 492032 = 492075
47 + 250000 = 250047
49 + 576 = 625
49 + 16335 = 16384
49 + 531392 = 531441
64 + 190269 = 190333
73 + 15552 = 15625
81 + 1250 = 1331
81 + 123823 = 123904
81 + 134375 = 134456
95 + 279841 = 279936
121 + 12167 = 12288
121 + 255879 = 256000
125 + 2187 = 2312
125 + 50176 = 50301
128 + 59049 = 59177
128 + 109375 = 109503
128 + 483025 = 483153
169 + 58880 = 59049
243 + 1805 = 2048
243 + 21632 = 21875
289 + 6272 = 6561
338 + 390625 = 390963
343 + 59049 = 59392
423 + 16384 = 16807
507 + 32768 = 33275
625 + 2048 = 2673
864 + 923521 = 924385
1025 + 262144 = 263169
1331 + 9604 = 10935
1375 + 279841 = 281216
1625 + 16807 = 18432
2197 + 583443 = 585640
2197 + 700928 = 703125
3481 + 262144 = 265625
3584 + 14641 = 18225
5103 + 130321 = 135424
6125 + 334611 = 340736
6561 + 22000 = 28561
7153 + 524288 = 531441
7168 + 78125 = 85293
8192 + 75843 = 84035
8192 + 634933 = 643125
9583 + 524288 = 533871
10816 + 520625 = 531441
12005 + 161051 = 173056
12672 + 117649 = 130321
15625 + 701784 = 717409
18225 + 112847 = 131072
19683 + 228125 = 247808
24389 + 393216 = 417605
28561 + 89088 = 117649
28561 + 98415 = 126976
28561 + 702464 = 731025
32768 + 859375 = 892143
296875 + 371293 = 668168
36864 + 41261 = 78125
38307 + 371293 = 409600
303264 + 390625 = 693889
62192 + 823543 = 885735
71875 + 190269 = 262144
131072 + 221875 = 352947
132651 + 588245 = 720896


Por desgracia, el programa aún funciona lentamente, no esperé los resultados para N = 10,000,000, el tiempo de cálculo es más de una hora (tal vez cometí un error al optimizar el algoritmo en algún lugar, y puedo hacerlo mejor).

Es aún más interesante ver los resultados gráficamente:



En principio, es bastante obvio que la dependencia del número de triples posibles en N crece notablemente más lento que N en sí mismo, y es probable que el resultado converja en algún número específico para cada ε. Por cierto, con un aumento en ε, el número de triples disminuye notablemente, por ejemplo, para ε = 0.4 tenemos solo 2 igualdades para N <100000 (1 + 4374 = 4375 y 343 + 59049 = 59392). Entonces, en general, parece que el teorema realmente se cumple (bueno, y probablemente ya se haya probado en computadoras más potentes, y tal vez todo esto se haya calculado durante mucho tiempo).

Aquellos que lo deseen pueden experimentar por su cuenta, si alguien tiene resultados para números 10,000,000 y superiores, estaré encantado de agregarlos al artículo. Por supuesto, sería interesante "contar" hasta el momento en que el conjunto de "triples" deje de crecer por completo, pero puede llevar mucho tiempo, la velocidad de cálculo parece depender de N como N * N (o tal vez N ^ 3), y el proceso muy largo Sin embargo, hay algo sorprendente cerca, y aquellos que lo deseen pueden unirse a la búsqueda.

Editar: como se sugiere en los comentarios, Wikipedia ya tiene una tabla con los resultados : en el rango N hasta 10 ^ 18 el número de "triples" sigue creciendo, por lo que aún no se ha encontrado el "final" del conjunto. Aún más interesante: la intriga aún permanece.

Source: https://habr.com/ru/post/es427091/


All Articles