Falsi positivi su un reticolo intero


12

Classifica

 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

sfondo

Quando si lavora su una griglia 2D di coordinate intere, a volte si desidera sapere se due vettori (con componenti interi) hanno la stessa grandezza. Naturalmente, nella geometria euclidea, la grandezza di un vettore (x,y)è data da

√(x² + y²)

Quindi un'implementazione ingenua potrebbe calcolare questo valore per entrambi i vettori e confrontare i risultati. Ciò comporta non solo un inutile calcolo della radice quadrata, ma causa anche problemi con imprecisioni in virgola mobile, che potrebbero produrre falsi positivi: vettori le cui magnitudini sono diverse, ma dove le cifre significative nella rappresentazione in virgola mobile sono tutte identiche.

Ai fini di questa sfida, definiamo un falso positivo come una coppia di coppie di coordinate (a,b)e (c,d)per le quali:

  • La loro grandezza quadrata è diversa se rappresentata come numeri interi senza segno a 64 bit.
  • La loro grandezza è identica se rappresentata come numero binario a virgola mobile a 64 bit e calcolata tramite una radice quadrata a 64 bit (secondo IEEE 754 ).

Ad esempio, usando le rappresentazioni a 16 bit (anziché 64), la più piccola 1 coppia di vettori che produce un falso positivo è

(25,20) and (32,0)

Le loro magnitudini quadrate quadrate sono 1025e 1024. Prendendo i raccolti della radice quadrata

32.01562118716424 and 32.0

Ma nei float a 16 bit entrambi vengono troncati 32.0.

Allo stesso modo, è la 2 coppia più piccola che produce un falso positivo per le rappresentazioni a 32 bit

(1659,1220) and (1951,659)

1 "Più piccolo" misurato in base alla magnitudo in virgola mobile a 16 bit.
2 "Più piccolo" misurato in base alla magnitudo in virgola mobile a 32 bit.

Infine, ecco una manciata di casi validi a 64 bit:

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

* L'ultimo caso è uno dei tanti con la minima entità possibile per i falsi positivi a 64 bit.

La sfida

In meno di 10.000 byte di codice, usando un singolo thread, troverai tanti falsi positivi per i numeri in virgola mobile a 64 bit (binari) nell'intervallo di coordinate 0 ≤ y ≤ x(ovvero solo nel primo ottante del piano euclideo) tale che entro 10 minuti . Se due invii vincolano per lo stesso numero di coppie, il pareggio è il tempo effettivo impiegato per trovare l'ultima di quelle coppie.x² + y² ≤ 253

Il tuo programma non deve utilizzare più di 4 GB di memoria in qualsiasi momento (per motivi pratici).

Deve essere possibile eseguire il programma in due modalità: una che emette ogni coppia come la trova e una che emette solo il numero di coppie trovate alla fine. Il primo verrà utilizzato per verificare la validità delle coppie (osservando alcuni esempi di output) e il secondo verrà utilizzato per sincronizzare effettivamente l'invio. Si noti che la stampa deve essere l' unica differenza. In particolare, il programma di conteggio potrebbe non codificare il numero di coppie che è riuscito a trovare. Deve comunque eseguire lo stesso loop che verrebbe utilizzato per stampare tutti i numeri e omettere solo la stampa stessa!

Testerò tutti gli invii sul mio laptop Windows 8, quindi per favore chiedi nei commenti se vuoi usare un linguaggio non troppo comune.

Si noti che le coppie non devono essere conteggiate due volte durante la commutazione della prima e della seconda coppia di coordinate.

Si noti inoltre che eseguirò il processo tramite un controller Ruby, che interromperà il processo se non è terminato dopo 10 minuti. Assicurati di produrre il numero di coppie trovate da allora. Puoi tenere traccia del tempo da solo e stampare il risultato appena prima che siano trascorsi i 10 minuti, oppure puoi semplicemente emettere il numero di coppie trovate sporadicamente e io prenderò l'ultimo numero come il tuo punteggio.


Come commento laterale, è possibile determinare simultaneamente se un numero intero è un quadrato perfetto e anche calcolare in modo efficiente la sua radice quadrata precisa. Il seguente algoritmo è 5 volte più veloce dell'hardware radice quadrata sul mio sistema (confrontando numeri interi senza segno a 64 bit con doppio lungo a 80 bit): math.stackexchange.com/questions/41337/…
Todd Lehman,

Risposte:


5

C ++, 275.000.000+

