Роздрукуйте конкретне значення в модулі 2 матриці Wythoff


11

Матриця Wythoff - це нескінченна матриця, що складається з номерів Grundy кожного квадрата на шаховій дошці в грі Wythoff .

Кожен запис у цій матриці дорівнює найменшому негативному номеру, яке не з’являється ніде вгорі, зліва або по діагоналі на північний захід від позиції.

Лівий верхній квадрат 20 на 20 виглядає так:

  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

Наразі не існує відомого ефективного алгоритму для обчислення довільного запису в матриці Вайтофа. Однак ваше завдання в цій проблемі - спробувати спроектувати евристичну функцію, яка підкаже, чи є число певної координати wythoff(x, y)парним чи непарним.

Ваша програма не може містити більше 64 КБ (65,536 байт) вихідного коду або використовувати більше 2 Мб (2 097 152 байт) робочої пам'яті.

Зокрема, для використання пам'яті, це означає, що максимальний розмір набору резидентів вашої програми не може перевищувати 2 Мб більше, ніж максимальний розмір набору резидентної порожньої програми на цій мові. У випадку інтерпретованої мови це буде використання пам'яті самого інтерпретатора / віртуальної машини, а у випадку складеної мови - це використання пам'яті програми, яка виконує основний метод і нічого не робить.

Ваша програма буде протестована на 10000 x 10000матриці на значення рядків у 20000 <= x <= 29999та значеннях стовпців у 20000 <= y <= 29999.

Оцінка вашої програми - це швидкість точності (кількість правильних здогадок), яку досягає ваша програма, і коротший код виступає в ролі краватки.


3
01.Rявляє собою 05AB1E, який виводить істинно або помилково випадковим чином. Нехай 0 відповідає істині, а 1 - хибною, теоретично моя програма буде коректною ~ 50% часу. Це дійсний запис?
Magic Octopus Urn

@carusocomputing Насправді я забув зазначити, що рандомізовані рішення заборонені - ваша програма повинна створювати один і той же вихід на один і той же вхід, хоча я підозрюю, що функція слова передбачає це.
Джо З.

Якщо я зафіксую насіння мого prng під час кожного виклику, він дасть однаковий вихід для однакового введення, і я знаю, що ви маєте на увазі, але вам, мабуть, доведеться це якось конкретніше сказати.
милі

Я отримую щось зовсім інше, коли шукаю Матрицю Вайтофа
Лінус

@Linus Чи краща буде "Ігрова матриця Вайтофа"? Я бачив і цю сторінку.
Джо З.

Відповіді:


6

Пітон; точність = 54,074,818; розмір = 65 526 байт

Попередні бали: 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

Цей підхід розділяє всі унікальні записи матриці на 523 200 груп і читає найкращу здогадку для групи (x, y) з двійкового рядка. Ви можете завантажити повний вихідний код з Диска Google .

Я використовував паритети @ PeterTaylor для створення рядка та обчислення точності.


Я спробував багато різних, цікавіших підходів, але врешті-решт, простий жорсткий код випередив їх усіх ...
Денніс

Жорстке кодування також є коректним підходом - воно може перетворитись, скажімо, на яку схему жорсткого кодування повертає найкращий результат.
Джо З.

Не кажучи інакше, але оскільки розподіл паритетів є явно невипадковим, я сподівався перемогти цей підхід. Поки всі мої спроби не вдалися.
Денніс

Ні, це добре. Це просто означає, що цю проблему надто важко зробити належним чином. Я створював ще багато проблем цього стилю, крім одновимірних. Вони всі в пісочниці, якщо ви хочете перевірити їх.
Джо Z.

4

CJam (точність 50016828/100000000, 6 байт)

{+1&!}

(У псевдокоді в стилі ALGOL для не-CJammers:) return ((x + y) & 1) == 0.

Це краще, ніж будь-яка з двох інших десятків простих евристик, які я пробував. Це навіть краще, ніж будь-яке поєднання з наступними двома найкращими.


Оцінка передбачає, що мій обчислений розділ матриці правильний. Незалежна перевірка вітається. Я розміщую обчислені біти парності на http://cheddarmonk.org/codegolf/PPCG95604-parity.bz2 (завантаження 8 МБ, витяги до текстового файлу 50 Мб: оскільки матриця симетрична щодо основної діагоналі, я включив лише кожен лінія, що починається від основної діагоналі, тому вам доведеться змістити, транспонувати та побіжно АБО, щоб отримати повний квадрат).

Код, який я використовував для його обчислення, - це Java. Він використовує визначення досить прямо, але зі встановленою структурою даних, довжина пробігу кодує діапазони, так що можна швидко перейти до наступного дозволеного значення. Подальша оптимізація була б можливою, але вона працює на моєму помірно старому робочому столі приблизно за дві години та 1,5 Гб простору купи.

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, точність = 50022668/10 8 = 50.0227%, 4 байти

2|*.

Координати приймає як два аргументи, обчислює LCM між ними і приймає його за модулем 2. А 0означає, що це парне, а 1засіб - непарне.

Вистава базується на парних бітах, наданих @ Peter Taylor .

Раніше версія 2|?.@#.PRNG на 7 байт мала точність 50010491/10 8 .

Пояснення

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

1
Паритет LCM - це парність порозрядного AND. Це рятує вас від байта? Захоплююча річ у тому, що це так очевидно погано евристично (це дає 1лише 25% часу, коли правильна частка майже рівно 50%), і все-таки це краще, ніж багато хто, що не так очевидно погано.
Пітер Тейлор

Дякую, але, на жаль, побіжно І в J буквально AND.
милі

@PeterTaylor Таке дивне евристичне відкриття - це те, у чому повинні бути проблеми, як ця.
Джо З.
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.