Stampa un valore specifico nella matrice Wythoff modulo 2


11

La matrice di Wythoff è una matrice infinita composta dai numeri Grundy di ogni quadrato su una scacchiera nel gioco di Wythoff .

Ogni voce in questa matrice è uguale al numero non negativo più piccolo che non appare da nessuna parte sopra, a sinistra o diagonalmente a nord-ovest della posizione della voce.

Il quadrato 20 per 20 in alto a sinistra è simile al seguente:

  0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17 18 19
  1  2  0  4  5  3  7  8  6 10 11  9 13 14 12 16 17 15 19 20
  2  0  1  5  3  4  8  6  7 11  9 10 14 12 13 17 15 16 20 18
  3  4  5  6  2  0  1  9 10 12  8  7 15 11 16 18 14 13 21 17
  4  5  3  2  7  6  9  0  1  8 13 12 11 16 15 10 19 18 17 14
  5  3  4  0  6  8 10  1  2  7 12 14  9 15 17 13 18 11 16 21
  6  7  8  1  9 10  3  4  5 13  0  2 16 17 18 12 20 14 15 11
  7  8  6  9  0  1  4  5  3 14 15 13 17  2 10 19 21 12 22 16
  8  6  7 10  1  2  5  3  4 15 16 17 18  0  9 14 12 19 23 24
  9 10 11 12  8  7 13 14 15 16 17  6 19  5  1  0  2  3  4 22
 10 11  9  8 13 12  0 15 16 17 14 18  7  6  2  3  1  4  5 23
 11  9 10  7 12 14  2 13 17  6 18 15  8 19 20 21  4  5  0  1
 12 13 14 15 11  9 16 17 18 19  7  8 10 20 21 22  6 23  3  5
 13 14 12 11 16 15 17  2  0  5  6 19 20  9  7  8 10 22 24  4
 14 12 13 16 15 17 18 10  9  1  2 20 21  7 11 23 22  8 25 26
 15 16 17 18 10 13 12 19 14  0  3 21 22  8 23 20  9 24  7 27
 16 17 15 14 19 18 20 21 12  2  1  4  6 10 22  9 13 25 11 28
 17 15 16 13 18 11 14 12 19  3  4  5 23 22  8 24 25 21 26 10
 18 19 20 21 17 16 15 22 23  4  5  0  3 24 25  7 11 26 12 13
 19 20 18 17 14 21 11 16 24 22 23  1  5  4 26 27 28 10 13 25

Al momento non esiste un algoritmo efficiente noto per calcolare una voce arbitraria nella matrice di Wythoff. Tuttavia, il tuo compito in questo problema è provare a progettare una funzione euristica che indichi se il numero in corrispondenza di una coordinata specifica wythoff(x, y)è pari o dispari.

Il programma non può contenere più di 64 KB (65.536 byte) di codice sorgente o utilizzare più di 2 MB (2.097.152 byte) di memoria di lavoro.

In particolare per l'utilizzo della memoria, ciò significa che la dimensione massima impostata del residente del programma non può superare 2 MB in più rispetto alla dimensione massima impostata del residente di un programma vuoto in quella lingua. Nel caso di un linguaggio interpretato, sarebbe l'utilizzo della memoria dell'interprete / macchina virtuale stessa e, nel caso di un linguaggio compilato, sarebbe l'utilizzo della memoria di un programma che esegue il metodo principale e non fa nulla.

Il programma verrà testato sulla 10000 x 10000matrice per i valori di riga in 20000 <= x <= 29999e i valori di colonna in 20000 <= y <= 29999.

Il punteggio del tuo programma è il tasso di precisione (numero di ipotesi corrette) raggiunto dal tuo programma, con un codice più breve che funge da tiebreaker.


3
01.Rè un 05AB1E che genera in modo casuale vero o falso. Sia 0 vero e 1 falso, il mio programma sarà teoricamente corretto ~ 50% delle volte. È una voce valida?
Magic Octopus Urn

@carusocomputing In realtà, ho dimenticato di menzionare che le soluzioni randomizzate non sono consentite: il tuo programma dovrebbe produrre sempre lo stesso output per lo stesso input, anche se sospetto che la parola funzione lo implichi.
Joe Z.

Se aggiusto il seme del mio prng su ogni chiamata, produrrà lo stesso output per input identico e so cosa intendi, ma probabilmente dovrai esprimerlo in modo più specifico in qualche modo.
miglia