Faremo riferimento a coppie la cui magnitudine è rappresentabile con precisione, come (x, 0) , come coppie oneste e a tutte le altre coppie come coppie disoneste di magnitudo m , dove m è la magnitudine erroneamente segnalata della coppia. Il primo programma nel post precedente utilizzava un insieme di coppie strettamente legate di coppie oneste e disoneste:
(x, 0) e (x, 1) , rispettivamente, per x abbastanza grande. Il secondo programma utilizzava lo stesso insieme di coppie disoneste, ma estendeva l'insieme di coppie oneste cercando tutte le coppie oneste di grandezza integrale. Il programma non termina entro dieci minuti, ma trova la maggior parte dei suoi risultati molto presto, il che significa che la maggior parte del tempo di esecuzione va sprecato. Invece di continuare a cercare coppie oneste sempre meno frequenti, questo programma usa il tempo libero per fare la prossima cosa logica: estendere l'insieme di coppie disoneste .

Dal post precedente sappiamo che per tutti gli interi abbastanza grandi r , sqrt (r 2 + 1) = r , dove sqrt è la funzione radice quadrata a virgola mobile. Il nostro piano di attacco è trovare coppie P = (x, y) tali che x 2 + y 2 = r 2 + 1 per un numero intero abbastanza grande r . È abbastanza semplice da fare, ma cercare ingenuamente coppie individuali di questo tipo è troppo lento per essere interessante. Vogliamo trovare queste coppie alla rinfusa, proprio come abbiamo fatto per le coppie oneste nel programma precedente.

Sia { v , w } una coppia di vettori ortogonali. Per tutti gli scalari reali r , || r v + w || 2 = r 2 + 1 . In 2 , questo è un risultato diretto del teorema di Pitagora:

Immagine 1

Siamo alla ricerca di vettori V e w tale che esiste un intero r per il quale x ed y sono anche interi. Come nota a margine, nota che l'insieme di coppie disoneste che abbiamo usato nei due programmi precedenti era semplicemente un caso speciale di questo, in cui { v , w } era la base standard di 2 ; questa volta desideriamo trovare una soluzione più generale. Questo è dove terzine pitagoriche (terzine di numeri interi (a, b, c) che soddisfano a 2 + b 2 = c 2, che abbiamo usato nel programma precedente) ritornano.

Sia (a, b, c) una tripletta pitagorica. I vettori v = (b / c, a / c) e w = (-a / c, b / c) (e anche
w = (a / c, -b / c) ) sono ortonormali, come è facile da verificare . Come si è visto, per ogni scelta di Pitagora tripletto, esiste un intero r tale che x ed y sono numeri interi. Per dimostrarlo e per trovare efficacemente r e P , abbiamo bisogno di una piccola teoria dei numeri / gruppi; Ho intenzione di risparmiare i dettagli. In entrambi i casi, supponiamo di avere le nostre integrante r , x e y . Mancano ancora alcune cose: abbiamo bisogno di ressere abbastanza grandi e vogliamo un metodo veloce per derivare molte più coppie simili da questo. Fortunatamente, c'è un modo semplice per ottenere questo risultato.

Nota che la proiezione di P su v è r v , quindi r = P · v = (x, y) · (b / c, a / c) = xb / c + ya / c , tutto questo per dire che xb + ya = rc . Di conseguenza, per tutti i numeri interi 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. In altre parole, la grandezza quadrata delle coppie della forma
(x + bn, y + an) è (r + cn) 2 + 1 , che è esattamente il tipo di coppie che stiamo cercando! Per n abbastanza grandi , si tratta di coppie disoneste di magnitudine r + cn .

È sempre bello guardare un esempio concreto. Se prendiamo la tripletta di Pitagora (3, 4, 5) , allora a r = 2 abbiamo P = (1, 2) (puoi controllare che (1, 2) · (4/5, 3/5) = 2 e, chiaramente, 1 2 + 2 2 = 2 2 + 1 ). Aggiungere 5 a r e (4, 3) a P ci porta a r '= 2 + 5 = 7 e P' = (1 + 4, 2 + 3) = (5, 5) . Ecco ed ecco, 5 2 + 5 2 = 7 2 + 1. Le coordinate successive sono r '' = 12 e P '' = (9, 8) , e ancora, 9 2 + 8 2 = 12 2 + 1 , e così via, e così via ...

Immagine 2

Una volta che r è abbastanza grande, iniziamo a ottenere coppie disoneste con incrementi di magnitudo di 5 . Sono circa 27.797.402 / 5 coppie disoneste.

Quindi ora abbiamo molte coppie disoneste di magnitudo integrale. Possiamo facilmente accoppiarli con le coppie oneste del primo programma per formare falsi positivi e con la dovuta cura possiamo anche usare le coppie oneste del secondo programma. Questo è fondamentalmente ciò che fa questo programma. Come il programma precedente, trova anche la maggior parte dei suoi risultati molto presto --- arriva a 200.000.000 di falsi positivi in ​​pochi secondi --- e quindi rallenta considerevolmente.

