Imprimer une valeur spécifique dans la matrice Wythoff modulo 2

11

La matrice Wythoff est une matrice infinie composée des nombres Grundy de chaque carré sur un échiquier dans le jeu de Wythoff .

Chaque entrée de cette matrice est égale au plus petit nombre non négatif qui n'apparaît nulle part au-dessus, à gauche ou en diagonale au nord-ouest de la position de l'entrée.

Le carré 20 par 20 en haut à gauche ressemble à ceci:

  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

Il n'existe actuellement aucun algorithme efficace connu pour calculer une entrée arbitraire dans la matrice Wythoff. Cependant, votre tâche dans ce problème est d'essayer de concevoir une fonction heuristique qui dira si le nombre à une coordonnée spécifique wythoff(x, y)est pair ou impair.

Votre programme ne doit pas contenir plus de 64 Ko (65 536 octets) de code source ou utiliser plus de 2 Mo (2 097 152 octets) de mémoire de travail.

En particulier pour l'utilisation de la mémoire, cela signifie que la taille maximale définie par le résident de votre programme ne peut pas dépasser 2 Mo de plus que la taille maximale définie par le résident d'un programme vide dans cette langue. Dans le cas d'un langage interprété, ce serait l'utilisation de la mémoire de l'interpréteur / machine virtuelle elle-même, et dans le cas d'un langage compilé, ce serait l'utilisation de la mémoire d'un programme qui exécute la méthode principale et ne fait rien.

Votre programme sera testé sur la 10000 x 10000matrice pour les valeurs de ligne 20000 <= x <= 29999et les valeurs de colonne dans 20000 <= y <= 29999.

Le score de votre programme est le taux de précision (nombre de suppositions correctes) atteint par votre programme, avec un code plus court faisant office de bris d'égalité.

Joe Z.
la source
3
01.Rest un 05AB1E qui sort vrai ou faux au hasard. Soit 0 vrai et 1 faux, mon programme sera théoriquement correct ~ 50% du temps. Est-ce une entrée valide?
Magic Octopus Urn
@carusocomputing En fait, j'ai oublié de mentionner que les solutions randomisées ne sont pas autorisées - votre programme devrait produire la même sortie pour la même entrée à chaque fois, bien que je soupçonne que le mot fonction implique cela.
Joe Z.
Si je fixe la graine de mon prng à chaque appel, cela produira la même sortie pour une entrée identique, et je sais ce que vous voulez dire, mais vous devrez probablement le formuler plus spécifiquement d'une manière ou d'une autre.
miles
Je reçois quelque chose de très différent lorsque je recherche Wythoff Matrix
Linus
@Linus La "matrice de jeu de Wythoff" serait-elle meilleure? J'ai aussi vu cette page.
Joe Z.

Réponses:

6

Python; précision = 54 074 818; taille = 65,526 octets

Scores précédents: 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

Cette approche divise toutes les entrées uniques de la matrice en 523 200 groupes et lit la meilleure estimation pour le groupe (x, y) à partir d'une chaîne binaire. Vous pouvez télécharger le code source complet depuis Google Drive .

J'ai utilisé les parités de @ PeterTaylor pour générer la chaîne et calculer la précision.

Dennis
la source
J'ai essayé de nombreuses approches différentes et plus intéressantes, mais au final, un simple code dur les a toutes surpassées ...
Dennis
Le codage en dur est également une approche valable - il pourrait se transformer, par exemple, en un schéma de codage en dur qui renvoie le meilleur résultat.
Joe Z.
Je ne dis pas le contraire, mais comme la distribution des parités est très évidemment non aléatoire, j'espérais dépasser cette approche. Jusqu'à présent, toutes mes tentatives ont échoué.
Dennis
Non, ça va. Cela signifie simplement que ce problème est trop difficile à résoudre correctement. J'ai créé beaucoup plus de problèmes de ce style, sauf unidimensionnel. Ils sont tous dans le bac à sable si vous voulez les vérifier.
Joe Z.
4

CJam (précision 50016828/100000000, 6 octets)

{+1&!}

(Dans le pseudocode de style ALGOL pour les non-CJammers:) return ((x + y) & 1) == 0.

Cela fonctionne mieux que les deux autres douzaines d'heuristiques simples que j'ai essayées. C'est encore mieux que n'importe quelle combinaison avec les deux meilleurs suivants.


Le score suppose que ma section calculée de la matrice est correcte. Vérification indépendante bienvenue. J'héberge les bits de parité calculés sur http://cheddarmonk.org/codegolf/PPCG95604-parity.bz2 (téléchargement de 8 Mo, extrait dans un fichier texte de 50 Mo: puisque la matrice est symétrique par rapport à la diagonale principale, je n'ai inclus que chacun ligne à partir de la diagonale principale, vous devez donc décaler, transposer et bit à bit OU pour obtenir le carré complet).

Le code que j'ai utilisé pour le calculer est Java. Il utilise la définition de manière assez simple, mais avec une structure de données définie dont la longueur d'exécution code les plages de sorte qu'il est rapide de passer à la prochaine valeur autorisée. Une optimisation supplémentaire serait possible, mais elle s'exécute sur mon bureau modérément ancien en environ deux heures et 1,5 Go d'espace de stockage.

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);
            }
        }
    }
}
Peter Taylor
la source
3

J, précision = 50022668/10 8 = 50,0227%, 4 octets

2|*.

Prend les coordonnées comme deux arguments, calcule le LCM entre eux et le prend modulo 2. A 0signifie qu'il est pair et un 1moyen qu'il est impair.

Les performances sont basées sur les bits de parité fournis par @ Peter Taylor .

La version PRNG avant pour 7 octets, 2|?.@#.avait une précision de 50010491/10 8 .

Explication

2|*.  Input: x on LHS, y on RHS
  *.  LCM(x, y)
2|    Modulo 2
miles
la source
1
La parité du LCM est la parité de l'ET au niveau du bit. Cela vous fait-il économiser un octet? Ce qui est fascinant, c'est que c'est évidemment une mauvaise heuristique (cela ne donne 1que 25% du temps, lorsque la proportion correcte est presque exactement 50%), et pourtant elle fait mieux que beaucoup d'autres qui ne sont pas si manifestement mauvaises.
Peter Taylor
Merci, mais malheureusement au niveau du bit ET en J est littéralement AND.
miles
@PeterTaylor Ce genre de découverte heuristique surprenante est ce que sont censés être les défis comme celui-ci.
Joe Z.