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 , ш } был стандартный базис ℝ 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 . Мы по- прежнему не хватает нескольких вещей: мы должны гбыть достаточно большим, и мы хотим, чтобы быстрый метод вывел еще много подобных пар из этой. К счастью, есть простой способ сделать это.
Обратите внимание, что проекция P на v есть r v , следовательно, r = P · v = (x, y) · (b / c, a / c) = xb / c + ya / c , все это говорит о том, что xb + ya = rc . В результате для всех целых чисел 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, Другими словами, квадратичная величина пар вида
(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);
}