Tích cực sai trên một mạng nguyên


12

Bảng xếp hạng

 User            Language      Score
 =========================================
 Ell             C++11         293,619,555
 feersum         C++11         100,993,667
 Ell             C++11          78,824,732
 Geobits         Java           27,817,255
 Ell             Python         27,797,402
 Peter Taylor    Java                2,468
 <reference>     Julia                 530

Lý lịch

Khi làm việc trên lưới 2 chiều có tọa độ nguyên, đôi khi bạn muốn biết liệu hai vectơ (có thành phần nguyên) có cùng độ lớn hay không. Tất nhiên, trong hình học Euclide, độ lớn của vectơ (x,y)được cho bởi

√(x² + y²)

Vì vậy, một triển khai ngây thơ có thể tính giá trị này cho cả hai vectơ và so sánh kết quả. Điều này không chỉ gây ra tính toán căn bậc hai không cần thiết, mà còn gây ra sự không chính xác cho dấu phẩy động, có thể mang lại kết quả dương: các vectơ có độ lớn khác nhau, nhưng trong đó các chữ số có nghĩa trong biểu diễn điểm nổi đều giống hệt nhau.

Đối với mục đích của thử thách này, chúng tôi xác định dương tính giả là một cặp tọa độ (a,b)và theo (c,d)đó:

  • Độ lớn bình phương của chúng là khác nhau khi được biểu diễn dưới dạng số nguyên không dấu 64 bit.
  • Độ lớn của chúng là giống hệt nhau khi được biểu diễn dưới dạng số dấu phẩy động nhị phân 64 bit và được tính thông qua một căn bậc hai 64 bit (theo IEEE 754 ).

Ví dụ: sử dụng các biểu diễn 16 bit (thay vì 64), 1 cặp vectơ nhỏ nhất mang lại dương tính giả là

(25,20) and (32,0)

Độ lớn bình phương bình phương của họ là 10251024. Lấy năng suất căn bậc hai

32.01562118716424 and 32.0

Nhưng trong phao 16 bit, cả hai đều bị cắt cụt 32.0.

Tương tự, cặp 2 nhỏ nhất mang lại dương tính giả cho biểu diễn 32 bit là

(1659,1220) and (1951,659)

1 "Nhỏ nhất" được đo bằng cường độ điểm nổi 16 bit của chúng.
2 "Nhỏ nhất" được đo bằng cường độ điểm nổi 32 bit của chúng.

Cuối cùng, đây là một số ít trường hợp 64 bit hợp lệ:

 (51594363,51594339) and (54792160,48184783)
 (54356775,54353746) and (54620742,54088476)
 (54197313,46971217) and (51758889,49645356)
 (67102042,  956863) and (67108864,       6) *

* Trường hợp cuối cùng là một trong nhiều trường hợp có cường độ nhỏ nhất có thể đối với dương tính giả 64 bit.

Các thách thức

Trong ít hơn 10.000 byte mã, sử dụng một luồng, bạn sẽ tìm thấy nhiều số dương cho các số dấu phẩy động 64 bit (nhị phân) trong phạm vi tọa độ 0 ≤ y ≤ x(nghĩa là chỉ trong quãng tám đầu tiên của mặt phẳng Euclidian) như vậy trong vòng 10 phút . Nếu hai lần gửi kết hợp cho cùng một số cặp, thì bộ ngắt kết nối là thời gian thực tế để tìm ra lần cuối cùng của các cặp đó.x² + y² ≤ 253

Chương trình của bạn không được sử dụng quá 4 GB bộ nhớ bất cứ lúc nào (vì lý do thực tế).

Có thể chạy chương trình của bạn ở hai chế độ: một chế độ xuất ra mọi cặp khi nó tìm thấy và một chế độ chỉ xuất ra số lượng cặp tìm thấy ở cuối. Cái đầu tiên sẽ được sử dụng để xác minh tính hợp lệ của các cặp của bạn (bằng cách xem một số mẫu đầu ra) và cái thứ hai sẽ được sử dụng để thực sự định thời gian gửi của bạn. Lưu ý rằng việc in ấn phải là sự khác biệt duy nhất . Đặc biệt, chương trình đếm có thể không mã hóa số lượng cặp có thể tìm thấy. Nó vẫn phải thực hiện cùng một vòng lặp sẽ được sử dụng để in tất cả các số và chỉ bỏ qua việc in!

Tôi sẽ kiểm tra tất cả các bài nộp trên máy tính xách tay Windows 8 của mình, vì vậy vui lòng hỏi trong các nhận xét nếu bạn muốn sử dụng một số ngôn ngữ không quá phổ biến.

