C ++, 275 000 000+
Nous ferons référence à des paires dont la magnitude est représentable avec précision, telles que (x, 0) , en tant que paires honnêtes et à toutes les autres paires comme des paires malhonnêtes de magnitude m , où m est la magnitude mal déclarée de la paire. Le premier programme dans le post précédent a utilisé un ensemble de couples étroitement liés de paires honnêtes et malhonnêtes:
(x, 0) et (x, 1) , respectivement, pour assez grand x. Le deuxième programme a utilisé le même ensemble de paires malhonnêtes mais a étendu l'ensemble de paires honnêtes en recherchant toutes les paires honnêtes de magnitude intégrale. Le programme ne s'arrête pas dans les dix minutes, mais il trouve la grande majorité de ses résultats très tôt, ce qui signifie que la plupart du temps d'exécution est gaspillé. Au lieu de continuer à chercher des paires honnêtes de moins en moins fréquentes, ce programme utilise le temps libre pour faire la prochaine chose logique: étendre l'ensemble des paires malhonnêtes .
De l'article précédent, nous savons que pour tous les entiers suffisamment grands r , sqrt (r 2 + 1) = r , où sqrt est la fonction racine carrée à virgule flottante. Notre plan d'attaque est de trouver des paires P = (x, y) telles que x 2 + y 2 = r 2 + 1 pour un entier r assez grand . C'est assez simple à faire, mais chercher naïvement de telles paires individuelles est trop lent pour être intéressant. Nous voulons trouver ces paires en vrac, tout comme nous l'avons fait pour les paires honnêtes dans le programme précédent.
Soit { v , w } une paire orthonormée de vecteurs. Pour tous les scalaires réels r , || r v + w || 2 = r 2 + 1 . Dans ℝ 2 , c'est un résultat direct du théorème de Pythagore:
Nous recherchons des vecteurs v et w tels qu'il existe un entier r pour lequel x et y sont également des entiers. En remarque, notons que l'ensemble des paires malhonnêtes que nous avons utilisées dans les deux programmes précédents n'était qu'un cas particulier de ceci, où { v , w } était la base standard de ℝ 2 ; cette fois, nous souhaitons trouver une solution plus générale. C'est là que les triplets de Pythagore (triplets entiers (a, b, c) satisfaisant a 2 + b 2 = c 2, que nous utilisions dans le programme précédent) font leur retour.
Soit (a, b, c) un triplet pythagoricien. Les vecteurs v = (b / c, a / c) et w = (-a / c, b / c) (et aussi
w = (a / c, -b / c) ) sont orthonormés, comme cela est facile à vérifier . En fait, pour tout choix de triplet de Pythagore, il existe un entier r tel que x et y sont des entiers. Pour le prouver et pour trouver efficacement r et P , nous avons besoin d'une petite théorie des nombres / groupes; Je vais épargner les détails. Quoi qu'il en soit, supposons que nous ayons nos r , x et y intégraux . Nous sommes encore à court de quelques choses: nous avons besoin de rêtre assez grand et nous voulons une méthode rapide pour dériver beaucoup plus de paires similaires de celle-ci. Heureusement, il existe un moyen simple d'y parvenir.
Notez que la projection de P sur v est r v , donc r = P · v = (x, y) · (b / c, a / c) = xb / c + ya / c , tout cela pour dire que xb + ya = rc . Par conséquent, pour tous les entiers 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 d'autres termes, la magnitude au carré des paires de la forme
(x + bn, y + an) est (r + cn) 2 + 1 , ce qui est exactement le type de paires que nous recherchons! Pour n assez grand , ce sont des paires de magnitude malhonnêtes r + cn .
C'est toujours agréable de regarder un exemple concret. Si nous prenons le triplet de Pythagore (3, 4, 5) , alors à r = 2 nous avons P = (1, 2) (vous pouvez vérifier que (1, 2) · (4/5, 3/5) = 2 et, clairement, 1 2 + 2 2 = 2 2 + 1. ) Ajouter 5 à r et (4, 3) à P nous amène à r '= 2 + 5 = 7 et P' = (1 + 4, 2 + 3) = (5, 5) . Et voilà, 5 2 + 5 2 = 7 2 + 1. Les coordonnées suivantes sont r '' = 12 et P '' = (9, 8) , et encore, 9 2 + 8 2 = 12 2 + 1 , et ainsi de suite, et ainsi de suite ...
Une fois que r est assez grand, nous commençons à obtenir des paires malhonnêtes avec des incréments de magnitude de 5 . Cela représente environ 27 797 402/5 paires malhonnêtes.
Alors maintenant, nous avons beaucoup de paires malhonnêtes de magnitude intégrale. Nous pouvons facilement les coupler avec les paires honnêtes du premier programme pour former des faux positifs, et avec prudence, nous pouvons également utiliser les paires honnêtes du deuxième programme. C'est essentiellement ce que fait ce programme. Comme le programme précédent, il trouve lui aussi la plupart de ses résultats très tôt --- il atteint 200 000 000 de faux positifs en quelques secondes --- puis ralentit considérablement.
Compilez avec g++ flspos.cpp -oflspos -std=c++11 -msse2 -mfpmath=sse -O3
. Pour vérifier les résultats, ajoutez -DVERIFY
(ce sera notamment plus lent.)
Courez avec flspos
. Tout argument de ligne de commande pour le mode détaillé.
#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);
}