Tamsayı Kafesinde Yanlış Pozitifler


12

Liderler Sıralaması

 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

Arka fon

İki boyutlu bir tamsayı koordinatları ızgarası üzerinde çalışırken, bazen iki vektörün (tamsayı bileşenleri ile) aynı büyüklüğe sahip olup olmadığını bilmek isteyebilirsiniz. Tabii ki, Öklid geometrisinde bir vektörün büyüklüğü (x,y)ile verilmektedir

√(x² + y²)

Böylece naif bir uygulama bu değeri her iki vektör için de hesaplayabilir ve sonuçları karşılaştırabilir. Bu sadece gereksiz bir karekök hesaplaması yapmakla kalmaz, aynı zamanda yanlış pozitifler verebilecek kayan nokta yanlışlıklarıyla ilgili sorunlara da neden olur: büyüklükleri farklı olan, ancak kayan nokta gösterimindeki önemli basamakların hepsi aynı olan vektörler.

Bu zorluğun amaçları için, yanlış bir pozitif bir çift koordinat çifti olarak tanımlarız (a,b)ve (c,d)bunun için:

  • 64 bit işaretsiz tamsayı olarak temsil edildiklerinde kare büyüklükleri farklıdır.
  • Büyüklükleri 64-bit ikili kayan nokta sayısı olarak temsil edildiğinde ve 64-bit karekökü ( IEEE 754 uyarınca ) ile hesaplandığında aynıdır .

Örnek olarak, 16 bitlik gösterimler (64 yerine) kullanılarak, yanlış pozitif veren en küçük 1 çift ​​vektör

(25,20) and (32,0)

Onların kare kare büyüklükleri 1025ve 1024. Karekök verimini almak

32.01562118716424 and 32.0

Ancak 16 bitlik kayan noktalarda her ikisi de kısaltılır 32.0.

Benzer şekilde, 32 bit gösterim için yanlış pozitif veren en küçük 2 çift

(1659,1220) and (1951,659)

16 bit kayan nokta büyüklüğüyle ölçüldüğü şekliyle 1 "En Küçük". 32 bit kayan nokta büyüklüğüyle ölçülen
2 "En Küçük".

Son olarak, şu anda geçerli 64 bitlik birkaç durum vardır:

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

* Son durum, 64 bit yanlış pozitifler için mümkün olan en küçük büyüklüğe sahip birkaç örnektir.

Meydan okuma

Tek bir iş parçacığı kullanarak 10.000 bayttan az kodda, koordinat aralığındaki 64 bit (ikili) kayan nokta sayıları için 0 ≤ y ≤ x(yani, yalnızca Öklid düzleminin ilk oktantı içinde ) kadar çok sayıda yanlış pozitif bulabilirsiniz. öyle ki 10 dakika içinde . İki başvuru aynı sayıda çift için bağlanırsa, bağ kırıcı bu çiftlerin sonuncusunu bulmak için geçen gerçek zamandır.x² + y² ≤ 253

Programınız hiçbir zaman 4 GB'den fazla bellek kullanmamalıdır (pratik nedenlerle).

Programınızı iki modda çalıştırmak mümkün olmalıdır: biri her çifti bulduğu gibi çıkarır ve diğeri yalnızca bulunan çiftlerin sayısını çıkarır. Birincisi, çiftlerinizin geçerliliğini doğrulamak için kullanılacaktır (bazı çıktı örneklerine bakarak) ve ikincisi, başvurunuzu gerçekten zamanlamak için kullanılır. Tek farkın baskının olması gerektiğini unutmayın . Özellikle, sayma programı çiftlerinin sayısı hardcode olmayabilir olabilir bulabilirsiniz. Yine de tüm sayıları yazdırmak için kullanılacak olan aynı döngüyü gerçekleştirmeli ve yalnızca baskıyı atlamalıdır!

Windows 8 dizüstü bilgisayarımdaki tüm gönderimleri test edeceğim, bu yüzden lütfen çok yaygın olmayan bir dil kullanmak istiyorsanız yorumlarda sorun.

Çiftleri gerektiğine dikkat olup , birinci ve ikinci koordinat çiftinin geçiş altında iki kez sayılır.

Ayrıca, işleminizi 10 dakika sonra bitirmediyse, işlemi bir Ruby denetleyicisi aracılığıyla çalıştıracağımı unutmayın. O zamana kadar bulunan çift sayısını çıkardığınızdan emin olun. Ya kendiniz zamanınızı takip edebilir ve 10 dakika geçmeden hemen önce sonucu yazdırabilir ya da ara sıra bulunan çiftlerin sayısını çıkartabilirsiniz ve puanınız gibi son sayıyı alacağım.


