C ++, 275,000,000+
(x, 0) 과 같은 크기 를 정직한 쌍으로 , 다른 모든 쌍을 부정직 한 크기의 m 으로 표현할 수있는 쌍을 참조합니다 . 여기서 m 은 쌍의 잘못보고 된 크기입니다. 이전 게시물 의 첫 번째 프로그램은 충분히 큰 x에 대해
(x, 0) 및 (x, 1) 과 밀접하게 관련된 정직하고 부정직 한 쌍을 사용했습니다.. 두 번째 프로그램은 동일한 부정직 한 쌍을 사용했지만 모든 정직한 크기의 정직한 쌍을 찾아서 정직한 쌍을 확장했습니다. 이 프로그램은 10 분 안에 종료되지 않지만 대부분의 결과는 매우 초기에 발견되므로 대부분의 런타임이 낭비됩니다. 자주없는 정직한 쌍을 계속 찾는 대신이 프로그램은 여가 시간을 사용하여 다음 논리적 인 일을 수행합니다 . 부정직 한 쌍 집합 확장 .
이전 게시물에서 우리는 모든 큰 정수 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 및 승 거기에 존재하도록 정수 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 + 나중에 / 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'= (+ 4 1 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 부정직 한 쌍입니다.
이제 우리는 정수 크기의 부정직 한 쌍을 많이 가지고 있습니다. 우리는 그것들을 첫 프로그램의 정직한 쌍과 쉽게 결합하여 오탐 (false-positive)을 형성 할 수 있으며,주의를 기울이면 두 번째 프로그램의 정직한 쌍을 사용할 수도 있습니다. 이것이 기본적으로이 프로그램이하는 일입니다. 이전 프로그램과 마찬가지로이 프로그램은 결과의 대부분을 매우 일찍 발견합니다. 몇 초 내에 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);
}