C ++, hơn 275.000.000
Chúng ta sẽ đề cập đến các cặp có độ lớn có thể biểu diễn chính xác, chẳng hạn như (x, 0) , là các cặp trung thực và tất cả các cặp khác là các cặp cường độ m không trung thực , trong đó m là cường độ được báo cáo sai của cặp. Chương trình đầu tiên trong bài trước đã sử dụng một tập hợp các cặp cặp trung thực và không trung thực:
(x, 0) và (x, 1) , tương ứng, cho x đủ lớn. Chương trình thứ hai sử dụng cùng một cặp các cặp không trung thực nhưng đã mở rộng tập các cặp trung thực bằng cách tìm kiếm tất cả các cặp có độ lớn tích phân trung thực. Chương trình không chấm dứt trong vòng mười phút, nhưng nó tìm thấy phần lớn kết quả của nó từ rất sớm, điều đó có nghĩa là hầu hết thời gian chạy đều bị lãng phí. Thay vì tiếp tục tìm kiếm các cặp trung thực ít thường xuyên hơn, chương trình này sử dụng thời gian rảnh rỗi để làm điều hợp lý tiếp theo: mở rộng tập hợp các cặp không trung thực .
Từ bài trước chúng ta biết rằng với tất cả các số nguyên đủ lớn r , sqrt (r 2 + 1) = r , trong đó sqrt là hàm căn bậc hai dấu phẩy động. Kế hoạch tấn công của chúng tôi là tìm các cặp P = (x, y) sao cho x 2 + y 2 = r 2 + 1 cho một số nguyên r đủ lớn . Điều đó đủ đơn giản để làm, nhưng ngây thơ tìm kiếm những cặp như vậy thì quá chậm để trở nên thú vị. Chúng tôi muốn tìm các cặp này với số lượng lớn, giống như chúng tôi đã làm cho các cặp trung thực trong chương trình trước.
Đặt { v , w } là một cặp vectơ trực giao. Đối với tất cả các vô hướng thực r , | | r v + w | | 2 = r 2 + 1 . Trong ℝ 2 , đây là một kết quả trực tiếp của định lý Pythagore:
Chúng ta đang tìm các vectơ v và w sao cho tồn tại một số nguyên r mà x và y cũng là các số nguyên. Lưu ý phụ, lưu ý rằng tập hợp các cặp không trung thực mà chúng tôi đã sử dụng trong hai chương trình trước chỉ đơn giản là trường hợp đặc biệt của điều này, trong đó { v , w } là cơ sở tiêu chuẩn của ℝ 2 ; lần này chúng tôi muốn tìm một giải pháp tổng quát hơn. Đây là nơi bộ ba Pythagore (bộ ba số nguyên (a, b, c) thỏa mãn a 2 + b 2 = c 2, mà chúng tôi đã sử dụng trong chương trình trước đó) làm cho sự trở lại của họ.
Đặt (a, b, c) là một bộ ba Pythagore. Các vectơ v = (b / c, a / c) và w = (-a / c, b / c) (và cả
w = (a / c, -b / c) ) là trực giao, vì rất dễ xác minh . Hóa ra, đối với bất kỳ sự lựa chọn nào của bộ ba Pythagore, tồn tại một số nguyên r sao cho x và y là các số nguyên. Để chứng minh điều này, và để tìm ra r và P một cách hiệu quả , chúng ta cần một lý thuyết số / nhóm nhỏ; Tôi sẽ tiết kiệm chi tiết. Dù bằng cách nào, giả sử chúng ta có tích phân r , x và y . Chúng ta vẫn còn thiếu một vài điều: chúng ta cần rđủ lớn và chúng tôi muốn một phương pháp nhanh để lấy được nhiều cặp tương tự từ cái này. May mắn thay, có một cách đơn giản để thực hiện điều này.
Lưu ý rằng hình chiếu của P lên v là r v , do đó r = P · v = (x, y) · (b / c, a / c) = xb / c + ya / c , tất cả điều này để nói rằng xb + ya = RC . Kết quả là, với mọi số nguyên 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. Nói cách khác, độ lớn bình phương của các cặp có dạng
(x + bn, y + an) là (r + cn) 2 + 1 , đây chính xác là loại cặp chúng ta đang tìm kiếm! Đối với n đủ lớn , đây là những cặp độ lớn không trung thực r + cn .
Thật tuyệt khi nhìn vào một ví dụ cụ thể. Nếu chúng ta lấy bộ ba Pythagore (3, 4, 5) , thì tại r = 2, chúng ta có P = (1, 2) (bạn có thể kiểm tra xem (1, 2) · (4/5, 3/5) = 2 và, rõ ràng, 1 2 + 2 2 = 2 2 + 1. ) Thêm 5 vào r và (4, 3) vào P sẽ đưa chúng ta đến r '= 2 + 5 = 7 và P' = (1 + 4, 2 + 3) = (5, 5) . Lo và kìa, 5 2 + 5 2 = 7 2 + 1. Các tọa độ tiếp theo là r '' = 12 và P '' = (9, 8) , và một lần nữa, 9 2 + 8 2 = 12 2 + 1 , v.v.
Khi r đủ lớn, chúng ta bắt đầu nhận được các cặp không trung thực với mức tăng cường độ là 5 . Đó là khoảng 27.797.402 / 5 cặp không trung thực.
Vì vậy, bây giờ chúng ta có rất nhiều cặp không trung thực tích hợp. Chúng ta có thể dễ dàng ghép chúng với các cặp trung thực của chương trình đầu tiên để tạo thành dương tính giả và với sự cẩn trọng, chúng ta cũng có thể sử dụng các cặp trung thực của chương trình thứ hai. Đây là cơ bản những gì chương trình này làm. Giống như chương trình trước đó, nó cũng tìm thấy hầu hết các kết quả từ rất sớm --- nó nhận được tới 200.000.000 dương tính giả trong vài giây --- và sau đó chậm lại đáng kể.
Biên dịch với g++ flspos.cpp -oflspos -std=c++11 -msse2 -mfpmath=sse -O3
. Để xác minh kết quả, hãy thêm -DVERIFY
(điều này sẽ chậm hơn đáng kể.)
Chạy với flspos
. Bất kỳ đối số dòng lệnh cho chế độ dài dòng.
#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);
}