C ++, 275.000.000+
Kami akan merujuk pada pasangan yang besarnya dapat diwakili secara akurat, seperti (x, 0) , sebagai pasangan yang jujur dan untuk semua pasangan lainnya sebagai pasangan yang tidak jujur dari besarnya m , di mana m adalah besarnya pasangan yang dilaporkan salah. Program pertama pada posting sebelumnya menggunakan satu set pasangan yang terkait erat pasangan jujur dan tidak jujur:
(x, 0) dan (x, 1) , masing-masing, untuk x yang cukup besar. Program kedua menggunakan pasangan pasangan yang tidak jujur yang sama tetapi memperluas pasangan pasangan yang jujur dengan mencari semua pasangan jujur yang besarnya tidak terpisahkan. Program tidak berhenti dalam sepuluh menit, tetapi menemukan sebagian besar hasilnya sangat awal, yang berarti bahwa sebagian besar runtime menjadi sia-sia. Alih-alih terus mencari pasangan jujur yang semakin jarang, program ini menggunakan waktu luang untuk melakukan hal logis berikutnya: memperluas perangkat tidak jujur pasangan .
Dari posting sebelumnya kita tahu bahwa untuk semua bilangan bulat besar cukup r , sqrt (r 2 + 1) = r , di mana sqrt adalah floating-point fungsi akar kuadrat. Rencana kami dari serangan adalah untuk menemukan pasangan P = (x, y) sehingga x 2 + y 2 = r 2 + 1 untuk beberapa besar cukup bilangan bulat r . Itu cukup sederhana untuk dilakukan, tetapi secara naif mencari pasangan seperti itu terlalu lambat untuk menarik. Kami ingin menemukan pasangan ini secara massal, seperti yang kami lakukan untuk pasangan jujur di program sebelumnya.
Biarkan { v , w } menjadi pasangan vektor ortonormal. Untuk semua skalar nyata r , || r v + w || 2 = r 2 + 1 . Dalam ℝ 2 , ini adalah akibat langsung dari teorema Pythagoras:
Kami sedang mencari vektor v dan w sedemikian rupa sehingga ada bilangan r yang x dan y juga bilangan bulat. Sebagai catatan tambahan, perhatikan bahwa himpunan pasangan tidak jujur yang kami gunakan dalam dua program sebelumnya hanyalah kasus khusus ini, di mana { v , w } adalah basis standar dari ℝ 2 ; kali ini kami ingin menemukan solusi yang lebih umum. Di sinilah kembar tiga Pythagoras (bilangan bulat bilangan bulat (a, b, c) memuaskan a 2 + b 2 = c 2, yang kami gunakan dalam program sebelumnya) membuat comeback mereka.
Biarkan (a, b, c) menjadi triplet Pythagoras. Vektor v = (b / c, a / c) dan w = (-a / c, b / c) (dan juga
w = (a / c, -b / c) ) adalah ortonormal, karena mudah untuk memverifikasi . Ternyata, untuk sembarang pilihan triplet Pythagoras, ada bilangan r sehingga x dan y adalah bilangan bulat. Untuk membuktikan ini, dan untuk secara efektif menemukan r dan P , kita memerlukan sedikit teori / kelompok; Saya akan menyimpan rinciannya. Either way, misalkan kita memiliki r integral , x dan y . Kami masih kekurangan beberapa hal: kami perlu rcukup besar dan kami ingin metode cepat untuk mendapatkan lebih banyak pasangan serupa dari yang satu ini. Untungnya, ada cara sederhana untuk mencapai ini.
Perhatikan bahwa proyeksi P ke v adalah r v , maka r = P · v = (x, y) · (b / c, a / c) = xb / c + ya / c , semua ini untuk mengatakan bahwa xb + ya = rc . Akibatnya, untuk semua bilangan bulat 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. Dengan kata lain, besarnya kuadrat pasangan bentuk
(x + bn, y + an) adalah (r + cn) 2 + 1 , yang merupakan jenis pasangan yang kami cari! Untuk n yang cukup besar , ini adalah pasangan magnitudo r + cn yang tidak jujur .
Selalu menyenangkan untuk melihat contoh konkret. Jika kita mengambil triplet Pythagoras (3, 4, 5) , maka pada r = 2 kita memiliki P = (1, 2) (Anda dapat memeriksa bahwa (1, 2) · (4/5, 3/5) = 2 dan, jelas, 1 2 + 2 2 = 2 2 + 1. ) Menambahkan 5 ke r dan (4, 3) ke P membawa kita ke r '= 2 + 5 = 7 dan P' = (1 + 4, 2 + 3) = (5, 5) . Lihatlah, 5 2 + 5 2 = 7 2 + 1. Koordinat berikutnya adalahr '' = 12 dan P '' = (9, 8) , dan lagi, 9 2 + 8 2 = 12 2 +1 , dan seterusnya, dan seterusnya ...
Setelah r cukup besar, kita mulai mendapatkan pasangan yang tidak jujur dengan kenaikan 5 . Itu kira-kira 27.797.402 / 5 pasangan tidak jujur.
Jadi sekarang kita memiliki banyak pasangan tidak jujur yang besarnya tidak terpisahkan. Kita dapat dengan mudah memasangkan mereka dengan pasangan jujur dari program pertama untuk membentuk false-positive, dan dengan hati-hati kita juga dapat menggunakan pasangan jujur dari program kedua. Inilah yang pada dasarnya dilakukan oleh program ini. Seperti program sebelumnya, ia juga menemukan sebagian besar hasilnya sangat awal --- ia mendapatkan 200.000.000 positif palsu dalam beberapa detik --- dan kemudian melambat jauh.
Kompilasi dengan g++ flspos.cpp -oflspos -std=c++11 -msse2 -mfpmath=sse -O3
. Untuk memverifikasi hasil, tambahkan -DVERIFY
(ini akan lebih lambat.)
Jalankan dengan flspos
. Argumen baris perintah apa pun untuk mode verbose.
#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);
}