False Positives บน Integer Lattice


12

ลีดเดอร์บอร์ด

 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

พื้นหลัง

เมื่อทำงานกับตารางจำนวนเต็มแบบสองมิติในบางครั้งคุณต้องการทราบว่าเวกเตอร์สองตัว (ที่มีส่วนประกอบจำนวนเต็ม) มีขนาดเท่ากันหรือไม่ แน่นอนในเรขาคณิตแบบยุคลิดขนาดของเวกเตอร์(x,y)นั้นได้มาจาก

√(x² + y²)

ดังนั้นการนำไปปฏิบัติที่ไร้เดียงสาอาจคำนวณค่านี้สำหรับทั้งเวกเตอร์และเปรียบเทียบผลลัพธ์ ไม่เพียง แต่จะได้รับการคำนวณรากที่สองที่ไม่จำเป็นเท่านั้น แต่ยังทำให้เกิดปัญหากับความไม่ถูกต้องของจุดลอยตัวซึ่งอาจให้ผลบวกปลอม: เวกเตอร์ที่มีขนาดต่างกัน

สำหรับจุดประสงค์ของการท้าทายนี้เราได้นิยามค่าบวกปลอมเป็นคู่ของพิกัด(a,b)และ(c,d)ซึ่ง:

  • ขนาดกำลังสองของพวกเขาแตกต่างกันเมื่อแสดงเป็นจำนวนเต็ม 64 บิตที่ไม่ได้ลงชื่อ
  • ขนาดของมันจะเหมือนกันเมื่อแสดงเป็นเลขทศนิยม 64 บิตและคำนวณโดยใช้สแควร์รูท 64 บิต (ตามมาตรฐาน IEEE 754 )

ตัวอย่างเช่นการใช้การแทนค่า 16 บิต (แทน 64) เวกเตอร์ที่เล็กที่สุด1คู่ซึ่งให้ผลบวกเป็นเท็จคือ

(25,20) and (32,0)

ขนาดกำลังสองกำลังสอง1025และ1024. รับผลรากที่สอง

32.01562118716424 and 32.0

แต่ใน 16 32.0บิตลอยทั้งสองเหล่านี้ได้รับการตัดออกไป

ในทำนองเดียวกันที่มีขนาดเล็ก2คู่ยอมบวกปลอมสำหรับการแสดงแบบ 32 บิตเป็น

(1659,1220) and (1951,659)

1 "เล็กที่สุด" วัดจากขนาดจุดลอยตัว 16 บิต
2 "เล็กที่สุด" วัดจากขนาดจุดลอยตัวแบบ 32 บิต

สุดท้ายนี่คือกรณี 64 บิตที่ถูกต้อง:

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

* กรณีสุดท้ายเป็นหนึ่งในหลาย ๆ ที่มีขนาดที่เล็กที่สุดที่เป็นไปได้สำหรับผลบวกปลอม 64- บิต

ความท้าทาย

ในโค้ดน้อยกว่า 10,000 ไบต์โดยใช้เธรดเดียวคุณจะพบว่ามีผลบวกปลอมจำนวนมากสำหรับเลขทศนิยม 64 บิต (ไบนารี) ในช่วงพิกัด0 ≤ y ≤ x(นั่นคือเฉพาะภายใน octant แรกของระนาบ Euclidian) ดังกล่าวว่าภายใน10 นาที หากการส่งสองครั้งเสมอกันสำหรับจำนวนคู่เท่ากันตัวแบ่งไทเบรกคือเวลาที่เกิดขึ้นจริงเพื่อค้นหาคู่สุดท้ายของคู่นั้นx² + y² ≤ 253

โปรแกรมของคุณต้องไม่ใช้หน่วยความจำเกิน4 GBในเวลาใดก็ได้ (เพื่อเหตุผลในทางปฏิบัติ)

