C ++, 275.000.000+
Bu gibi, büyüklüğü doğru sunulabilen çiftleri, referans alırız (x, 0) olarak, dürüst çiftleri ve diğer tüm çiftleri dürüst olmayan çift genliği m , m paritenin yanlış bildirilen büyüklüğüdür. Önceki yazıdaki ilk program, dürüst ve dürüst olmayan çiftlerin sıkıca ilişkili bir çiftini kullandı: yeterince büyük x için sırasıyla
(x, 0) ve (x, 1).. İkinci program aynı dürüst olmayan çiftleri kullandı, ancak dürüst çiftlerin bütün dürüst çiftlerini arayarak genişledi. Program on dakika içinde sona ermez, ancak sonuçlarının büyük çoğunluğunu çok erken bulur, bu da çalışma zamanının çoğunun boşa gittiği anlamına gelir. Her zaman daha az sıklıkta dürüst çiftler aramak yerine, bu program bir sonraki mantıklı şeyi yapmak için boş zamanı kullanır: dürüst olmayan çiftler kümesini genişletmek .
Önceki yazıdan , yeterince büyük olan tamsayılar için r , sqrt (r 2 + 1) = r , burada sqrt kayan nokta karekök fonksiyonudur. Saldırı planı bulmaktır çift p = (x, y) bu tür x 2 + y 2 = r 2 + 1 , bazı yeterince büyük tam sayı için r . Bunu yapmak için yeterince basit, ancak bu tür çiftleri saf bir şekilde aramak ilginç olmak için çok yavaş. Bu çiftleri, tıpkı bir önceki programdaki dürüst çiftler için yaptığımız gibi, toplu olarak bulmak istiyoruz.
Let { v , w } vektörlerin bir ortonormal çifti olabilir. Tüm gerçek skalar için r , || r v + w || 2 = r 2 + 1 . In ℝ 2 , bu Pisagor teoreminin doğrudan bir sonucudur:
Bu vektörler için ideal v ve w orada bulunacak şekilde tam sayı r olan x ve y de tamsayılardır. Bir yan not olarak, önceki iki programda kullandığımız dürüst olmayan çiftler setinin bunun özel bir durumu olduğunu unutmayın; burada { v , w } , ℝ 2'nin standart temeli idi ; bu sefer daha genel bir çözüm bulmak istiyoruz. Bu burada Pisagor üçlüler (tamsayı üçlü (a, b, c) tatmin edici bir 2 + B 2 = C 2önceki programda kullandığımız) geri dönüşlerini yaparlar.
Let , (a, b, c) bir Pisagor üçlü olabilir. Vektörleri v = (b / c, a / c) ve w = (-a / C, B / C) (ve aynı zamanda
ağırlık = (A / C, b / c) ) ortonormaldir, kolay doğrulanmasıdır . Anlaşıldığı gibi, herhangi bir Pisagor üçlüsü seçimi için , x ve y tamsayı olacak şekilde r tamsayısı vardır. Bunu kanıtlamak ve r ve P'yi etkili bir şekilde bulmak için az sayıda / grup teorisine ihtiyacımız var; Ayrıntıları ayıracağım. Her iki şekilde de, diyelim ki r , x ve y integralimiz var . Hala birkaç şeyden kısayız: r'ye ihtiyacımız varyeterince büyük olacak ve bundan çok daha fazla benzer çift elde etmek için hızlı bir yöntem istiyoruz. Neyse ki, bunu başarmanın basit bir yolu var.
Not çıkıntısı bu P üzerine v olan R v , dolayısıyla r = P · v = (x, y) · (b / c / c) = Xb / C + ya / C , bu demek bu xb + ya = rc . Sonuç olarak, tüm n tamsayıları için , (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. Başka bir deyişle, formun çiftleri karesi alınan büyüklüğü
(x + bn y + an) olduğu (r + cn) 2 + 1 aradığımız çiftlerinin tür tam olarak,! Yeterince büyük n için bunlar, dürüst olmayan büyüklük r + cn çiftleridir .
Somut bir örneğe bakmak her zaman güzeldir. Pisagor üçlüsünü (3, 4, 5) alırsak , r = 2'de P = (1, 2) var ( (1, 2) · (4/5, 3/5) = 2 ve, açık bir şekilde, 1 2 + 2 2 = 2 2 + 1 ). ekleme 5 için r ve (4, 3) için p götürür r '= 2 + 5 = 7 ve P' = (1 + 4, 2 + 3) = (5, 5) . Bakın, 5 2 + 5 2 = 7 2 + 1. Bir sonraki koordinatlar r '' = 12 ve P '' = (9, 8) ve yine 9 2 + 8 2 = 12 2 + 1 , vb.
Bir kez r Yeterince büyük, biz büyüklüğü artışlarla birlikte dürüst olmayan çiftleri almaya başlamak 5 . Bu kabaca 27.797.402 / 5 dürüst olmayan çifttir.
Şimdi bol miktarda integral-büyük dürüst olmayan çiftimiz var. Yanlış pozitifler oluşturmak için onları ilk programın dürüst çiftleriyle kolayca birleştirebiliriz ve dikkatle ikinci programın dürüst çiftlerini de kullanabiliriz. Temel olarak bu programın yaptığı budur. Önceki program gibi, sonuçlarının çoğunu çok erken bulur - birkaç saniye içinde 200.000.000 yanlış pozitif alır - ve sonra önemli ölçüde yavaşlar.
İle derleyin g++ flspos.cpp -oflspos -std=c++11 -msse2 -mfpmath=sse -O3
. Sonuçları doğrulamak için ekleyin -DVERIFY
(bu oldukça yavaş olacaktır.)
İle çalıştırın flspos
. Ayrıntılı mod için herhangi bir komut satırı argümanı.
#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);
}