C ++, 275.000.000+
Wir bezeichnen Paare, deren Größe genau darstellbar ist, wie z. B. (x, 0) , als ehrliche Paare und alle anderen Paare als unehrliche Paare der Größe m , wobei m die falsch angegebene Größe des Paares ist. Das erste Programm im vorherigen Beitrag verwendete eine Reihe eng verwandter Paare von ehrlichen und unehrlichen Paaren:
(x, 0) bzw. (x, 1) für x , das groß genug ist. Das zweite Programm verwendete dieselbe Menge unehrlicher Paare, erweiterte jedoch die Menge ehrlicher Paare, indem es nach allen ehrlichen Paaren mit ganzzahliger Größe suchte. Das Programm wird nicht innerhalb von zehn Minuten beendet, findet jedoch den größten Teil seiner Ergebnisse sehr früh. Dies bedeutet, dass ein Großteil der Laufzeit verschwendet wird. Anstatt immer seltener nach ehrlichen Paaren zu suchen, nutzt dieses Programm die freie Zeit, um die nächste logische Sache zu tun: die Menge der unehrlichen Paare zu erweitern.
Von der früheren Post wissen wir , dass für alle groß genug , um ganze Zahlen r , sqrt (r 2 + 1) = r , wobei sqrt die Fließkommaquadratwurzelfunktion ist. Unser Angriffsplan besteht darin, Paare P = (x, y) zu finden, so dass x 2 + y 2 = r 2 + 1 für eine ausreichend große ganze Zahl r gilt . Das ist einfach genug, aber naiv nach solchen Paaren zu suchen, ist zu langsam, um interessant zu sein. Wir möchten diese Paare in großen Mengen finden, so wie wir es im vorherigen Programm für ehrliche Paare getan haben.
Sei { v , w } ein orthonormales Vektorpaar. Für alle reellen Skalare gilt r , || r v + w || 2 = r 2 + 1 . In ℝ 2 ist dies ein direktes Ergebnis des Satzes von Pythagoras:
Wir suchen nach Vektoren v und w, so dass es eine ganze Zahl r gibt, für die x und y auch ganze Zahlen sind. Als Randnotiz sei angemerkt, dass die Menge der unehrlichen Paare, die wir in den beiden vorhergehenden Programmen verwendet haben, lediglich ein Sonderfall davon war, wobei { v , w } die Standardbasis von ℝ 2 war ; Dieses Mal möchten wir eine allgemeinere Lösung finden. Hier sind pythagoreische Tripletts (ganzzahlige Tripletts (a, b, c), die a 2 + b 2 = c 2 erfüllen, die wir im vorherigen Programm verwendet haben) machen ihr Comeback.
Sei (a, b, c) ein pythagoreisches Triplett. Die Vektoren v = (b / c, a / c) und w = (-a / c, b / c) (und auch
w = (a / c, -b / c) ) sind orthonormal, wie leicht zu überprüfen ist . Wie sich herausstellt, existiert für jede Wahl des pythagoreischen Triplets eine ganze Zahl r, so dass x und y ganze Zahlen sind. Um dies zu beweisen und r und P effektiv zu finden , brauchen wir eine kleine Zahl / Gruppentheorie; Ich werde die Details schonen. Angenommen, wir haben unser Integral r , x und y . Wir haben noch ein paar Kleinigkeiten: Wir brauchen rum groß genug zu sein und wir wollen eine schnelle Methode, um noch viele ähnliche Paare von diesem abzuleiten. Glücklicherweise gibt es einen einfachen Weg, dies zu erreichen.
Man beachte , dass die Projektion von P auf v ist r v , also R = P · V = (x, y) · (b / c, a / c) = XB / c + YA / c , alles dies , dass sagen xb + ya = rc . Als Ergebnis für alle ganzen Zahlen 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. Mit anderen Worten, die quadratische Größe von Paaren der Form
(x + bn, y + an) ist (r + cn) 2 + 1 , was genau die Art von Paaren ist, nach denen wir suchen! Für groß genug n sind dies unehrliche Größenpaare r + cn .
Es ist immer schön, sich ein konkretes Beispiel anzuschauen. Wenn wir das pythagoreische Triplett (3, 4, 5) nehmen , dann haben wir bei r = 2 P = (1, 2) (Sie können überprüfen, dass (1, 2) · (4/5, 3/5) = 2 und 1 2 + 2 2 = 2 2 + 1. ) Addieren von 5 zu r und (4, 3) zu P führt uns zu r '= 2 + 5 = 7 und P' = (1 + 4, 2 + 3) = (5, 5) . Siehe da, 5 2 + 5 2 = 7 2 + 1. Die nächsten Koordinaten sind r '' = 12 und P '' = (9, 8) und wieder 9 2 + 8 2 = 12 2 + 1 und so weiter und so fort ...
Sobald r groß genug ist, erhalten wir unehrliche Paare mit Größeninkrementen von 5 . Das sind ungefähr 27.797.402 / 5 unehrliche Paare.
Jetzt haben wir also viele unehrliche Paare mit ganzzahliger Größe. Wir können sie leicht mit den ehrlichen Paaren des ersten Programms koppeln, um False-Positives zu bilden, und mit der gebotenen Sorgfalt können wir auch die ehrlichen Paare des zweiten Programms verwenden. Dies ist im Grunde, was dieses Programm tut. Wie das vorherige Programm findet auch es die meisten seiner Ergebnisse sehr früh - es erreicht innerhalb weniger Sekunden 200.000.000 falsch positive Ergebnisse - und verlangsamt sich dann beträchtlich.
Kompilieren mit g++ flspos.cpp -oflspos -std=c++11 -msse2 -mfpmath=sse -O3
. Fügen Sie hinzu, um die Ergebnisse zu überprüfen -DVERIFY
(dies ist erheblich langsamer).
Laufen Sie mit flspos
. Ein beliebiges Befehlszeilenargument für den ausführlichen Modus.
#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);
}