มันจะต้องเป็นไปได้ที่จะรันโปรแกรมของคุณในสองโหมด: หนึ่งที่ส่งออกทุกคู่ตามที่มันพบและหนึ่งที่เพียงส่งออกจำนวนคู่ที่พบในตอนท้าย อันแรกจะใช้ในการตรวจสอบความถูกต้องของคู่ของคุณ (โดยดูที่ตัวอย่างบางส่วนของการส่งออก) และที่สองจะใช้สำหรับการกำหนดเวลาการส่งของคุณ โปรดทราบว่าการพิมพ์จะต้องแตกต่างเท่านั้น โดยเฉพาะอย่างยิ่งโปรแกรมการนับอาจไม่ hardcode จำนวนคู่ที่สามารถหาได้ มันจะต้องยังคงทำลูปเดียวกันกับที่จะใช้ในการพิมพ์ตัวเลขทั้งหมดและละเว้นการพิมพ์เท่านั้น!

ฉันจะทดสอบการส่งทั้งหมดในแล็ปท็อป Windows 8 ของฉันดังนั้นโปรดถามในความคิดเห็นหากคุณต้องการใช้ภาษาที่ไม่เหมือนกัน

โปรดทราบว่าคู่จะต้องไม่ถูกนับสองครั้งภายใต้การสลับของคู่พิกัดที่หนึ่งและที่สอง

โปรดทราบว่าฉันจะเรียกใช้กระบวนการของคุณผ่านตัวควบคุม Ruby ซึ่งจะฆ่ากระบวนการของคุณหากยังไม่เสร็จหลังจาก 10 นาที ตรวจสอบให้แน่ใจว่าได้ส่งออกจำนวนคู่ที่พบแล้ว คุณสามารถติดตามเวลาด้วยตัวคุณเองและพิมพ์ผลลัพธ์ก่อนเวลาผ่านไป 10 นาทีหรือคุณแค่เอาท์พุทจำนวนคู่ที่พบเป็นระยะ ๆ และฉันจะนำตัวเลขดังกล่าวเป็นคะแนนสุดท้ายของคุณ


ในฐานะที่เป็นความคิดเห็นด้านข้างเป็นไปได้ที่จะตรวจสอบว่าจำนวนเต็มเป็นรูปสี่เหลี่ยมจัตุรัสที่สมบูรณ์พร้อมกันหรือไม่และคำนวณรากที่แม่นยำของมันอย่างมีประสิทธิภาพ อัลกอริทึมต่อไปนี้เร็วกว่าฮาร์ดแวร์สแควร์รูทบนระบบของฉัน 5 เท่า (เปรียบเทียบจำนวนเต็ม 64 บิตที่ไม่ได้ลงชื่อกับ 80 บิตยาวสองเท่า): math.stackexchange.com/questions/41337/…
Todd Lehman

คำตอบ:


5

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นี่เป็นผลโดยตรงของทฤษฎีบทพีทาโกรัส:

ภาพ 1

เรากำลังหาเวกเตอร์vและwว่ามันมีจำนวนเต็ม rซึ่งxและyเป็นจำนวนเต็มด้วย ในฐานะที่เป็นบันทึกด้านข้างโปรดทราบว่าชุดของคู่ที่ไม่ซื่อสัตย์ที่เราใช้ในสองโปรแกรมก่อนหน้านี้เป็นเพียงกรณีพิเศษของเรื่องนี้โดยที่{ v , w }เป็นมาตรฐานพื้นฐานของ2 ; เวลานี้เราต้องการค้นหาวิธีแก้ปัญหาทั่วไปเพิ่มเติม นี่คือที่ซึ่ง Pythagorean triplets (จำนวนเต็ม triplets (a, b, c) เป็นที่พอใจ2 + b 2 = c 2ซึ่งเราใช้ในโปรแกรมก่อนหน้า) ทำการคัมแบ็กของพวกเขา

