C ++, 275,000,000+
हम उन जोड़ों का उल्लेख करेंगे जिनकी परिमाण सटीक रूप से प्रतिनिधित्व करने योग्य है, जैसे कि (x, 0) , ईमानदार जोड़े के रूप में और अन्य सभी जोड़ों को परिमाण m के बेईमान जोड़े के रूप में , जहाँ m जोड़े की गलत-कथित परिमाण है। पिछली पोस्ट के पहले कार्यक्रम में ईमानदार और बेईमान-जोड़े के कसकर संबंधित जोड़ों का एक सेट इस्तेमाल किया गया था :
(x, 0) और (x, 1) , क्रमशः काफी बड़े x के लिए। दूसरे कार्यक्रम में बेईमान जोड़ों के एक ही सेट का इस्तेमाल किया गया, लेकिन सभी ईमानदार जोड़े की अभिन्न परिमाण को देखते हुए ईमानदार जोड़े के सेट को बढ़ाया गया। कार्यक्रम दस मिनट के भीतर समाप्त नहीं होता है, लेकिन यह अपने परिणामों के विशाल बहुमत को बहुत पहले ही ढूंढ लेता है, जिसका अर्थ है कि अधिकांश रनटाइम बर्बाद हो जाता है। इस कार्यक्रम में हमेशा कम-लगातार ईमानदार जोड़े की तलाश में रहने के बजाय, यह कार्यक्रम अगली तार्किक बात करने के लिए खाली समय का उपयोग करता है: बेईमान जोड़े के सेट का विस्तार करना ।
पिछली पोस्ट से हम जानते हैं कि सभी बड़े-पर्याप्त पूर्णांक r , sqrt (r 2 + 1) = r के लिए , जहाँ sqrt चल-बिंदु वर्गमूल फ़ंक्शन है। हमले की हमारी योजना कुछ बड़े-पर्याप्त पूर्णांक आर के लिए जोड़े पी = (एक्स, वाई) जैसे कि x 2 + y 2 = r 2 + 1 है । यह करने के लिए पर्याप्त सरल है, लेकिन व्यक्तिगत रूप से ऐसे जोड़े की तलाश दिलचस्प होने के लिए बहुत धीमी है। हम इन जोड़ियों को थोक में ढूंढना चाहते हैं, जैसे हमने पिछले कार्यक्रम में ईमानदार जोड़े के लिए किया था।
बता दें कि { v , w } वैक्टरों की एक असाधारण जोड़ी है। सभी वास्तविक स्केलर r के लिए , || r v + w || 2 = आर 2 + 1 । में ℝ 2 , इस पाइथागोरस प्रमेय का एक सीधा परिणाम है:
हम वैक्टर v और w की तलाश कर रहे हैं जैसे कि एक पूर्णांक r मौजूद है जिसके लिए x और y पूर्णांक भी हैं। एक तरफ ध्यान दें, ध्यान दें कि हम पिछले दो कार्यक्रमों में इस्तेमाल किया बेईमान जोड़े के सेट बस इस है, जहां का एक विशेष मामला था के रूप में { v , w } के मानक आधार था ℝ 2 ; इस बार हम एक अधिक सामान्य समाधान खोजने की इच्छा रखते हैं। यह वह जगह है जहाँ पाइथागोरस ट्रिपलेट्स (पूर्णांक तीनों (ए, बी, सी) 2 + बी 2 = सी 2 को संतुष्ट करते हैं, जो हमने पिछले कार्यक्रम में इस्तेमाल किया था) उनकी वापसी।
आज्ञा देना (ए, बी, सी) एक पायथागॉरियन ट्रिपल हो। वैक्टर v = (b / c, a / c) और w = (-a / c, b / c) (और भी
w = (a / c -b / c) ) orthonormal हैं, जिन्हें सत्यापित करना आसान है । जैसा कि यह पता चला है, पायथागॉरियन ट्रिपल के किसी भी विकल्प के लिए, एक पूर्णांक आर मौजूद है जैसे कि x और y पूर्णांक हैं। यह साबित करने के लिए, और प्रभावी रूप से आर और पी को खोजने के लिए , हमें थोड़ी संख्या / समूह सिद्धांत की आवश्यकता है; मैं विवरणों को बख्शता जा रहा हूं। किसी भी तरह से, मान लें कि हमारे पास हमारे अभिन्न आर , एक्स और वाई हैं । हम अभी भी कुछ चीजों से कम हैं: हमें r की आवश्यकता हैपर्याप्त रूप से बड़ा होना और हम इस एक से कई और समान जोड़े प्राप्त करना चाहते हैं। सौभाग्य से, इसे पूरा करने का एक सरल तरीका है।
ध्यान दें कि P पर v का प्रक्षेपण r v है , इसलिए r = P · v = (x, y) · (b / c, a / c) = xb / c + ya / c , यह सब कहना है कि xb + य = आरसी । परिणामस्वरूप, सभी पूर्णांकों के लिए n , (x + bn) 2 + (y + a) 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 + अरब, y + एक) है (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 के लिए आर और (4, 3) के पी करने के लिए हमें ले जाता है आर '= 2 + 5 = 7 और पी' = (1 + 4, 2 + 3) = (5, 5) । लो और निहारना, 5 2 + 5 2 = 7 2 + 1। अगले निर्देशांक आर '' = 12 और पी '' = (9, 8) हैं , और फिर से, 9 2 + 8 2 = 12 2 + 1 , और इसी तरह, और इतने पर ...
एक बार जब आर काफी बड़ा हो जाता है, तो हम 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);
}