Lưu ý rằng các cặp không được tính hai lần khi chuyển đổi cặp tọa độ thứ nhất và thứ hai.

Cũng lưu ý rằng tôi sẽ chạy quy trình của bạn thông qua bộ điều khiển Ruby, điều này sẽ giết quá trình của bạn nếu nó chưa kết thúc sau 10 phút. Hãy chắc chắn để xuất số lượng cặp được tìm thấy sau đó. Bạn có thể tự theo dõi thời gian và in kết quả ngay trước khi hết 10 phút hoặc bạn chỉ cần xuất số lượng cặp được tìm thấy một cách rời rạc và tôi sẽ lấy số cuối cùng làm điểm số của bạn.


Là một nhận xét phụ, có thể xác định đồng thời một số nguyên có phải là một hình vuông hoàn hảo hay không và cũng tính toán căn bậc hai chính xác của nó một cách hiệu quả. Thuật toán sau nhanh hơn 5x so với căn bậc hai phần cứng trên hệ thống của tôi (so sánh số nguyên không dấu 64 bit với gấp đôi dài 80 bit): math.stackexchange.com/questions/41337/ trộm
Todd Lehman

Câu trả lời:


5

C ++, hơn 275.000.000

Chúng ta sẽ đề cập đến các cặp có độ lớn có thể biểu diễn chính xác, chẳng hạn như (x, 0) , là các cặp trung thực và tất cả các cặp khác là các cặp cường độ m không trung thực , trong đó m là cường độ được báo cáo sai của cặp. Chương trình đầu tiên trong bài trước đã sử dụng một tập hợp các cặp cặp trung thực và không trung thực:
(x, 0)(x, 1) , tương ứng, cho x đủ lớn. Chương trình thứ hai sử dụng cùng một cặp các cặp không trung thực nhưng đã mở rộng tập các cặp trung thực bằng cách tìm kiếm tất cả các cặp có độ lớn tích phân trung thực. Chương trình không chấm dứt trong vòng mười phút, nhưng nó tìm thấy phần lớn kết quả của nó từ rất sớm, điều đó có nghĩa là hầu hết thời gian chạy đều bị lãng phí. Thay vì tiếp tục tìm kiếm các cặp trung thực ít thường xuyên hơn, chương trình này sử dụng thời gian rảnh rỗi để làm điều hợp lý tiếp theo: mở rộng tập hợp các cặp không trung thực .

Từ bài trước chúng ta biết rằng với tất cả các số nguyên đủ lớn r , sqrt (r 2 + 1) = r , trong đó sqrt là hàm căn bậc hai dấu phẩy động. Kế hoạch tấn công của chúng tôi là tìm các cặp P = (x, y) sao cho x 2 + y 2 = r 2 + 1 cho một số nguyên r đủ lớn . Điều đó đủ đơn giản để làm, nhưng ngây thơ tìm kiếm những cặp như vậy thì quá chậm để trở nên thú vị. Chúng tôi muốn tìm các cặp này với số lượng lớn, giống như chúng tôi đã làm cho các cặp trung thực trong chương trình trước.

Đặt { v , w } là một cặp vectơ trực giao. Đối với tất cả các vô hướng thực r , | | r v + w | | 2 = r 2 + 1 . Trong 2 , đây là một kết quả trực tiếp của định lý Pythagore:

Hình 1

Chúng ta đang tìm các vectơ vw sao cho tồn tại một số nguyên rxy cũng là các số nguyên. Lưu ý phụ, lưu ý rằng tập hợp các cặp không trung thực mà chúng tôi đã sử dụng trong hai chương trình trước chỉ đơn giản là trường hợp đặc biệt của điều này, trong đó { v , w } là cơ sở tiêu chuẩn của 2 ; lần này chúng tôi muốn tìm một giải pháp tổng quát hơn. Đây là nơi bộ ba Pythagore (bộ ba số nguyên (a, b, c) thỏa mãn a 2 + b 2 = c 2, mà chúng tôi đã sử dụng trong chương trình trước đó) làm cho sự trở lại của họ.