ปล่อยให้(a, b, c)เป็นทริปเปิลทาทา เวกเตอร์v = (b / c, a / c)และw = (-a / c, b / c) (และ
w = (a / c, -b / c) ) เป็น orthonormal เช่นเดียวกับที่ตรวจสอบได้ง่าย . ตามที่ปรากฎว่าสำหรับตัวเลือกใด ๆ ของ Pythagorean triplet มีจำนวนเต็มrซึ่งxและyเป็นจำนวนเต็ม เพื่อพิสูจน์สิ่งนี้และเพื่อค้นหาrและPอย่างมีประสิทธิภาพเราจำเป็นต้องมีทฤษฎีจำนวน / กลุ่มเล็กน้อย ฉันจะเก็บรายละเอียดไว้ ทั้งสองวิธีสมมติว่าเรามีหนึ่งของเราR , XและY เรายังขาดบางสิ่งอยู่เราต้องการrมีขนาดใหญ่พอและเราต้องการวิธีที่รวดเร็วในการหาคู่ที่คล้ายกันมากขึ้นจากคู่นี้ โชคดีที่มีวิธีง่ายๆในการทำสิ่งนี้ให้สำเร็จ

โปรดทราบว่าการฉายภาพของPลงบนvคือr vดังนั้นr = P · v = (x, y) · (b / c, a / c) = xb / c + ya / cทั้งหมดนี้เพื่อบอกว่าxb + ยา = 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

เป็นเรื่องที่ดีเสมอที่ได้ดูตัวอย่างที่เป็นรูปธรรม ถ้าเราใช้ Pythagorean triplet (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 , และอื่น ๆ , และอื่น ๆ ...

ภาพที่ 2

เมื่อRคือขนาดใหญ่พอเราเริ่มได้รับคู่ที่ไม่สุจริตกับการเพิ่มขึ้นขนาดของ5 นั่นคือประมาณ27,797,402 / 5คู่ที่ไม่น่าไว้วางใจ

ดังนั้นตอนนี้เรามีคู่ที่ไม่น่าไว้วางใจครบบริบูรณ์ เราสามารถจับคู่พวกเขาเข้ากับคู่ที่ซื่อสัตย์ของโปรแกรมแรกเพื่อก่อให้เกิดผลบวกปลอมและด้วยความระมัดระวังเราสามารถใช้คู่ที่ซื่อสัตย์ของโปรแกรมที่สองได้ นี่คือสิ่งที่โปรแกรมนี้ทำ เช่นเดียวกับโปรแกรมก่อนหน้านี้พบว่าผลลัพธ์ส่วนใหญ่เร็วเกินไป --- ได้รับผลบวกปลอม 200,000,000 ภายในไม่กี่วินาที --- จากนั้นช้าลงอย่างมาก

g++ flspos.cpp -oflspos -std=c++11 -msse2 -mfpmath=sse -O3คอมไพล์ด้วย ในการตรวจสอบผลลัพธ์ให้เพิ่ม-DVERIFY(ซึ่งจะช้าลงอย่างเห็นได้ชัด)

flsposทำงานด้วย อาร์กิวเมนต์บรรทัดคำสั่งใด ๆ สำหรับโหมด verbose

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

ดี! :) ฉันได้ 293,619,555 เครื่องของฉันและอัพเดทกระดานผู้นำ
Martin Ender

8

Python 27,797,402

เพียงแค่ตั้งค่าแถบสูงขึ้นเล็กน้อย ...

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

ง่ายต่อการตรวจสอบว่าสำหรับ67,108,864 <= x <= 94,906,265 = ชั้น (sqrt (2 53 ))คู่(x, 0)และ(x, 1)เป็นผลบวกปลอม