Bir yan yorum olarak, bir tamsayının mükemmel bir kare olup olmadığını aynı anda belirlemek ve aynı zamanda kesin kare kökünü verimli bir şekilde hesaplamak mümkündür. Aşağıdaki algoritma sistemimdeki donanım karekökünden 5 kat daha hızlıdır (64 bit işaretsiz tam sayıları 80 bit uzunluğunda iki katına kıyasla
Todd Lehman

Yanıtlar:


5

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:

Resim 1

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.

Resim 2

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

Güzel! :) Makinemde 293.619.555 aldım ve skor tablosunu güncelledim.
Martin Ender

8

Python, 27.797.402

Sadece çıtayı biraz daha yükseğe koymak için ...

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 = kat (sqrt (2 53 )) için çiftlerin (x, 0) ve (x, 1) yanlış pozitif olduğunu doğrulamak kolaydır .

Neden çalışır : 67.108.864 = 2 26 . Bu nedenle, yukarıdaki aralıktaki tüm x sayıları, 0 <= x '<2 26 için 2 26 + x' biçimindedir . Tüm pozitif e için , (x + e) 2 = x 2 + 2xe + e 2 = x 2 + 2 27 e + 2x'e + e 2 . (X + e) 2 = x 2 + 1'e sahip olmak istiyorsak en az 2 27 e <= 1 , yani e <= 2 -27'ye ihtiyacımız var
Bununla birlikte, çift kesinlikli kayar sayıların mantis 52-bit genişliğinde olduğu, en küçük E şekilde , x + E> X olduğu , e = 2 26-52 = 2 -26 . Diğer bir deyişle, daha küçük Temsil sayısı daha x olduğu x + 2 -26 sonucu ise sqrt (x 2 + 1) en az bir x + 2 -27 . Varsayılan IEEE-754 yuvarlama modu yuvarlaktan en yakın olduğu için; eşit bağlar, her zaman x'e yuvarlanır ve asla x + 2 -26'ya (kravat kopması sadece x = 67,108,864 için geçerlidir), hiç değilse. Daha büyük olan herhangi bir sayı x'e yuvarlanır ).


C ++, 75.000.000+

3 2 + 4 2 = 5 2 olduğunu hatırlayın . Bunun anlamı, noktanın (4, 3) orijin etrafında ortalanmış yarıçap 5 dairesi üzerinde olmasıdır . Aslında, n tamsayıları için , (4n, 3n) 5n yarıçapında bir daire üzerinde yer alır . Yeterince büyük n için (yani, 5n> = 2 26 ), bu çemberdeki tüm noktalar için zaten yanlış pozitif biliyoruz: (5n, 1) . Harika! Bu, başka bir 27.797.402 / 5 ücretsiz yanlış pozitif çift var! Ama neden burada duruyorsun? (3, 4, 5) böyle bir üçlü değildir.

Bu program tüm pozitif tamsayı üçüzlerini (a, b, c) arar , böylece 2 + b 2 = c 2 olur ve bu şekilde yanlış pozitifleri sayar. Bu alır 70000000 oldukça hızlı yanlış pozitif ama sonra sayıları artmaya ö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>
#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, bu etkili bir strateji. 2**53Sınırın bunu dışlamak için seçildiğini düşünmüştüm , ama sanmıyorum.
xnor

Bu aralıktaki her sayının x ^ 2 ve x ^ 2 + 1'in kare köklerinin tek bir örneği olmadan nasıl çalıştığı tuhaftır.
feersum

@xnor Sınır, kareli büyüklüğün 64 bit şamandıralarda tam olarak temsil edilebilmesi için seçilmiştir.
Martin Ender

Hey, işe yarıyor, kimin umurunda? ;) Programın sahte bir döngüde sayılması veya sonuçları gerçekten doğrulaması gerektiği anlamına mı geliyor?
Ell

@MartinButtner Oh, anlıyorum. Alt sınır, bu miktarın 2'nin kareköküne bölünmesi gibi görünüyor. Sezgisel olarak bu sayıların neden çalışması gerektiğini anlıyorum, ancak her birinin neden çalıştığını merak ediyorum.
xnor

4

C ++ 11-100.993.667

EDIT: Yeni program.

Eskisi çok fazla bellek kullandı. Bu, karma tablo yerine dev bir vektör dizisi kullanarak bellek kullanımını yarıya indirir. Ayrıca rastgele iplik sargısını kaldırır.

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

Sayıları -Pyerine noktaları yazdırmak için bir argümanla çalıştırın .

Benim için sayım modunda 2 dakikadan az ve bir dosyaya (~ 4 GB) baskı ile yaklaşık 5 dakika sürüyor, bu yüzden I / O sınırlı hale gelmedi.