Đặt (a, b, c) là một bộ ba Pythagore. Các vectơ v = (b / c, a / c)w = (-a / c, b / c) (và cả
w = (a / c, -b / c) ) là trực giao, vì rất dễ xác minh . Hóa ra, đối với bất kỳ sự lựa chọn nào của bộ ba Pythagore, tồn tại một số nguyên r sao cho xy là các số nguyên. Để chứng minh điều này, và để tìm ra rP một cách hiệu quả , chúng ta cần một lý thuyết số / nhóm nhỏ; Tôi sẽ tiết kiệm chi tiết. Dù bằng cách nào, giả sử chúng ta có tích phân r , xy . Chúng ta vẫn còn thiếu một vài điều: chúng ta cần rđủ lớn và chúng tôi muốn một phương pháp nhanh để lấy được nhiều cặp tương tự từ cái này. May mắn thay, có một cách đơn giản để thực hiện điều này.

Lưu ý rằng hình chiếu của P lên vr v , do đó r = P · v = (x, y) · (b / c, a / c) = xb / c + ya / c , tất cả điều này để nói rằng xb + ya = RC . Kết quả là, với mọi số nguyên 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. Nói cách khác, độ lớn bình phương của các cặp có dạng
(x + bn, y + an)(r + cn) 2 + 1 , đây chính xác là loại cặp chúng ta đang tìm kiếm! Đối với n đủ lớn , đây là những cặp độ lớn không trung thực r + cn .

Thật tuyệt khi nhìn vào một ví dụ cụ thể. Nếu chúng ta lấy bộ ba Pythagore (3, 4, 5) , thì tại r = 2, chúng ta có P = (1, 2) (bạn có thể kiểm tra xem (1, 2) · (4/5, 3/5) = 2 và, rõ ràng, 1 2 + 2 2 = 2 2 + 1. ) Thêm 5 vào r(4, 3) vào P sẽ đưa chúng ta đến r '= 2 + 5 = 7P' = (1 + 4, 2 + 3) = (5, 5) . Lo và kìa, 5 2 + 5 2 = 7 2 + 1. Các tọa độ tiếp theo là r '' = 12P '' = (9, 8) , và một lần nữa, 9 2 + 8 2 = 12 2 + 1 , v.v.

Hình 2

Khi r đủ lớn, chúng ta bắt đầu nhận được các cặp không trung thực với mức tăng cường độ là 5 . Đó là khoảng 27.797.402 / 5 cặp không trung thực.

Vì vậy, bây giờ chúng ta có rất nhiều cặp không trung thực tích hợp. Chúng ta có thể dễ dàng ghép chúng với các cặp trung thực của chương trình đầu tiên để tạo thành dương tính giả và với sự cẩn trọng, chúng ta cũng có thể sử dụng các cặp trung thực của chương trình thứ hai. Đây là cơ bản những gì chương trình này làm. Giống như chương trình trước đó, nó cũng tìm thấy hầu hết các kết quả từ rất sớm --- nó nhận được tới 200.000.000 dương tính giả trong vài giây --- và sau đó chậm lại đáng kể.

Biên dịch với g++ flspos.cpp -oflspos -std=c++11 -msse2 -mfpmath=sse -O3. Để xác minh kết quả, hãy thêm -DVERIFY(điều này sẽ chậm hơn đáng kể.)

Chạy với flspos. Bất kỳ đối số dòng lệnh cho chế độ dài dòng.

#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);
}

Đẹp! :) Tôi đã nhận được 293.619.555 trên máy của mình và cập nhật bảng xếp hạng.
Martin Ender

8

Con trăn, 27.797.402

Chỉ cần đặt thanh cao hơn một chút ...

from sys import argv
verbose = len(argv) > 1
found = 0
for x in xrange(67108864, 94906266):
    found += 1
    if verbose:
        print "(%d, 0) (%d, 1)" % (x, x)
print found

Thật dễ dàng để xác minh rằng với tất cả 67.108.864 <= x <= 94,906,265 = sàn (sqrt (2 53 )), các cặp (x, 0)(x, 1) là dương tính giả.

Tại sao nó hoạt động : 67.108.864 = 2 26 . Do đó, tất cả các số x trong phạm vi trên đều có dạng 2 26 + x ' với một số 0 <= x' <2 26 . Với mọi e dương , (x + e) 2 = x 2 + 2xe + e 2 = x 2 + 2 27 e + 2x'e + e 2 . Nếu chúng ta muốn có
(x + e) 2 = x 2 + 1, chúng ta cần ít nhất 2 27 e <= 1 , nghĩa là, e <= 2 -27 Tuy nhiên, vì mantissa của các số dấu phẩy động có độ chính xác kép rộng 52 bit, e nhỏ nhất sao cho x + e> xe = 2 26 - 52 = 2 -26 . Nói cách khác, số đại diện nhỏ nhất lớn hơn xx + 2 -26 trong khi kết quả của sqrt (x 2 + 1) nhiều nhất là x + 2 -27 . Vì chế độ làm tròn mặc định của IEEE-754 là từ tròn đến gần nhất; liên kết ngang nhau, nó sẽ luôn làm tròn thành x và không bao giờ thành x + 2 -26 (trong đó tie-break thực sự chỉ phù hợp với x = 67.108.864, nếu có Bất kỳ số lớn hơn sẽ làm tròn đến x bất kể).


