在Wythoff矩阵模2中打印特定值


11

Wythoff矩阵是一个无限矩阵,由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矩阵中的任意项。但是,解决此问题的任务是尝试设计一种启发式函数,该函数将告诉特定坐标上的数字wythoff(x, y)是偶数还是奇数。

您的程序包含的源代码不得超过64 KB(65,536字节),或使用的工作内存不得超过2 MB(2,097,152字节)。

特别是对于内存使用,这意味着程序的最大常驻集大小不得超过该语言中空程序的最大常驻集大小2 MB以上。在解释语言的情况下,将是解释程序/虚拟机本身的内存使用情况;在编译语言的情况下,将是执行main方法但不执行任何操作的程序的内存使用情况。

您的程序将在10000 x 10000矩阵上针对中的行值20000 <= x <= 29999和中的列值进行测试20000 <= y <= 29999

程序的分数是程序达到的准确率(正确猜测的数量),其中较短的代码充当决胜局。


3
01.R是一个05AB1E,它随机输出true或false。假设0为true,1为false,理论上我的程序在大约50%的时间内都是正确的。这是有效的条目吗?
魔术章鱼缸

@carusocomputing实际上,我忘了提及不允许使用随机解的方法—尽管我怀疑函数一词的含义是,您的程序每次都应为相同的输入产生相同的输出。
Joe Z.

如果我在每次调用中修复prng的种子,它将为相同的输入产生相同的输出,并且我知道您的意思,但您可能不得不以某种方式更具体地措辞。
英里

当我搜索Wythoff Matrix
Linus

@Linus“ Wythoff的游戏矩阵”会更好吗?我也看到了该页面。
Joe Z.

Answers:


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的奇偶校验来生成字符串并计算准确性。


我尝试了许多不同的,更有趣的方法,但最后,简单的硬编码却超越了所有方法……
Dennis

硬编码也是一种有效的方法,例如,硬编码可能会返回最佳结果。
Joe Z.

不用说别的了,但是由于奇偶校验的分布很明显是非随机的,所以我希望超越这种方法。到目前为止,我的所有尝试都失败了。
丹尼斯

不,没关系。这仅表示此问题很难正确解决。除了一维之外,我一直在打这种样式的问题。如果您要检出它们,它们都在沙箱中。
Joe Z.

4

CJam(精度50016828/100000000,6字节)

{+1&!}

(对于非CJammers使用ALGOL样式的伪代码:)return ((x + y) & 1) == 0

这比我尝试过的其他两种简单的启发式方法中的任何一种都要好。它甚至比后两个最佳组合更好。


得分假设我的矩阵计算部分正确。欢迎独立验证。我将计算的奇偶校验位托管在http://cheddarmonk.org/codegolf/PPCG95604-parity.bz2(下载8MB,提取到50MB文本文件:由于矩阵关于主对角线是对称的,因此我只包括了每个线从主对角线开始,因此您必须进行偏移,转置和按位或以获得完整的正方形)。

我用来计算它的代码是Java。它非常直接地使用该定义,但是具有设置的数据结构,该数据结构对范围进行游程编码,以便快速跳至下一个允许的值。可能会进行进一步的优化,但是它可以在大约两个小时和1.5GB的堆空间中在我的老式桌面上运行。

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。A 0表示偶数,而1奇数。

性能基于@ Peter Taylor提供的奇偶校验位。

之前的PRNG版本为7个字节,2|?.@#.精度为50010491/10 8

说明

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

1
LCM的奇偶校验是按位与的奇偶校验。这样可以节省您一个字节吗?令人着迷的是,它显然是一种很糟糕的启发式方法(1仅占25%的时间,而正确的比例几乎恰好是50%),但它的表现却好于很多那些还不是很明显的问题。
彼得·泰勒

谢谢,但不幸的是,按位AND在J中实际上是AND
英里

@PeterTaylor那种令人惊讶的启发式发现就是这种挑战所应该解决的所有问题。
Joe Z.
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.