C ++, 275.000.000+
Faremo riferimento a coppie la cui magnitudine è rappresentabile con precisione, come (x, 0) , come coppie oneste e a tutte le altre coppie come coppie disoneste di magnitudo m , dove m è la magnitudine erroneamente segnalata della coppia. Il primo programma nel post precedente utilizzava un insieme di coppie strettamente legate di coppie oneste e disoneste:
(x, 0) e (x, 1) , rispettivamente, per x abbastanza grande. Il secondo programma utilizzava lo stesso insieme di coppie disoneste, ma estendeva l'insieme di coppie oneste cercando tutte le coppie oneste di grandezza integrale. Il programma non termina entro dieci minuti, ma trova la maggior parte dei suoi risultati molto presto, il che significa che la maggior parte del tempo di esecuzione va sprecato. Invece di continuare a cercare coppie oneste sempre meno frequenti, questo programma usa il tempo libero per fare la prossima cosa logica: estendere l'insieme di coppie disoneste .
Dal post precedente sappiamo che per tutti gli interi abbastanza grandi r , sqrt (r 2 + 1) = r , dove sqrt è la funzione radice quadrata a virgola mobile. Il nostro piano di attacco è trovare coppie P = (x, y) tali che x 2 + y 2 = r 2 + 1 per un numero intero abbastanza grande r . È abbastanza semplice da fare, ma cercare ingenuamente coppie individuali di questo tipo è troppo lento per essere interessante. Vogliamo trovare queste coppie alla rinfusa, proprio come abbiamo fatto per le coppie oneste nel programma precedente.
Sia { v , w } una coppia di vettori ortogonali. Per tutti gli scalari reali r , || r v + w || 2 = r 2 + 1 . In ℝ 2 , questo è un risultato diretto del teorema di Pitagora:
Siamo alla ricerca di vettori V e w tale che esiste un intero r per il quale x ed y sono anche interi. Come nota a margine, nota che l'insieme di coppie disoneste che abbiamo usato nei due programmi precedenti era semplicemente un caso speciale di questo, in cui { v , w } era la base standard di ℝ 2 ; questa volta desideriamo trovare una soluzione più generale. Questo è dove terzine pitagoriche (terzine di numeri interi (a, b, c) che soddisfano a 2 + b 2 = c 2, che abbiamo usato nel programma precedente) ritornano.
Sia (a, b, c) una tripletta pitagorica. I vettori v = (b / c, a / c) e w = (-a / c, b / c) (e anche
w = (a / c, -b / c) ) sono ortonormali, come è facile da verificare . Come si è visto, per ogni scelta di Pitagora tripletto, esiste un intero r tale che x ed y sono numeri interi. Per dimostrarlo e per trovare efficacemente r e P , abbiamo bisogno di una piccola teoria dei numeri / gruppi; Ho intenzione di risparmiare i dettagli. In entrambi i casi, supponiamo di avere le nostre integrante r , x e y . Mancano ancora alcune cose: abbiamo bisogno di ressere abbastanza grandi e vogliamo un metodo veloce per derivare molte più coppie simili da questo. Fortunatamente, c'è un modo semplice per ottenere questo risultato.
Nota che la proiezione di P su v è r v , quindi r = P · v = (x, y) · (b / c, a / c) = xb / c + ya / c , tutto questo per dire che xb + ya = rc . Di conseguenza, per tutti i numeri interi 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. In altre parole, la grandezza quadrata delle coppie della forma
(x + bn, y + an) è (r + cn) 2 + 1 , che è esattamente il tipo di coppie che stiamo cercando! Per n abbastanza grandi , si tratta di coppie disoneste di magnitudine r + cn .
È sempre bello guardare un esempio concreto. Se prendiamo la tripletta di Pitagora (3, 4, 5) , allora a r = 2 abbiamo P = (1, 2) (puoi controllare che (1, 2) · (4/5, 3/5) = 2 e, chiaramente, 1 2 + 2 2 = 2 2 + 1 ). Aggiungere 5 a r e (4, 3) a P ci porta a r '= 2 + 5 = 7 e P' = (1 + 4, 2 + 3) = (5, 5) . Ecco ed ecco, 5 2 + 5 2 = 7 2 + 1. Le coordinate successive sono r '' = 12 e P '' = (9, 8) , e ancora, 9 2 + 8 2 = 12 2 + 1 , e così via, e così via ...
Una volta che r è abbastanza grande, iniziamo a ottenere coppie disoneste con incrementi di magnitudo di 5 . Sono circa 27.797.402 / 5 coppie disoneste.
Quindi ora abbiamo molte coppie disoneste di magnitudo integrale. Possiamo facilmente accoppiarli con le coppie oneste del primo programma per formare falsi positivi e con la dovuta cura possiamo anche usare le coppie oneste del secondo programma. Questo è fondamentalmente ciò che fa questo programma. Come il programma precedente, trova anche la maggior parte dei suoi risultati molto presto --- arriva a 200.000.000 di falsi positivi in pochi secondi --- e quindi rallenta considerevolmente.
Compila con g++ flspos.cpp -oflspos -std=c++11 -msse2 -mfpmath=sse -O3
. Per verificare i risultati, aggiungi -DVERIFY
(questo sarà notevolmente più lento.)
Corri con flspos
. Qualsiasi argomento della riga di comando per la modalità dettagliata.
#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);
}