C ++, 75.000.000 trở lên

Nhắc lại rằng 3 2 + 4 2 = 5 2 . Điều này có nghĩa là điểm (4, 3) nằm trên vòng tròn bán kính 5 tập trung xung quanh gốc tọa độ . Trên thực tế, với mọi số nguyên n , (4n, 3n) nằm trên đường tròn bán kính 5n như vậy . Đối với n đủ lớn (cụ thể là 5n> = 2 26 ), chúng ta đã biết dương tính giả cho tất cả các điểm trên vòng tròn này: (5n, 1) . Tuyệt quá! Đó là 27.797.402 / 5 cặp dương tính giả miễn phí khác ngay tại đó! Nhưng tại sao dừng lại ở đây? (3, 4, 5) không phải là bộ ba duy nhất như vậy.

Chương trình này tìm kiếm tất cả các bộ ba số nguyên dương (a, b, c) sao cho a 2 + b 2 = c 2 và đếm số dương giả trong kiểu này. Nó nhận được tới 70.000.000 dương tính giả khá nhanh nhưng sau đó chậm lại đáng kể khi số lượng tăng lên.

Biên dịch với g++ flspos.cpp -oflspos -std=c++11 -msse2 -mfpmath=sse -O3. Để xác minh kết quả, hãy thêm -DVERIFY(điều này sẽ chậm hơn đáng kể.)

Chạy với flspos. Bất kỳ đối số dòng lệnh cho chế độ dài dòng.

#include <cstdio>
#include <cmath>
#include <cfloat>

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> inline long long sqr(T x) { return 1ll * x * x; }
template <typename T>
T gcd(T a, T b) { while (b) { T t = a; a = b; b = t % b; } return a; }

int main(int argc, const char* argv[]) {
    const bool verbose = argc > 1;

    const int min = 1 << 26;
    const int max = sqrt(1ll << 53);

    int found = 0;
    auto add = [=, &found] (int x1, int y1, int x2, int y2) {
#ifdef VERIFY
        auto n1 = sqr(x1) + sqr(y1), n2 = sqr(x2) + sqr(y2);
        if (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, x2, y1, y2);
        ++found;
    };

    for (int x = min; x <= max; ++x) add(x, 0,    x, 1);

    for (int a = 1; a < max; ++a) {
        auto a2b2 = sqr(a) + 1;
        for (int b = 1; b <= a; a2b2 += 2 * b + 1, ++b) {
            int c = sqrt(a2b2);
            if (a2b2 == sqr(c) && gcd(a, b) == 1) {
                int max_c = max / c;
                for (int n = (min + c - 1) / c; n <= max_c; ++n)
                    add(n * a, n * b,    n * c, 1);
            }
        }

        if (a % 512 == 0) printf("%d\n", found), fflush(stdout);
    }

    printf("%d\n", found);
}

Yeesh, đó là một chiến lược hiệu quả. Tôi đã nghĩ rằng 2**53ranh giới đã được chọn để loại trừ điều này, nhưng tôi đoán là không.
xnor

Thật buồn cười là mọi số trong phạm vi này hoạt động mà không có một trường hợp căn bậc hai của x ^ 2 và x ^ 2 + 1 rơi vào các cạnh khác nhau của một số nguyên + 1/2.
frageum

@xnor Ranh giới được chọn để độ lớn bình phương có thể biểu diễn chính xác trong các phao 64 bit.
Martin Ender

Này, nó hoạt động, ai quan tâm thế nào? ;) Bạn có nghĩa là chương trình nên được tính trong một vòng lặp giả, hoặc thực sự xác minh kết quả?
Ell

@MartinButtner ơi, tôi hiểu rồi. Có vẻ như giới hạn dưới là số tiền chia cho căn bậc hai của 2. Tôi hiểu một cách heuristur tại sao những con số như vậy nên hoạt động, nhưng tôi cũng tò mò tại sao mỗi một hoạt động.
xnor

4

C ++ 11 - 100,993,667

