C ++, 275,000,000+
Nos referiremos a pares cuya magnitud es representable con precisión, como (x, 0) , como pares honestos y a todos los demás pares como pares deshonestos de magnitud m , donde m es la magnitud informada erróneamente del par. El primer programa en la publicación anterior usó un conjunto de parejas estrechamente relacionadas de pares honestos y deshonestos:
(x, 0) y (x, 1) , respectivamente, para x suficientemente grande. El segundo programa usó el mismo conjunto de pares deshonestos pero extendió el conjunto de pares honestos al buscar todos los pares honestos de magnitud integral. El programa no finaliza en diez minutos, pero encuentra la gran mayoría de sus resultados muy pronto, lo que significa que la mayor parte del tiempo de ejecución se desperdicia. En lugar de seguir buscando pares honestos cada vez menos frecuentes, este programa usa el tiempo libre para hacer la siguiente cosa lógica: extender el conjunto de pares deshonestos .
De la publicación anterior, sabemos que para todos los enteros suficientemente grandes r , sqrt (r 2 + 1) = r , donde sqrt es la función de raíz cuadrada de punto flotante. Nuestro plan de ataque es encontrar pares P = (x, y) de modo que x 2 + y 2 = r 2 + 1 para algún número entero suficientemente grande r . Eso es lo suficientemente simple como para hacerlo, pero la ingenua búsqueda de pares individuales es demasiado lenta para ser interesante. Queremos encontrar estos pares a granel, tal como lo hicimos para los pares honestos en el programa anterior.
Sea { v , w } un par de vectores ortonormales. Para todos los escalares reales r , || r v + w || 2 = r 2 + 1 . En ℝ 2 , este es un resultado directo del teorema de Pitágoras:
Estamos buscando los vectores v y w de modo que exista un número entero r para el cual x e y también sean números enteros. Como nota al margen, tenga en cuenta que el conjunto de pares deshonestos que utilizamos en los dos programas anteriores fue simplemente un caso especial de esto, donde { v , w } era la base estándar de ℝ 2 ; Esta vez deseamos encontrar una solución más general. Aquí es donde los trillizos pitagóricos (trillizos enteros (a, b, c) que satisfacen a 2 + b 2 = c 2, que utilizamos en el programa anterior) hacen su regreso.
Sea (a, b, c) un triplete pitagórico. Los vectores v = (b / c, a / c) y w = (-a / c, b / c) (y también
w = (a / c, -b / c) ) son ortonormales, como es fácil de verificar . Resulta que, para cualquier elección del triplete pitagórico, existe un número entero r tal que x e y son números enteros. Para probar esto, y para encontrar efectivamente r y P , necesitamos una pequeña teoría de números / grupos; Voy a ahorrar los detalles. De cualquier manera, supongamos que tenemos nuestra integral r , x e y . Todavía nos faltan algunas cosas: necesitamos rser lo suficientemente grande y queremos un método rápido para derivar muchos más pares similares de este. Afortunadamente, hay una manera simple de lograr esto.
Tenga en cuenta que la proyección de P en v es r v , por lo tanto, r = P · v = (x, y) · (b / c, a / c) = xb / c + ya / c , todo esto para decir que xb + ya = rc . Como resultado, para todos los enteros n , (x + bn) 2 + (y + an) 2 = (x 2 + y 2 ) + 2 (xb + ya) n + (a 2 + b 2 ) n 2 = ( r 2 + 1) + 2 (rc) n + (c 2 ) n 2 = (r + cn) 2 + 1. En otras palabras, la magnitud al cuadrado de los pares de la forma
(x + bn, y + an) es (r + cn) 2 + 1 , ¡que es exactamente el tipo de pares que estamos buscando! Para n suficientemente grande , estos son pares deshonestos de magnitud r + cn .
Siempre es bueno mirar un ejemplo concreto. Si tomamos el triplete pitagórico (3, 4, 5) , entonces en r = 2 tenemos P = (1, 2) (puede verificar que (1, 2) · (4/5, 3/5) = 2 y, claramente, 1 2 + 2 2 = 2 2 + 1. ) Sumar 5 a r y (4, 3) a P nos lleva a r '= 2 + 5 = 7 y P' = (1 + 4, 2 + 3) = (5, 5) . Lo y he aquí, 5 2 + 5 2 = 7 2 + 1. Las siguientes coordenadas son r '' = 12 y P '' = (9, 8) , y nuevamente, 9 2 + 8 2 = 12 2 + 1 , y así sucesivamente ...
Una vez que r es lo suficientemente grande, comenzamos a obtener pares deshonestos con incrementos de magnitud de 5 . Eso es aproximadamente 27,797,402 / 5 pares deshonestos.
Así que ahora tenemos muchos pares deshonestos de magnitud integral. Podemos unirlos fácilmente con los pares honestos del primer programa para formar falsos positivos, y con el debido cuidado también podemos usar los pares honestos del segundo programa. Esto es básicamente lo que hace este programa. Al igual que el programa anterior, también encuentra la mayoría de sus resultados muy pronto, llega a 200,000,000 de falsos positivos en unos pocos segundos, y luego se ralentiza considerablemente.
Compilar con g++ flspos.cpp -oflspos -std=c++11 -msse2 -mfpmath=sse -O3
. Para verificar los resultados, agregue -DVERIFY
(esto será notablemente más lento).
Corre con flspos
. Cualquier argumento de línea de comandos para el modo detallado.
#include <cstdio>
#define _USE_MATH_DEFINES
#undef __STRICT_ANSI__
#include <cmath>
#include <cfloat>
#include <vector>
#include <iterator>
#include <algorithm>
using namespace std;
/* Make sure we actually work with 64-bit precision */
#if defined(VERIFY) && FLT_EVAL_METHOD != 0 && FLT_EVAL_METHOD != 1
# error "invalid FLT_EVAL_METHOD (did you forget `-msse2 -mfpmath=sse'?)"
#endif
template <typename T> struct widen;
template <> struct widen<int> { typedef long long type; };
template <typename T>
inline typename widen<T>::type mul(T x, T y) {
return typename widen<T>::type(x) * typename widen<T>::type(y);
}
template <typename T> inline T div_ceil(T a, T b) { return (a + b - 1) / b; }
template <typename T> inline typename widen<T>::type sq(T x) { return mul(x, x); }
template <typename T>
T gcd(T a, T b) { while (b) { T t = a; a = b; b = t % b; } return a; }
template <typename T>
inline typename widen<T>::type lcm(T a, T b) { return mul(a, b) / gcd(a, b); }
template <typename T>
T div_mod_n(T a, T b, T n) {
if (b == 0) return a == 0 ? 0 : -1;
const T n_over_b = n / b, n_mod_b = n % b;
for (T m = 0; m < n; m += n_over_b + 1) {
if (a % b == 0) return m + a / b;
a -= b - n_mod_b;
if (a < 0) a += n;
}
return -1;
}
template <typename T> struct pythagorean_triplet { T a, b, c; };
template <typename T>
struct pythagorean_triplet_generator {
typedef pythagorean_triplet<T> result_type;
private:
typedef typename widen<T>::type WT;
result_type p_triplet;
WT p_c2b2;
public:
pythagorean_triplet_generator(const result_type& triplet = {3, 4, 5}) :
p_triplet(triplet), p_c2b2(sq(triplet.c) - sq(triplet.b))
{}
const result_type& operator*() const { return p_triplet; }
const result_type* operator->() const { return &p_triplet; }
pythagorean_triplet_generator& operator++() {
do {
if (++p_triplet.b == p_triplet.c) {
++p_triplet.c;
p_triplet.b = ceil(p_triplet.c * M_SQRT1_2);
p_c2b2 = sq(p_triplet.c) - sq(p_triplet.b);
} else
p_c2b2 -= 2 * p_triplet.b - 1;
p_triplet.a = sqrt(p_c2b2);
} while (sq(p_triplet.a) != p_c2b2 || gcd(p_triplet.b, p_triplet.a) != 1);
return *this;
}
result_type operator()() { result_type t = **this; ++*this; return t; }
};
int main(int argc, const char* argv[]) {
const bool verbose = argc > 1;
const int min = 1 << 26;
const int max = sqrt(1ll << 53);
const size_t small_triplet_count = 1000;
vector<pythagorean_triplet<int>> small_triplets;
small_triplets.reserve(small_triplet_count);
generate_n(
back_inserter(small_triplets),
small_triplet_count,
pythagorean_triplet_generator<int>()
);
int found = 0;
auto add = [&] (int x1, int y1, int x2, int y2) {
#ifdef VERIFY
auto n1 = sq(x1) + sq(y1), n2 = sq(x2) + sq(y2);
if (x1 < y1 || x2 < y2 || x1 > max || x2 > max ||
n1 == n2 || sqrt(n1) != sqrt(n2)
) {
fprintf(stderr, "Wrong false-positive: (%d, %d) (%d, %d)\n",
x1, y1, x2, y2);
return;
}
#endif
if (verbose) printf("(%d, %d) (%d, %d)\n", x1, y1, x2, y2);
++found;
};
int output_counter = 0;
for (int x = min; x <= max; ++x) add(x, 0, x, 1);
for (pythagorean_triplet_generator<int> i; i->c <= max; ++i) {
const auto& t1 = *i;
for (int n = div_ceil(min, t1.c); n <= max / t1.c; ++n)
add(n * t1.b, n * t1.a, n * t1.c, 1);
auto find_false_positives = [&] (int r, int x, int y) {
{
int n = div_ceil(min - r, t1.c);
int min_r = r + n * t1.c;
int max_n = n + (max - min_r) / t1.c;
for (; n <= max_n; ++n)
add(r + n * t1.c, 0, x + n * t1.b, y + n * t1.a);
}
for (const auto t2 : small_triplets) {
int m = div_mod_n((t2.c - r % t2.c) % t2.c, t1.c % t2.c, t2.c);
if (m < 0) continue;
int sr = r + m * t1.c;
int c = lcm(t1.c, t2.c);
int min_n = div_ceil(min - sr, c);
int min_r = sr + min_n * c;
if (min_r > max) continue;
int x1 = x + m * t1.b, y1 = y + m * t1.a;
int x2 = t2.b * (sr / t2.c), y2 = t2.a * (sr / t2.c);
int a1 = t1.a * (c / t1.c), b1 = t1.b * (c / t1.c);
int a2 = t2.a * (c / t2.c), b2 = t2.b * (c / t2.c);
int max_n = min_n + (max - min_r) / c;
int max_r = sr + max_n * c;
for (int n = min_n; n <= max_n; ++n) {
add(
x2 + n * b2, y2 + n * a2,
x1 + n * b1, y1 + n * a1
);
}
}
};
{
int m = div_mod_n((t1.a - t1.c % t1.a) % t1.a, t1.b % t1.a, t1.a);
find_false_positives(
/* r = */ (mul(m, t1.c) + t1.b) / t1.a,
/* x = */ (mul(m, t1.b) + t1.c) / t1.a,
/* y = */ m
);
} {
int m = div_mod_n((t1.b - t1.c % t1.b) % t1.b, t1.a, t1.b);
find_false_positives(
/* r = */ (mul(m, t1.c) + t1.a) / t1.b,
/* x = */ m,
/* y = */ (mul(m, t1.a) + t1.c) / t1.b
);
}
if (output_counter++ % 50 == 0)
printf("%d\n", found), fflush(stdout);
}
printf("%d\n", found);
}