Orijinal programım düzgün, ama sadece 10 ^ 5 sonuçlarında üretebildiğinden çoğunu düşürdüm. Yaptığı şey, formun (x ^ 2 + Ax + B, x ^ 2 + Cx + D), (x ^ 2 + ax + b, x ^ 2 + cx + d) parametrelerini aramaktır. x, (x ^ 2 + Ax + B) ^ 2 + (x ^ 2 + Cx + D) ^ 2 = (x ^ 2 + ax + b) ^ 2 + (x ^ 2 + cx + d) ^ 2 + 1. Böyle bir parametre kümesi bulduğunda {a, b, c, d, A, B, C, D} maksimum değerlerin altındaki tüm x değerlerini kontrol etmeye devam etti. Bu programdaki hata ayıklama çıktıma bakarken, kolayca çok sayıda sayı üretmeme izin veren parametrelendirmenin parametrelendirilmesinde belirli bir parametreleştirme olduğunu fark ettim. Ben kendimden çok fazla olduğu için Ell'in numaralarını basmamayı seçtim. Umarım şimdi birisi her iki sayı grubumuzu yazdırmaz ve kazanan olduğunu iddia etmez :)

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

Bir sürü derleyici hatası alıyorum: pastebin.com/enNcY9fx Neler oluyor?
Martin Ender

@Martin Hiçbir fikrim yok ... Yazımı aynı anahtarlarla Windows 8 Dizüstü bilgisayarda derlenmiş bir dosyaya kopyaladım. Benim için iyi çalışıyor. Hangi gcc sürümüne sahipsiniz?
feersum

Btw hatalara neden olurlarsa, tamamen gereksiz olan iş parçacığıyla ilgili tüm bitleri silebilirsiniz. Sadece gerekli olmayan bir "-K" seçeneği kullanırsanız bir şey yaparlar.
feersum

g++ (GCC) 4.8.1. Tamam, iş parçacığı bitlerini kaldırdım, ancak yine de stricmpbir nedenden dolayı tanınmıyor .
Martin Ender

1
Şu anda çok fazla başka şeyim var, bu yüzden yaklaşımınızı geliştirme fikrimi size söyleyeceğim. Aralığın üst ucuna yakın yarıçaplı kareyle, yarıçap karesi arasında 2 ile farklılık gösteren çarpışmalar da elde edebilirsiniz.
Peter Taylor

1

Java, Bresenham-esque daire taraması

Sezgisel olarak, halkanın daha geniş ucundan başlayarak daha fazla çarpışma bekliyorum. Her çarpışma için bir tarama yaparak, değerleri surplusarasında 0ve r2max - r2kapsayıcı olan değerleri kaydederek biraz iyileşme bekledim , ancak testimde bu sürümden daha yavaş olduğu kanıtlandı. Benzer şekilde int[], iki elemanlı diziler ve listeler oluşturmaktan çok tek bir tampon kullanmaya çalışır . Performans optimizasyonu gerçekten de garip bir canavar.

Çiftlerin çıktısı için komut satırı bağımsız değişkeniyle ve basit sayımlar olmadan çalıştırı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

Bunların çoğu Ell'in gösterdiği ile aynıdır ve geri kalanı da dayanmaktadır (j,0) (k,l). Her biri için jbazı karelere geri dönüp kalanların yanlış pozitif olup olmadığını kontrol ediyorum. Bu, sadece 25k (yaklaşık% 0.1) kazançla temelde tüm zamanı alır (j,0) (j,1), ancak kazanç bir kazançtır.

Bu, makinemde on dakikadan kısa bir sürede bitecek, ancak neye sahip olduğunuzu bilmiyorum. O takdirde nedenlerden dolayı, gelmez zaman dolmadan bitirmek, bir ölçüde daha kötü puanına sahip olacaktır. Bu durumda, böleni 8 numaralı hat üzerinde ince ayar yapabilirsiniz, böylece zamanla biter (bu her biri için ne kadar geriye yürüdüğünü belirler j). Bazı çeşitli bölenler için puanlar:

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

Her maç için çıktıyı açmak için (ve tanrı, eğer yaparsanız yavaştır), sadece 10 ve 19. satırları açın.

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

Referans olarak, verdiği ilk 20 çıkış (bölen = 7 için, (j,0)(j,1)tipler hariç ):

(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 yanlış pozitif

İşte size bir referans uygulaması olarak görebileceğiniz çok naif bir kaba kuvvet araştırması.

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Çizgiyi kaldırarak çiftleri (ve tam kare büyüklüklerini) yazdırabilirsiniz .

Temel olarak bu x = y = 6e7, ilk koordinat çifti için aramayı başlatır ve x değerini azaltmadan önce x eksenine giden yolun yaklaşık% 1'ini tarar. Daha sonra bu tür her bir koordinat çifti için, aynı büyüklükteki tüm yayı (yukarı ve aşağı yuvarlama) bir çarpışma için kontrol eder.

Kod, 64-bit bir sistemde çalıştığını varsayar, böylece varsayılan tamsayı ve kayan nokta türleri 64-bit olanlardır (eğer değilse, bunları int64()ve float64()yapıcıları ile oluşturabilirsiniz ).

Bu yetersiz 530 sonuç verir.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.