EDIT: Chương trình mới.

Cái cũ sử dụng quá nhiều bộ nhớ. Điều này giảm một nửa mức sử dụng bộ nhớ bằng cách sử dụng một mảng vector khổng lồ thay vì bảng băm. Ngoài ra, nó loại bỏ các chủ đề ngẫu nhiên chủ đề.

   /* by feersum  2014/9
   http://codegolf.stackexchange.com/questions/37627/false-positives-on-an-integer-lattice */
#include <iostream>
#include <cmath>
#include <cstdlib>
#include <cstring>
#include <functional>
#include <vector>
using namespace std;
#define ul unsigned long long

#define K const



#define INS(A)   { bool already = false; \
    for(auto e = res[A.p[0][0]].end(), it = res[A.p[0][0]].begin(); it != e; ++it) \
        if(A.p[0][1] == it->y1 && A.p[1][0] == it->x2 && A.p[1][1] == it->y2) { \
            already = true; \
            break; } \
    if(!already) res[A.p[0][0]].push_back( {A.p[0][1], A.p[1][0], A.p[1][1]} ), ++n; }

#define XMAXMIN (1<<26)

struct ints3 {
    int y1, x2, y2;
};


struct pparm {
    int a,b,c,d;
    int E[4];
    pparm(int A,int B,int C, int D):
        E{B*B+D*D,A*B+C*D,A*A+C*C+2*(B+D),A+C}
    {
        a=A;b=B;c=C;d=D;
    }

};

struct ans {
    int p[2][2];

};
ostream&operator<<(ostream&o, ans&a)
{
    o<<'('<<a.p[0][0]<<','<<a.p[0][1]<<"),("<<a.p[1][0]<<','<<a.p[1][1]<<')'<<endl;
    return o;
}



vector<ints3> res[XMAXMIN];

bool print;
int n;

void gen(K pparm&p1, K pparm&p2)
{
#ifdef DBUG
    for(int i=0;i<2;i++){
    K pparm&p=i?p2:p1;
    cout<<' '<<p.a<<' '<<p.b<<' '<<p.c<<' '<<p.d<<' ';}
    cout<<endl;
#endif

    for(ul x = 0; ; ++x) {
        ans a;
        ul s[2];
        for(int i = 0; i < 2; i++) {
            K pparm &p = i?p2:p1;
            int *pt = a.p[i];
            pt[0] = p.b+x*(p.a+x);
            pt[1] = p.d+x*(p.c+x);
            s[i] = (ul)pt[0]*pt[0] + (ul)pt[1]*pt[1];
        }
        if(*s >> 53)
            break;
if(s[1] - s[0] != 1)
exit(4);

        if(sqrt(s[0]) == sqrt(s[1])) {
             for(int i = 0; i < 2; i++)
                if(a.p[i][0] > a.p[i][1])
                    swap(a.p[i][0], a.p[i][1]);
            if(a.p[0][0] > a.p[0][1])
                for(int i = 0; i < 2; i++)
                    swap(a.p[0][i], a.p[1][i]);
            INS(a)
        }
    }
}



int main(int ac, char**av)
{
    for(int i = 1; i < ac; i++) {
        print |= !strcmp(av[1], "-P");
    }


    #define JMAX 43000000
    for(ul j = 0; j < JMAX; j++) {
        pparm p1(-~j,j,~-j,0),p2(j,1,j,j);
        gen(p1,p2);
        if(!print && !(j%1024))
#ifdef DBUG
            cout<<j<<' ',
#endif
            cout<<n<<endl;

    }
    if(print) 
        for(vector<ints3>& v: res)
            for(ints3& i: v)
                printf("(%d,%d),(%d,%d)\n", &v - res, i.y1, i.x2, i.y2);

    return 0;
}

Chạy với một -Pđối số để in ra các điểm thay vì số đó.

Đối với tôi, nó chỉ mất dưới 2 phút ở chế độ đếm và khoảng 5 phút với việc in được hướng đến một tệp (~ 4 GB), do đó, nó không hoàn toàn bị giới hạn I / O.