Compila con g++ flspos.cpp -oflspos -std=c++11 -msse2 -mfpmath=sse -O3. Per verificare i risultati, aggiungi -DVERIFY(questo sarà notevolmente più lento.)

Corri con flspos. Qualsiasi argomento della riga di comando per la modalità dettagliata.

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

Bello! :) Ho ottenuto 293.619.555 sulla mia macchina e aggiornato la classifica.
Martin Ender,

8

Python, 27.797.402

Solo per impostare la barra un po 'più in alto ...

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

È facile verificare che per tutte 67.108.864 <= x <= 94.906.265 = floor (sqrt (2 53 )) le coppie (x, 0) e (x, 1) sono falsi positivi.

Perché funziona : 67.108.864 = 2 26 . Pertanto, tutti i numeri x nell'intervallo sopra riportato sono nella forma 2 26 + x ' per alcuni 0 <= x' <2 26 . Per tutti i positivi e , (x + e) 2 = x 2 + 2xe + e 2 = x 2 + 2 27 e + 2x'e + e 2 . Se vogliamo avere
(x + e) 2 = x 2 + 1 abbiamo bisogno di almeno 2 27 e <= 1 , cioè, e <= 2 -27 Tuttavia, poiché la mantissa dei numeri in virgola mobile a precisione doppia è larga 52 bit, la più piccola e tale che x + e> x sia e = 2 26 - 52 = 2 -26 . In altre parole, il numero più piccolo rappresentabile maggiore di x è x + 2 -26 mentre il risultato di sqrt (x 2 + 1) è al massimo x + 2 -27 . Poiché la modalità di arrotondamento IEEE-754 predefinita è arrotondata al più vicino; pareggio, sarà sempre arrotondato a x e mai a x + 2 -26 (dove il tie-break è realmente rilevante solo per x = 67.108.864, se non del tutto. Qualsiasi numero più grande arrotonderà a x indipendentemente).


C ++, 75.000.000+

Ricorda che 3 2 + 4 2 = 5 2 . Ciò significa che il punto (4, 3) si trova sul cerchio del raggio 5 centrato attorno all'origine. In realtà, per tutti gli interi n , (4n, 3n) si trova su tale cerchio di raggio 5n . Per n abbastanza grande (ovvero 5n> = 2 26 ), conosciamo già un falso positivo per tutti i punti di questo cerchio: (5n, 1) . Grande! Ecco altre 27.797.402 / 5 coppie di falsi positivi libere proprio lì! Ma perché fermarsi qui? (3, 4, 5) non è l'unica tripletta del genere.

Questo programma cerca tutte le terzine intere positive (a, b, c) in modo tale che a 2 + b 2 = c 2 , e conti i falsi positivi in ​​questo modo. Si arriva a 70.000.000 falsi positivi piuttosto veloce, ma poi rallenta notevolmente i numeri crescere.

Compila con g++ flspos.cpp -oflspos -std=c++11 -msse2 -mfpmath=sse -O3. Per verificare i risultati, aggiungi -DVERIFY(questo sarà notevolmente più lento.)

Corri con flspos. Qualsiasi argomento della riga di comando per la modalità dettagliata.

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

Sì, questa è una strategia efficace. Avevo pensato che il 2**53confine fosse stato scelto per escluderlo, ma credo di no.
xnor

Divertente come ogni numero in questo intervallo funziona senza una singola istanza delle radici quadrate di x ^ 2 e x ^ 2 + 1 che ricadono su lati diversi di un numero intero + 1/2.
feersum,

@xnor Il limite è stato scelto affinché la grandezza quadrata fosse esattamente rappresentabile nei float a 64 bit.
Martin Ender,

Ehi, funziona, a chi importa come? ;) Vuoi dire che il programma dovrebbe contare in un ciclo fittizio o verificare effettivamente i risultati?
Ell,

@MartinButtner Oh, capisco. Sembra che il limite inferiore sia quell'importo diviso per la radice quadrata di 2. Capisco euristicamente il motivo per cui tali numeri dovrebbero funzionare, ma sono anche curioso del motivo per cui ognuno funziona.
xnor,

4

C ++ 11 - 100.993.667

EDIT: nuovo programma.

Il vecchio usava troppa memoria. Questo dimezza l'utilizzo della memoria usando una matrice vettoriale gigante invece di una tabella hash. Inoltre rimuove l'innesto di thread casuale.

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

Esegui con un -Pargomento per stampare i punti anziché il loro numero.

Per me ci vogliono meno di 2 minuti nella modalità di conteggio e circa 5 minuti con la stampa diretta su un file (~ 4 GB), quindi non è diventato del tutto limitato dall'I / O.