ทำไมมันทำงาน : 67108864 = 2 26 therfore ตัวเลขทั้งหมดxในช่วงข้างต้นเป็นของแบบฟอร์ม2 26 + X 'สำหรับบาง0 <= x' <2 26 สำหรับทุกบวกE , (x + E) 2 = x 2 + 2xe + E 2 = x 2 + 2 27 + e 2x'e + E 2 ถ้าเราต้องการมี
(x + e) 2 = x 2 + 1เราต้องการอย่างน้อย2 27 e <= 1นั่นคือe <= 2 -27 อย่างไรก็ตามตั้งแต่ mantissa ของความแม่นยำสองตัวเลขลอยจุดคือ 52 บิตกว้างที่เล็กที่สุดอีเช่นว่าx + E> xคือE = 2 26-52 = 2 -26 ในคำอื่น ๆ จำนวนที่น้อยที่สุดซึ่งแสดงมากขึ้นกว่าxคือx + 2 -26ขณะที่ผลจากการsqrt (x 2 + 1)ที่มากที่สุดx + 2 -27 เนื่องจากโหมดการปัดเศษเริ่มต้น IEEE-754 เป็นแบบปัดเศษไปใกล้ที่สุด เสมอกันมันจะปัดเศษเป็นxเสมอและไม่ถึงx + 2 -26 (ซึ่งไทเบรคจะเกี่ยวข้องเฉพาะกับx = 67,108,864 เท่านั้นถ้าทั้งหมด จำนวนที่มากกว่านี้จะปัดเป็นxโดยไม่คำนึงถึง)


C ++, 75,000,000+

จำได้ว่า3 2 + 4 2 = 5 2 สิ่งนี้หมายความว่าจุด(4, 3)อยู่ในวงกลมรัศมี5โดยมีศูนย์กลางอยู่ที่จุดกำเนิด ที่จริงสำหรับทุกจำนวนเต็มn , (4n, 3n)โกหกบนวงกลมดังกล่าวของรัศมี5n สำหรับขนาดใหญ่พอที่n (คือเช่นว่า5n> = 2 26 ) เรารู้อยู่แล้วว่าเท็จในเชิงบวกสำหรับทุกจุดบนวงกลมนี้: (5n, 1) ที่ดี! นั่นคืออีก27,797,402 / 5คู่ฟรีบวกเท็จที่นั่น! แต่ทำไมหยุดที่นี่ (3, 4, 5)ไม่ใช่เพียงแฝดแฝดดังกล่าว

โปรแกรมนี้รูปลักษณ์สำหรับทุกจำนวนเต็มบวกสาม(A, B, C)เช่นว่า2 + B 2 c = 2และข้อหาเท็จบวกในแบบนี้ มันได้ผลบวกเท็จ70,000,000อย่างรวดเร็ว แต่ก็ช้าลงอย่างมากเมื่อตัวเลขเติบโตขึ้น

g++ flspos.cpp -oflspos -std=c++11 -msse2 -mfpmath=sse -O3คอมไพล์ด้วย ในการตรวจสอบผลลัพธ์ให้เพิ่ม-DVERIFY(ซึ่งจะช้าลงอย่างเห็นได้ชัด)

flsposทำงานด้วย อาร์กิวเมนต์บรรทัดคำสั่งใด ๆ สำหรับโหมด verbose

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

ใช่ว่าเป็นกลยุทธ์ที่มีประสิทธิภาพ ฉันคิดว่า2**53เขตแดนนั้นเลือกที่จะกำจัดสิ่งนี้ แต่ฉันเดาไม่ได้
xnor

ตลกว่าตัวเลขทุกตัวในช่วงนี้ทำงานอย่างไรโดยไม่มีอินสแตนซ์เดียวของรากที่สองของ x ^ 2 และ x ^ 2 + 1 ที่ตกลงบนด้านที่แตกต่างกันของจำนวนเต็ม + 1/2
feersum

@xnor ขอบเขตถูกเลือกเพื่อให้ขนาดกำลังสองเป็นตัวแทนที่แน่นอนใน 64 บิต
Martin Ender

เฮ้มันใช้งานได้ดีใครสนใจอย่างไร ;) คุณหมายถึงว่าโปรแกรมควรนับใน dummy loop หรือตรวจสอบผลลัพธ์จริง ๆ หรือไม่
Ell

@ มาร์ตินบุตต์เนอร์โอ้ฉันเข้าใจแล้ว ดูเหมือนว่าขอบเขตล่างคือจำนวนที่หารด้วยสแควร์รูทของ 2 ฉันเข้าใจว่าทำไมตัวเลขดังกล่าวถึงควรทำงานได้ แต่ฉันก็อยากรู้ว่าทำไมทุก ๆ งาน
xnor