Chương trình ban đầu của tôi rất gọn gàng, nhưng tôi đã bỏ phần lớn vì nó chỉ có thể tạo ra theo thứ tự 10 ^ 5 kết quả. Những gì nó đã làm là tìm kiếm các tham số hóa của biểu mẫu (x ^ 2 + Ax + B, x ^ 2 + Cx + D), (x ^ 2 + ax + b, x ^ 2 + cx + d) sao cho x, (x ^ 2 + Ax + B) ^ 2 + (x ^ 2 + Cx + D) ^ 2 = (x ^ 2 + ax + b) ^ 2 + (x ^ 2 + cx + d) ^ 2 + 1. Khi tìm thấy một tập hợp các tham số {a, b, c, d, A, B, C, D}, nó đã tiến hành kiểm tra tất cả các giá trị x dưới mức tối đa. Trong khi nhìn vào đầu ra gỡ lỗi của tôi từ chương trình này, tôi nhận thấy một tham số hóa nhất định về tham số hóa tham số hóa cho phép tôi tạo ra nhiều số một cách dễ dàng. Tôi đã chọn không in ra số của Ell vì tôi có rất nhiều số của riêng mình. Hy vọng rằng bây giờ ai đó sẽ không in ra cả hai bộ số của chúng tôi và tuyên bố là người chiến thắng :)

 /* by feersum  2014/9
   http://codegolf.stackexchange.com/questions/37627/false-positives-on-an-integer-lattice */
    #include <iostream>
    #include <cmath>
    #include <cstdlib>
    #include <cstring>
    #include <functional>
    #include <unordered_set>
    #include <thread>
using namespace std;
#define ul unsigned long long

#define h(S) unordered_##S##set
#define P 2977953206964783763LL
#define K const

#define EQ(T, F)bool operator==(K T&o)K{return!memcmp(F,o.F,sizeof(F));}

struct pparm {
    int a,b,c,d;
    int E[4];
    pparm(int A,int B,int C, int D):
        E{B*B+D*D,A*B+C*D,A*A+C*C+2*(B+D),A+C}
    {
        a=A;b=B;c=C;d=D;
    }
    EQ(pparm,E)
};

struct ans {
    int p[2][2];
    EQ(ans,p)
};
ostream&operator<<(ostream&o, ans&a)
{
    o<<'('<<a.p[0][0]<<','<<a.p[0][1]<<"),("<<a.p[1][0]<<','<<a.p[1][1]<<')'<<endl;
    return o;
}

#define HASH(N,T,F) \
struct N { \
    size_t operator() (K T&p) K { \
        size_t h = 0; \
        for(int i = 4; i--; ) \
            h=h*P+((int*)p.F)[i]; \
        return h; \
    }};

#define INS(r, a) { \
    bool new1 = r.insert(a).second; \
    n += new1; \
    if(print && new1) \
        cout<<a; }

HASH(HA,ans,p)

bool print;
int n;

void gen(h()<ans,HA>&r, K pparm&p1, K pparm&p2)
{
#ifdef DBUG
    for(int i=0;i<2;i++){
    K pparm&p=i?p2:p1;
    cout<<' '<<p.a<<' '<<p.b<<' '<<p.c<<' '<<p.d<<' ';}
    cout<<endl;
#endif

    for(ul x = 0; ; ++x) {
        ans a;
        ul s[2];
        for(int i = 0; i < 2; i++) {
            K pparm &p = i?p2:p1;
            int *pt = a.p[i];
            pt[0] = p.b+x*(p.a+x);
            pt[1] = p.d+x*(p.c+x);
            s[i] = (ul)pt[0]*pt[0] + (ul)pt[1]*pt[1];
        }
        if(*s >> 53)
            break;
if(s[1] - s[0] != 1)
exit(4);

        if(sqrt(s[0]) == sqrt(s[1])) {
             for(int i = 0; i < 2; i++)
                if(a.p[i][0] > a.p[i][1])
                    swap(a.p[i][0], a.p[i][1]);
            INS(r,a)
        }
    }
    //if(!print) cout<<n<<endl;
}

void endit()
{
    this_thread::sleep_for(chrono::seconds(599));
    exit(0);
}

int main(int ac, char**av)
{
    bool kill = false;
    for(int i = 1; i < ac; i++) {
        print |= ac>1 && !stricmp(av[1], "-P");
        kill |= !stricmp(av[i], "-K");
    }

    thread KILLER;
    if(kill)
        KILLER = thread(endit);

    h()<ans, HA> res;
    res.reserve(1<<27);

    #define JMAX 43000000
    for(ul j = 0; j < JMAX; j++) {
        pparm p1(-~j,j,~-j,0),p2(j,1,j,j);
        gen(res,p1,p2);
        if(!print && !(j%1024))
#ifdef DBUG
            cout<<j<<' ',
#endif
            cout<<n<<endl;

    }
    exit(0);
}