Ottengo qualcosa di molto diverso quando cerco Wythoff Matrix
Linus l'

@Linus "Wythoff's Game Matrix" sarebbe meglio? Ho visto anche quella pagina.
Joe Z.

Risposte:


6

Pitone; precisione = 54.074.818; dimensione = 65.526 byte

Punteggi precedenti: 50.227.165; 50.803.687; 50.953.001

#coding=utf-8
d=r'''<65,400 byte string>'''
def f(x,y):
 a=max(x,y)-20000;b=min(x,y)-20000;c=(a*(a+1)//2+b)%523200
 return(ord(d[c//8])>>(c%8))&1

Questo approccio divide tutte le voci univoche della matrice in 523.200 gruppi e legge la migliore ipotesi per il gruppo (x, y) da una stringa binaria. Puoi scaricare il codice sorgente completo da Google Drive .

Ho usato le parità di @ PeterTaylor per generare la stringa e calcolare l'accuratezza.


Ho provato molti approcci diversi e più interessanti, ma alla fine un semplice hardcode ha superato tutti ...
Dennis

L'hardcoding è anche un approccio valido: potrebbe trasformarsi, per esempio, in quale schema di hardcoding restituisce il miglior risultato.
Joe Z.

Non dire diversamente, ma poiché la distribuzione delle parità è ovviamente molto casuale, speravo di superare questo approccio. Finora, però, tutti i miei tentativi sono falliti.
Dennis,

No, va bene. Significa solo che questo problema è troppo difficile da fare correttamente. Ho creato molti altri problemi di questo stile, tranne quelli monodimensionali. Sono tutti nella sandbox se vuoi controllarli.
Joe Z.

4

CJam (precisione 50016828/100000000, 6 byte)

{+1&!}

(In pseudocodice in stile ALGOL per non CJammer:) return ((x + y) & 1) == 0.

Ciò si comporta meglio di qualsiasi altra dozzina di euristiche semplici che ho provato. È anche meglio di qualsiasi combinazione con i prossimi due migliori.


Il punteggio presuppone che la mia sezione calcolata della matrice sia corretta. Accoglienza della verifica indipendente. Sto ospitando i bit di parità calcolati su http://cheddarmonk.org/codegolf/PPCG95604-parity.bz2 (download da 8 MB, estratto in file di testo da 50 MB: poiché la matrice è simmetrica rispetto alla diagonale principale, ho incluso solo ciascuno linea a partire dalla diagonale principale, quindi devi sfalsare, trasporre e bit a bit O per ottenere il quadrato completo).

Il codice che ho usato per calcolarlo è Java. Utilizza la definizione in modo abbastanza semplice, ma con una struttura di dati impostata che lunghezza di esecuzione codifica gli intervalli in modo che sia veloce per passare al successivo valore consentito. Un'ulteriore ottimizzazione sarebbe possibile, ma funziona sul mio desktop moderatamente vecchio in circa due ore e 1,5 GB di spazio heap.

import java.util.*;

public class PPCG95604Analysis
{
    static final int N = 30000;

    public static void main(String[] args) {
        Indicator[] cols = new Indicator[N];
        Indicator[] diag = new Indicator[N];
        for (int i = 0; i < N; i++) {
            cols[i] = new Indicator();
            diag[i] = new Indicator();
        }

        int maxVal = 0;

        for (int y = 0; y < N; y++) {
            Indicator row = new Indicator(cols[y]);

            for (int x = y; x < N; x++) {
                Indicator col = cols[x];
                Indicator dia = diag[x - y];

                Indicator.Result rr = row.firstCandidate();
                Indicator.Result rc = col.firstCandidate();
                Indicator.Result rd = dia.firstCandidate();

                while (true) {
                    int max = Math.max(Math.max(rr.candidateValue(), rc.candidateValue()), rd.candidateValue());
                    if (rr.candidateValue() == max && rc.candidateValue() == max && rd.candidateValue() == max) break;

                    if (rr.candidateValue() != max) rr = rr.firstCandidateGreaterThan(max - 1);
                    if (rc.candidateValue() != max) rc = rc.firstCandidateGreaterThan(max - 1);
                    if (rd.candidateValue() != max) rd = rd.firstCandidateGreaterThan(max - 1);
                }

                if (y >= 20000 && x >= 20000) System.out.format("%d", rr.candidateValue() & 1);
                maxVal = Math.max(maxVal, rr.candidateValue());
                rr.markUsed();
                rc.markUsed();
                rd.markUsed();
            }
            if (y >= 20000) System.out.println();
        }
    }

    static class Indicator
    {
        private final int INFINITY = (short)0xffff;
        private final int MEMBOUND = 10000;

        private short[] runLengths = new short[MEMBOUND];

        public Indicator() { runLengths[1] = INFINITY; }

        public Indicator(Indicator clone) { System.arraycopy(clone.runLengths, 0, runLengths, 0, MEMBOUND); }

        public Result firstCandidate() {
            // We have a run of used values, followed by a run of unused ones.
            return new Result(1, 0xffff & runLengths[0], 0xffff & runLengths[0]);
        }

        class Result
        {
            private final int runIdx;
            private final int runStart;
            private final int candidateValue;

            Result(int runIdx, int runStart, int candidateValue) {
                this.runIdx = runIdx;
                this.runStart = runStart;
                this.candidateValue = candidateValue;
            }

            public int candidateValue() {
                return candidateValue;
            }

            public Result firstCandidateGreaterThan(int x) {
                if (x < candidateValue) throw new IndexOutOfBoundsException();

                int idx = runIdx;
                int start = runStart;
                while (true) {
                    int end = start + (0xffff & runLengths[idx]) - 1;
                    if (end > x) return new Result(idx, start, x + 1);

                    // Run of excluded
                    start += 0xffff & runLengths[idx];
                    idx++;
                    // Run of included
                    start += 0xffff & runLengths[idx];
                    idx++;

                    if (start > x) return new Result(idx, start, start);
                }
            }

            public void markUsed() {
                if (candidateValue == runStart) {
                    // Transfer one from the start of the run to the previous run
                    runLengths[runIdx - 1]++;
                    if (runLengths[runIdx] != INFINITY) runLengths[runIdx]--;
                    // May need to merge runs
                    if (runLengths[runIdx] == 0) {
                        runLengths[runIdx - 1] += runLengths[runIdx + 1];
                        for (int idx = runIdx; idx < MEMBOUND - 2; idx++) {
                            runLengths[idx] = runLengths[idx + 2];
                            if (runLengths[idx] == INFINITY) break;
                        }
                    }

                    return;
                }

                if (candidateValue == runStart + (0xffff & runLengths[runIdx]) - 1) {
                    // Transfer one from the end of the run to the following run.
                    if (runLengths[runIdx + 1] != INFINITY) runLengths[runIdx + 1]++;
                    if (runLengths[runIdx] != INFINITY) runLengths[runIdx]--;
                    // We never need to merge runs, because if we did we'd have hit the previous case instead
                    return;
                }

                // Need to split the run. From
                //   runIdx: a+1+b
                // to
                //   runIdx: a
                //   runIdx+1: 1
                //   runIdx+2: b
                //   runIdx+3: previous val at runIdx+1
                for (int idx = MEMBOUND - 1; idx > runIdx + 2; idx--) {
                    runLengths[idx] = runLengths[idx - 2];
                }
                runLengths[runIdx + 2] = runLengths[runIdx] == INFINITY ? INFINITY : (short)((0xffff & runLengths[runIdx]) + runStart - 1 - candidateValue);
                runLengths[runIdx + 1] = 1;
                runLengths[runIdx] = (short)(candidateValue - runStart);
            }
        }
    }
}

3

J, precisione = 50022668/10 8 = 50,0227%, 4 byte

2|*.

Prende le coordinate come due argomenti, calcola l'LCM tra loro e lo prende modulo 2. Un 0significa che è pari e un 1mezzo è dispari.

La performance si basa sui bit di parità forniti da @ Peter Taylor .

La versione PRNG precedente per 7 byte, 2|?.@#.aveva una precisione di 50010491/10 8 .

Spiegazione

2|*.  Input: x on LHS, y on RHS
  *.  LCM(x, y)
2|    Modulo 2

1
La parità dell'LCM è la parità dell'AND bit a bit. Ti fa risparmiare un byte? La cosa affascinante è che è ovviamente una cattiva euristica (dà 1solo il 25% delle volte, quando la proporzione corretta è quasi esattamente il 50%), e tuttavia fa meglio di molti che non sono così ovviamente cattivi.
Peter Taylor,

Grazie, ma purtroppo AND bit a bit in J è letteralmente AND.
miglia

@PeterTaylor Questo tipo di sorprendente scoperta euristica è ciò che si suppone debbano affrontare sfide come questa.
Joe Z.
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.