C ++, 275 000 000+
Ми будемо називати пари, величина яких точно представлена, наприклад (x, 0) , як чесні пари, а всі інші пари як нечесні пари з величиною m , де m - неправильно повідомлена величина пари. Перша програма в попередньому дописі використовувала набір тісно пов'язаних пар чесних і нечесних пар:
(x, 0) та (x, 1) відповідно для досить великих x. Друга програма використовувала той самий набір нечесних пар, але розширила набір чесних пар, шукаючи всі чесні пари інтегральної величини. Програма не припиняється протягом десяти хвилин, але вона знаходить переважну більшість своїх результатів дуже рано, а це означає, що більша частина часу роботи втрачається. Замість того, щоб шукати все рідше чесних пар, ця програма використовує вільний час, щоб зробити наступну логічну справу: розширити набір нечесних пар.
З попереднього допису ми знаємо, що для всіх достатньо великих цілих чисел r , sqrt (r 2 + 1) = r , де sqrt є кореневою функцією квадрата з плаваючою комою. Наш план атаки полягає в тому, щоб знайти пари P = (x, y) такі, що x 2 + y 2 = r 2 + 1 для деякого досить великого цілого числа r . Це досить просто зробити, але наївно шукати окремих таких пар занадто повільно, щоб бути цікавим. Ми хочемо знайти ці пари оптом, як і для чесних пар у попередній програмі.
Нехай { v , w } - ортонормальна пара векторів. Для всіх реальних скалярів r , || r v + w || 2 = r 2 + 1 . У ℝ 2 це прямий результат теореми Піфагора:
Ми шукаємо вектори v і w такі, що існує ціле число r, для якого x і y також цілі числа. Зауважимо, що набір нечесних пар, який ми використовували у попередніх двох програмах, був просто окремим випадком цього, де { v , w } є стандартною основою ℝ 2 ; цього разу ми хочемо знайти більш загальне рішення. Ось де піфагорійські трійки (цілі трійки (a, b, c), що задовольняють a 2 + b 2 = c 2, яку ми використовували в попередній програмі) повертаються.
Нехай (a, b, c) є піфагорійська трійка. Вектори v = (b / c, a / c) і w = (-a / c, b / c) (а також
w = (a / c, -b / c) ) є ортонормальними, як це легко перевірити . Як виявляється, для будь-якого вибору трифата Піфагора існує ціле число r таке, що x і y - цілі числа. Щоб довести це і ефективно знайти r і P , нам потрібно невелика теорія чисел / груп; Я збираюся шкодувати деталі. Так чи інакше, припустимо, ми маємо інтеграл r , x і y . Нам ще не вистачає кількох речей: нам потрібен rщоб бути досить великим, і ми хочемо, щоб швидкий метод отримав ще багато подібних пар з цього. На щастя, існує простий спосіб досягти цього.
Зауважимо, що проекція P на v є r v , отже r = P · v = (x, y) · (b / c, a / c) = xb / c + ya / c , все це говорить про те, що xb + ya = rc . В результаті, для всіх цілих п , (х + млрд) 2 + (у + а) 2 = (х 2 + у 2 ) + 2 (XB + уа) п + (а 2 + Ь 2 ) п 2 = ( r 2 + 1) + 2 (rc) n + (c 2 ) n 2 = (r + cn) 2 + 1. Іншими словами, квадратна величина пар форми
(x + bn, y + an) дорівнює (r + cn) 2 + 1 , що саме є тими парами, яких ми шукаємо! Для досить великих n це нечесні пари величини r + cn .
Завжди приємно дивитись на конкретний приклад. Якщо взяти піфагорову триплету (3, 4, 5) , то при r = 2 маємо P = (1, 2) (ви можете перевірити, що (1, 2) · (4/5, 3/5) = 2 і, зрозуміло, 1 2 + 2 2 = 2 2 + 1. ) Додавання 5 до r і (4, 3) до P приводить нас до r '= 2 + 5 = 7 і P' = (1 + 4, 2 + 3) = (5, 5) . Ось і ось, 5 2 + 5 2 = 7 2 + 1. Наступними координатами є r '' = 12 і P '' = (9, 8) , і знову, 9 2 + 8 2 = 12 2 + 1 , і так далі, і так далі ...
Після того, як r досить великий, ми починаємо отримувати нечесні пари з кроком в 5 . Це приблизно 27 797 402/5 нечесних пар.
Тож зараз у нас є безліч нечесних пар цілісної величини. Ми можемо легко з'єднати їх з чесними парами першої програми, щоб сформувати помилкові позитиви, і з належною обережністю ми також можемо використовувати чесні пари другої програми. Це в основному те, що робить ця програма. Як і в попередній програмі, вона теж знаходить більшість своїх результатів дуже рано - вона потрапляє до 200 000 000 помилкових позитивних результатів протягом декількох секунд ---, а потім значно сповільнюється.
Компілювати з g++ flspos.cpp -oflspos -std=c++11 -msse2 -mfpmath=sse -O3
. Щоб перевірити результати, додайте -DVERIFY
(це буде помітно повільніше.)
Бігайте з flspos
. Будь-який аргумент командного рядка для багатослівного режиму.
#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);
}