Tôi đang nhận được một loạt các lỗi biên dịch: pastebin.com/enNcY9fx Bất kỳ manh mối nào đang xảy ra?
Martin Ender

@Martin Không có ý tưởng ... Tôi đã sao chép bài đăng của mình vào một tệp, được biên dịch trên Máy tính xách tay Windows 8 với các công tắc giống hệt nhau. Hoạt động tốt cho tôi. Bạn có phiên bản nào của gcc?
frageum

Btw nếu chúng gây ra lỗi, bạn chỉ cần xóa tất cả các bit liên quan đến luồng hoàn toàn không cần thiết. Họ chỉ làm điều gì đó nếu bạn sử dụng tùy chọn "-K" không cần thiết.
frageum

g++ (GCC) 4.8.1. Được rồi, tôi đã loại bỏ các bit luồng, nhưng nó vẫn không nhận ra stricmpvì một số lý do.
Martin Ender

1
Tôi đã có quá nhiều thứ khác đang diễn ra vào lúc này, vì vậy tôi sẽ cho bạn biết ý tưởng của tôi để cải thiện cách tiếp cận của bạn. Với bình phương bán kính gần đầu cuối của phạm vi, bạn cũng có thể bị va chạm giữa bình phương bán kính khác nhau bằng 2.
Peter Taylor

1

Quét vòng tròn Java, Bresenham-esque

Về mặt heurist, tôi hy vọng sẽ có được nhiều va chạm hơn bằng cách bắt đầu ở phần cuối của annulus. Tôi dự kiến ​​sẽ có một số cải tiến bằng cách thực hiện một lần quét cho mỗi lần va chạm, ghi lại các giá trị surplusnằm giữa 0r2max - r2bao gồm, nhưng trong thử nghiệm của tôi đã chứng minh chậm hơn phiên bản này. Tương tự cố gắng sử dụng một int[]bộ đệm duy nhất thay vì tạo ra các mảng và danh sách hai phần tử. Tối ưu hóa hiệu suất là một con thú kỳ lạ thực sự.

Chạy với một đối số dòng lệnh cho đầu ra của các cặp và không có số đếm đơn giản.

import java.util.*;

public class CodeGolf37627 {
    public static void main(String[] args) {
        final int M = 144;
        boolean[] possible = new boolean[M];
        for (int i = 0; i <= M/2; i++) {
            for (int j = 0; j <= M/2; j++) {
                possible[(i*i+j*j)%M] = true;
            }
        }

        long count = 0;
        double sqrt = 0;
        long r2max = 0;
        List<int[]> previousPoints = null;
        for (long r2 = 1L << 53; ; r2--) {
            if (!possible[(int)(r2 % M)]) continue;

            double r = Math.sqrt(r2);
            if (r != sqrt) {
                sqrt = r;
                r2max = r2;
                previousPoints = null;
            }
            else {
                if (previousPoints == null) previousPoints = findLatticePointsBresenham(r2max, (int)r);

                if (previousPoints.size() == 0) {
                    r2max = r2;
                    previousPoints = null;
                }
                else {
                    List<int[]> points = findLatticePointsBresenham(r2, (int)r);
                    for (int[] p1 : points) {
                        for (int[] p2 : previousPoints) {
                            if (args.length > 0) System.out.format("(%d, %d) (%d, %d)\n", p1[0], p1[1], p2[0], p2[1]);
                            count++;
                        }
                    }
                    previousPoints.addAll(points);
                    System.out.println(count);
                }
            }
        }
    }

    // Surprisingly, this seems to be faster than doing one scan for all two or three r2s.
    private static List<int[]> findLatticePointsBresenham(long r2, long r) {
        List<int[]> rv = new ArrayList<int[]>();
        // Require 0 = y = x
        long x = r, y = 0, surplus = r2 - r * r;
        while (y <= x) {
            if (surplus == 0) rv.add(new int[]{(int)x, (int)y});

            // Invariant: surplus = r2 - x*x - y*y >= 0
            y++;
            surplus -= 2*y - 1;
            if (surplus < 0) {
                x--;
                surplus += 2*x + 1;
            }
        }

        return rv;
    }
}

1

Java - 27.817.255

Hầu hết trong số này giống như những gì Ell thể hiện , và phần còn lại dựa trên (j,0) (k,l). Đối với mỗi j, tôi đi lại một số hình vuông và kiểm tra xem phần còn lại cho kết quả dương tính giả hay không. Điều này về cơ bản chiếm toàn bộ thời gian chỉ với mức tăng 25k (khoảng 0,1%) so với chỉ (j,0) (j,1), nhưng mức tăng là mức tăng.