4

C ++ 11 - 100,993,667

แก้ไข: โปรแกรมใหม่

อันเก่าใช้หน่วยความจำมากเกินไป อันนี้ลดการใช้หน่วยความจำลงครึ่งหนึ่งโดยใช้อาร์เรย์เวกเตอร์ขนาดยักษ์แทนตารางแฮช นอกจากนี้ยังลบ cruft เธรดแบบสุ่ม

   /* 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;
}

เรียกใช้ด้วย-Pอาร์กิวเมนต์เพื่อพิมพ์คะแนนแทนตัวเลขดังกล่าว

สำหรับฉันใช้เวลาน้อยกว่า 2 นาทีในโหมดการนับและประมาณ 5 นาทีเมื่อพิมพ์โดยตรงไปยังไฟล์ (~ 4 GB) ดังนั้นมันจึงไม่ได้กลายเป็น I / O จำกัด

โปรแกรมดั้งเดิมของฉันเรียบร้อย แต่ฉันก็ลดลงเพราะมันสามารถผลิตได้ตามลำดับของผลลัพธ์ 10 ^ 5 เท่านั้น สิ่งที่มันทำคือการหา parameterization ของแบบฟอร์ม (x ^ 2 + Axe + B, x ^ 2 + Cx + D), (x ^ 2 + ax + b, x ^ 2 + cx + d) เช่นนั้น x, (x ^ 2 + ขวาน + B) ^ 2 + (x ^ 2 + Cx + D) ^ 2 = (x ^ 2 + ขวาน + b) ^ 2 + (x ^ 2 + cx + d) ^ 2 + 1. เมื่อพบชุดพารามิเตอร์ {a, b, c, d, A, B, C, D} มันจะดำเนินการตรวจสอบค่า x ทั้งหมดภายใต้ค่าสูงสุด ในขณะที่ดูผลลัพธ์การดีบักของฉันจากโปรแกรมนี้ฉันสังเกตเห็นการกำหนดพารามิเตอร์บางอย่างของการกำหนดพารามิเตอร์ของการกำหนดพารามิเตอร์ที่อนุญาตให้ฉันสร้างจำนวนมากได้อย่างง่ายดาย ฉันเลือกที่จะไม่พิมพ์ตัวเลขของ Ell เนื่องจากฉันมีของตัวเองมากมาย หวังว่าตอนนี้บางคนจะไม่พิมพ์ทั้งชุดตัวเลขของเราและอ้างว่าเป็นผู้ชนะ :)

 /* 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);
}

ฉันพบข้อผิดพลาดเกี่ยวกับคอมไพเลอร์: pastebin.com/enNcY9fx มีอะไรเกิดขึ้นบ้าง
Martin Ender

@Martin ไม่ทราบ ... ฉันคัดลอกโพสต์ของฉันลงในไฟล์คอมไพล์บนแล็ปท็อป Windows 8 ที่มีสวิตช์เหมือนกัน ทำงานได้ดีสำหรับฉัน คุณมี gcc รุ่นใด
feersum

Btw หากเกิดข้อผิดพลาดคุณสามารถลบบิตที่เกี่ยวข้องกับเธรดทั้งหมดซึ่งไม่จำเป็นอย่างสมบูรณ์ พวกเขาจะทำอะไรถ้าคุณใช้ตัวเลือก "-K" ซึ่งไม่จำเป็น
feersum

g++ (GCC) 4.8.1. เอาล่ะฉันลบบิตเธรด แต่ก็ยังจำไม่ได้stricmpด้วยเหตุผลบางอย่าง
Martin Ender

1
ฉันมีสิ่งอื่น ๆ อีกมากมายเกิดขึ้นในขณะนี้ดังนั้นฉันจะบอกความคิดของฉันเพื่อปรับปรุงวิธีการของคุณ ด้วยรัศมีใกล้กับปลายบนสุดของช่วงคุณสามารถได้รับการชนกันระหว่างรัศมีรัศมีซึ่งแตกต่างกัน 2
Peter Taylor

1

Java, สแกนวงกลม Bresenham-esque

การคาดหวังแบบ Heuristically ฉันจะได้รับความขัดแย้งมากขึ้นโดยเริ่มจากปลายวงกว้าง ฉันคาดว่าจะได้รับการปรับปรุงโดยทำการสแกนหนึ่งครั้งสำหรับแต่ละการชนค่าการบันทึกที่surplusอยู่ระหว่าง0และr2max - r2รวม แต่ในการทดสอบของฉันที่พิสูจน์ได้ช้ากว่าเวอร์ชั่นนี้ ในทำนองเดียวกันความพยายามในการใช้int[]บัฟเฟอร์เดียวมากกว่าการสร้างอาร์เรย์สองรายการและรายการจำนวนมาก การเพิ่มประสิทธิภาพการปฏิบัติงานเป็นสัตว์ประหลาดที่แน่นอน

รันด้วยอาร์กิวเมนต์บรรทัดคำสั่งสำหรับเอาต์พุตของคู่และไม่มีการนับอย่างง่าย

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

เหล่านี้ส่วนใหญ่เป็นเช่นเดียวกับสิ่งที่แสดงให้เห็น Ell(j,0) (k,l)และส่วนที่เหลือจะขึ้นอยู่กับ สำหรับแต่ละjฉันเดินกลับสี่เหลี่ยมและตรวจสอบว่าส่วนที่เหลือให้บวกเป็นเท็จ โดยทั่วไปแล้วจะใช้เวลาทั้งหมดเพียง 25K (ประมาณ 0.1%) เท่านั้น(j,0) (j,1)แต่กำไรเป็นกำไร

นี่จะเสร็จสิ้นภายในสิบนาทีบนเครื่องของฉัน แต่ฉันไม่รู้ว่าคุณมีอะไร เพราะเหตุผลถ้ามันไม่เสร็จก่อนหมดเวลาก็จะมีคะแนนแย่ลงอย่างมาก ในกรณีที่คุณสามารถปรับแต่งหารในสาย 8 เพื่อที่จะเสร็จสิ้นในเวลา (นี้ก็จะกำหนดวิธีการที่จะกลับมามันเดินสำหรับแต่ละj) สำหรับตัวหารที่หลากหลายคะแนนคือ:

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

หากต้องการเปิดเอาต์พุตสำหรับแต่ละการแข่งขัน (และพระเจ้ามันช้าถ้าคุณทำ) เพียงแค่ไม่ใส่เครื่องหมายบรรทัดที่ 10 และ 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);
    }
}

สำหรับการอ้างอิงเอาต์พุต 20 รายการแรกที่ให้ (สำหรับตัวหาร = 7 ยกเว้น(j,0)(j,1)ประเภท) คือ:

(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 ผลบวกปลอม

นี่คือการค้นหาแรงเดรัจฉานไร้เดียงสามากซึ่งคุณสามารถดูเป็นการดำเนินการอ้างอิง

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

คุณสามารถพิมพ์คู่ (และขนาดกำลังสองที่แน่นอน) โดยไม่ใส่เครื่องหมายใน@printfบรรทัด

โดยพื้นฐานแล้วสิ่งนี้จะเริ่มค้นหาที่x = y = 6e7คู่พิกัดแรกและสแกนลงไปประมาณ 1% ของเส้นทางไปยังแกน x ก่อนที่จะลดค่า x จากนั้นสำหรับคู่ประสานงานแต่ละคู่นั้นจะตรวจสอบส่วนโค้งทั้งหมดที่มีขนาดเท่ากัน (ปัดเศษขึ้นและลง) เพื่อหาการชนกัน

รหัสจะถือว่ามันทำงานบนระบบ 64 บิตเพื่อให้จำนวนเต็มเริ่มต้นและประเภททศนิยมเป็น 64- บิต (ถ้าไม่คุณสามารถสร้างพวกเขาด้วยint64()และfloat64()ก่อสร้าง)

ที่ให้ผลลัพธ์ 530 น้อย

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.