Il mio programma originale era pulito, ma ne ho lasciato perdere gran parte poiché poteva produrre solo nell'ordine di 10 ^ 5 risultati. Ciò che ha fatto è cercare le parametrizzazioni del modulo (x ^ 2 + Ax + B, x ^ 2 + Cx + D), (x ^ 2 + ax + b, x ^ 2 + cx + d) tale che per qualsiasi x, (x ^ 2 + Ax + B) ^ 2 + (x ^ 2 + Cx + D) ^ 2 = (x ^ 2 + ax + b) ^ 2 + (x ^ 2 + cx + d) ^ 2 + 1. Quando ha trovato un tale insieme di parametri {a, b, c, d, A, B, C, D} ha proceduto a verificare tutti i valori x al di sotto del massimo. Mentre guardavo il mio output di debug da questo programma, ho notato una certa parametrizzazione della parametrizzazione della parametrizzazione che mi ha permesso di produrre facilmente molti numeri. Ho scelto di non stampare i numeri di Ell poiché ne avevo molti miei. Spero che ora qualcuno non stampi entrambi i nostri set di numeri e affermi di essere il vincitore :)

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

Ricevo un sacco di errori del compilatore: pastebin.com/enNcY9fx Qualche idea di cosa stia succedendo?
Martin Ender,

@Martin Nessuna idea ... Ho copiato il mio post in un file, compilato su un laptop Windows 8 con switch identici. Funziona bene per me. Quale versione di gcc hai?
feersum,

A proposito se causano errori, puoi semplicemente eliminare tutti i bit relativi al thread che sono completamente inutili. Fanno qualcosa solo se usi un'opzione "-K" che non è necessaria.
feersum,

g++ (GCC) 4.8.1. Ok, ho rimosso i bit del thread, ma non è ancora riconoscibile stricmpper qualche motivo.
Martin Ender,

1
Ho troppe altre cose in corso al momento, quindi ti dirò la mia idea per migliorare il tuo approccio. Con il raggio al quadrato vicino all'estremità superiore dell'intervallo, puoi anche ottenere collisioni tra il raggio al quadrato che differiscono di 2.
Peter Taylor,

1

Java, scansione del cerchio alla Bresenham

Dal punto di vista euristico, mi aspetto di ottenere più collisioni partendo dalla parte più ampia dell'annulus. Mi aspettavo di ottenere qualche miglioramento eseguendo una scansione per ogni collisione, registrando valori per i quali surplusè compreso 0e r2max - r2compreso, ma nei miei test si è rivelato più lento di questa versione. Allo stesso modo tenta di utilizzare un singolo int[]buffer piuttosto che molta creazione di matrici ed elenchi a due elementi. L'ottimizzazione delle prestazioni è davvero una strana bestia.

Esegui con un argomento della riga di comando per l'output delle coppie e senza conteggi semplici.

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

La maggior parte di questi sono gli stessi di quelli mostrati da Ell , e il resto si basa su (j,0) (k,l). Per ciascuno j, torno indietro di alcuni quadrati e controllo se il resto dà un falso positivo. Questo richiede praticamente tutto il tempo con solo un guadagno di 25k (circa lo 0,1%) rispetto al solo (j,0) (j,1), ma un guadagno è un guadagno.

Questo finirà tra meno di dieci minuti sulla mia macchina, ma non so cosa tu abbia. Perché ragioni, se non finisce prima che scada il tempo, avrà un punteggio drasticamente peggiore. In tal caso, è possibile modificare il divisore sulla riga 8 in modo che termini in tempo (questo determina semplicemente la distanza percorsa per ciascuno j). Per alcuni vari divisori, i punteggi sono:

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

Per attivare l'output per ogni corrispondenza (e, dio, è lento se lo fai), basta commentare le righe 10 e 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);
    }
}

Per riferimento, le prime 20 uscite che fornisce (per divisore = 7, esclusi i (j,0)(j,1)tipi) sono:

(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 falsi positivi

Ecco una ricerca della forza bruta molto ingenua, che puoi visualizzare come implementazione di riferimento.

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

È possibile stampare le coppie (e le loro esatte dimensioni quadrate) decommentando la @printflinea.

Fondamentalmente questo avvia la ricerca x = y = 6e7della prima coppia di coordinate e scansiona circa l'1% del percorso verso l'asse x prima di decrementare x. Quindi, per ciascuna di tali coppie di coordinate, controlla l'intero arco della stessa grandezza (arrotondando su e giù) per una collisione.

Il codice presuppone che sia eseguito su un sistema a 64 bit, quindi i tipi di numeri interi e in virgola mobile predefiniti sono quelli a 64 bit (in caso contrario, è possibile crearli con int64()e float64()costruttori).

Ciò produce un magro 530 risultati.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.