Điều này sẽ hoàn thành trong vòng mười phút trên máy của tôi, nhưng tôi không biết bạn có gì. Bởi vì lý do, nếu nó không hoàn thành trước khi hết thời gian, nó sẽ có điểm số nghiêm trọng hơn. Trong trường hợp đó, bạn có thể điều chỉnh ước số trên dòng 8 để nó kết thúc đúng lúc (điều này chỉ đơn giản là xác định khoảng cách mà nó đi được cho mỗi lần j). Đối với một số ước số khác nhau, điểm số là:

11    27817255 (best on OPs machine)
10    27818200
8     27820719
7     27822419 (best on my machine)

Để bật đầu ra cho mỗi trận đấu (và, trời ơi, thật chậm nếu bạn làm thế), chỉ cần bỏ dòng 10 và 19.

public class FalsePositive {
    public static void main(String[] args){
        long j = 67108864;
        long start = System.currentTimeMillis();
        long matches=0;
        while(j < 94906265 && System.currentTimeMillis()-start < 599900){
            long jSq = j*j;
            long limit = (long)Math.sqrt(j)/11; // <- tweak to fit inside 10 minutes for best results
            matches++; // count an automatic one for (j,0)(j,1)
            //System.out.println("("+j+",0) ("+j+",1)");        
            for(int i=1;i<limit;i++){
                long k = j-i;
                long kSq = k*k;
                long l = (long)Math.sqrt(jSq-kSq);
                long lSq = l*l;
                if(kSq+lSq != jSq){
                    if(Math.sqrt(kSq+lSq)==Math.sqrt(jSq)){
                        matches++;
                        //System.out.println("("+j+",0) ("+k+","+l+")");        
                    }
                }
            }
            j++;
        }
        System.out.println("\n"+matches+" Total matches, got to j="+j);
    }
}

Để tham khảo, 20 đầu ra đầu tiên mà nó cung cấp (cho ước số = 7, không bao gồm (j,0)(j,1)các loại) là:

(67110083,0) (67109538,270462)
(67110675,0) (67109990,303218)
(67111251,0) (67110710,269470)
(67111569,0) (67110668,347756)
(67112019,0) (67111274,316222)
(67112787,0) (67111762,370918)
(67115571,0) (67115518,84346)
(67117699,0) (67117698,11586)
(67117971,0) (67117958,41774)
(67120545,0) (67120040,260368)
(67121043,0) (67120118,352382)
(67122345,0) (67122320,57932)
(67122449,0) (67122444,25908)
(67122633,0) (67122328,202348)
(67122729,0) (67121972,318784)
(67122849,0) (67122568,194224)
(67124195,0) (67123818,224970)
(67125201,0) (67125172,62396)
(67125705,0) (67124632,379540)
(67126195,0) (67125882,204990)

0

Julia, 530 dương tính giả

Dưới đây là một tìm kiếm vũ phu rất ngây thơ, mà bạn có thể xem như là một triển khai tham khảo.

num = 0
for i = 60000000:-1:0
    for j = i:-1:ifloor(0.99*i)
        s = i*i + j*j
        for x = ifloor(sqrt(s/2)):ifloor(sqrt(s))
            min_y = ifloor(sqrt(s - x*x))
            max_y = min_y+1
            for y = min_y:max_y
                r = x*x + y*y
                if r != s && sqrt(r) == sqrt(s)
                    num += 1
                    if num % 10 == 0
                        println("Found $num pairs")
                    end
                    #@printf("(i,j) = (%d,%d); (x,y) = (%d,%d); s = %d, r = %d\n", i,j,x,y,s,r)
                end
            end
        end
    end
end

Bạn có thể in ra các cặp (và cường độ bình phương chính xác của chúng) bằng cách bỏ @printfdòng.

Về cơ bản, điều này bắt đầu tìm kiếm tại x = y = 6e7cặp tọa độ đầu tiên và quét xuống khoảng 1% đường đến trục x trước khi giảm x. Sau đó, đối với mỗi cặp tọa độ như vậy, nó sẽ kiểm tra toàn bộ cung có cùng độ lớn (làm tròn lên và xuống) xem có va chạm không.

Mã này giả định rằng nó chạy trên hệ thống 64 bit, do đó các kiểu số nguyên và dấu phẩy động mặc định là loại 64 bit (nếu không, bạn có thể tạo chúng với int64()và các hàm float64()tạo).

Điều đó mang lại một kết quả 530 ít ỏi.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.