最快的迷你手枪奎宁


26

迷你Flak是的一个子集脑高射炮语言,其中<><...>[]操作是不允许的。严格来说,它不能与以下正则表达式匹配

.*(<|>|\[])

Mini-Flak是Brain-Flak的最小已知图灵完整子集。


不久前,我能够用Mini-Flak制作Quine,但是它太慢了,无法在宇宙的生命周期中运行。

因此,我对您的挑战是使Quine更快。


计分

要对代码评分,请在代码@cy末尾添加一个标志,然后使用该标志在Ruby解释器中运行(使用ruby解释器在线尝试-d。您的分数应打印到STDERR,如下所示:

@cy <score>

这是程序终止之前所经过的周期数,两次运行之间相同。由于每个周期的运行时间大致相同,因此您的分数应与运行程序所需的时间直接相关。

如果Quine太长而无法在计算机上合理运行,则可以手动计算周期数。

计算周期数不是很困难。循环数等于运行的单子数加运行的nilad数的2倍。这与用一个字符替换每个nilad并计算总共运行的字符数相同。

评分示例

  • (()()()) 得分为5,因为它有1个单子和3个尼拉德。

  • (()()()){({}[()])} 得分为29,因为第一部分与以前相同,得分为5,而循环包含6个单子和2个得分为8的尼拉德。循环运行3次,因此我们计算其得分为3次。 1*5 + 3*8 = 29


要求

您的程序必须...

  • 至少2个字节

  • 在Brain-Flak中使用-A标志执行时打印其源代码

  • 不匹配正则表达式 .*(<|>|\[])


提示

  • 起重机高射炮解释是断然比Ruby解释器快,但缺少一些功能。我建议您先使用Crane-Flak测试您的代码,然后在您知道它可以工作时在ruby解释器中对其评分。我也强烈建议不要在TIO中运行您的程序。TIO不仅比台式机解释器慢,而且还会在大约一分钟后超时。如果有人在TIO超时之前设法获得足够低的分数来运行他们的程序,那将是非常令人印象深刻的。

  • [(...)]{}(...)[{}]工作与<...>但不违反受限源要求

  • 如果您想了解如何应对这一挑战,可以查看Brain-FlakMini-Flak Quines。


1
“当前最佳”->“仅当前”
HyperNeutrino

Answers:


33

Mini-Flak,6851113循环

该程序(字面意思)

我知道大多数人不太希望Mini-Flak quin使用不可打印的字符,甚至使用多字节字符(使编码有意义)。但是,这种方法确实可行,并且不可打印的内容与方法的大小(93919个字符编码为UTF-8的102646字节)相结合,使得将程序放入此帖子中相当困难。

但是,该程序非常重复,因此压缩效果非常好。为了使整个程序在字面上可以从Stack Exchange获得,在可折叠的后面隐藏了xxd一个gzip完整版本的-压缩版本的可逆十六进制转储:

(是的,它是如此重复,以至于压缩您甚至可以看到重复的片段)。

问题是:“我也强烈建议您不要在TIO中运行程序。TIO不仅比台式机解释器慢,而且还会在大约一分钟内超时。如果有人设法得分足够低而无法运行,那将给人留下深刻的印象。他们的程序在TIO超时之前。” 我可以做到的!使用Ruby解释器,在TIO上运行大约需要20秒:在线尝试!

该程序(可读)

现在,我提供了计算机可以读取的程序版本,让我们尝试一下人类可以读取的版本。我已经将构成五线组的字节转换为代码页437(如果设置了高位)或Unicode控制图片(如果它们是ASCII控制代码),则添加了空格(任何先前存在的空格都转换为控制图片) ),使用语法进行游程编码«string×length»,并删除了一些数据繁重的位:

␠
(((()()()()){}))
{{}
    (({})[(()()()())])
    (({})(
        {{}{}((()[()]))}{}
        (((((((({})){}){}{})){}{}){}){}())
        {
            ({}(
                (␀␀!S␠su! … many more comment characters … oq␝qoqoq)
                («()×35» («()×44» («()×44» («()×44» («()×44» («()×45»
                … much more data encoded the same way …
                («()×117»(«()×115»(«()×117»
                «000010101011┬â┬ … many more comment characters … ┬â0┬â┬à00␈␈
                )[({})(
                    ([({})]({}{}))
                    {
                        ((()[()]))
                    }{}
                    {
                        {
                            ({}(((({}())[()])))[{}()])
                        }{}
                        (({}))
                        ((()[()]))
                    }{}
                )]{}
                %Wwy$%Y%ywywy$wy$%%%WwyY%$$wy%$$%$%$%$%%wy%ywywy'×almost 241»
                ,444454545455┬ç┬ … many more comment characters … -a--┬ü␡┬ü-a␡┬ü
            )[{}()])
        }{}
        {}({}())
    )[{}])
    (({})(()()()()){})
}{}{}␊

(“几乎241”是因为第241个副本缺少结尾',但与其他240个相同)。

说明

关于评论

首先要解释的是,那些不是Mini-Flak命令的不可打印字符和其他垃圾怎么处理?您可能会认为,将注释添加到包装盒只会使事情变得更难,但这是一场速度竞赛(而非大小竞赛),这意味着注释不会损害程序的速度。同时,Brain-Flak和Mini-Flak只是将堆栈中的内容转储到标准输出中。如果您必须确保堆栈包含组成程序命令的字符,则必须花一些时间清理堆栈。实际上,Brain-Flak会忽略大多数字符,因此只要我们确保垃圾堆栈元素不是有效的Brain-Flak命令(使之成为Brain-Flak / Mini-Flak多色图),并且不是负数或在外部在Unicode范围内,我们可以将它们保留在堆栈上,允许它们输出,然后将相同字符放在程序中的相同位置以保留quine属性。

我们可以利用一种特别重要的方法来利用这一点。quine通过使用长数据字符串来工作,基本上,quine的所有输出都是通过以各种方式格式化数据字符串来产生的。尽管程序有多个片段,但是只有一个数据字符串;因此我们需要能够使用相同的数据字符串来打印程序的不同部分。“垃圾数据无关紧要”的技巧使我们能够以一种非常简单的方式进行操作。我们通过在其ASCII代码中添加或减去一个值来将组成程序的字符存储在数据字符串中。具体来说,构成程序开始的字符以ASCII码+ 4的形式存储,构成该节的字符以ASCII码− 4重复几乎241次。数据字符串的每个字符都有偏移量;例如,如果我们在每个字符代码上添加4,则将其打印出来,则得到重复部分的重复,并在前后加上一些注释。(这些注释只是程序的其他部分,字符代码进行了移位,以使它们不构成任何有效的Brain-Flak命令,因为添加了错误的偏移量。我们必须避开Brain-Flak命令,而不仅仅是Mini- Flak命令,以避免违反问题的部分;偏移量的选择旨在确保这一点。)

由于这个注释技巧,我们实际上只需要能够以两种不同的方式输出格式化的数据字符串:a)与源代码中的编码方式相同,b)作为字符代码,并在每个代码中添加指定的偏移量。这是一个极大的简化,使增加的长度完全值得。

程序结构

该程序包括四个部分:简介,数据字符串,数据字符串格式化程序和结尾。intro和outro基本上负责循环运行数据字符串及其格式化程序,每次都指定适当的格式(即,是编码还是偏移量,以及使用什么偏移量)。数据字符串只是数据,并且是quine的唯一部分,在该部分中,没有在数据字符串中按字面值指定组成字符的字符串(这样做显然是不可能的,因为它必须比自身更长);因此,它以一种非常容易从自身再生的方式编写。数据字符串格式化程序由241个几乎相同的部分组成,每个部分格式化数据字符串中241个中的特定数据。

程序的每个部分都可以通过数据字符串及其格式化程序生成,如下所示:

  • 要产生结果,请使用偏移量+8格式化数据字符串
  • 要生成数据字符串格式化程序,请使用偏移量+4格式化数据字符串241次
  • 要生成数据字符串,请通过将其编码为源格式来格式化数据字符串
  • 要制作简介,请使用偏移量-4格式化数据字符串

因此,我们要做的就是查看程序的这些部分如何工作。

数据字符串

(«()×35» («()×44» («()×44» («()×44» («()×44» («()×45» …

我们需要对数据字符串进行简单编码,因为我们必须能够反转Mini-Flak代码中的编码。您再简单不过了!

这种方法背后的关键思想(除了注释技巧)是要注意的是,基本上只有一个地方可以存储大量数据:程序源的各种嵌套级别中的“命令返回值之和”。(这通常称为第三叠,尽管Mini-Flak没有第二个堆栈,所以在Mini-Flak上下文中“工作堆栈”可能是一个更好的名称。)用于存储数据的其他可能性是主堆栈/第一个堆栈(不起作用)因为那是我们的输出所必须去的,并且我们不能以远距离有效的方式将输出移到存储之外),并在单个堆栈元素中将其编码成bignum(这不适用于此问题,因为它花费了指数时间)从中提取数据);当您消除这些位置时,工作堆栈是唯一剩余的位置。

为了将数据“存储”在该堆栈上,我们使用不平衡命令(在本例中为(…)命令的前半部分),稍后将在数据字符串格式化程序中对它们进行平衡。每次我们在格式化程序中关闭这些命令之一时,它将推送从数据字符串获取的数据之和,以及在格式化程序中处于该嵌套级别的所有命令的返回值;我们可以确保后者加为零,因此格式化程序仅查看从数据字符串获取的单个值。

格式非常简单:(,后跟的n个副本(),其中n是我们要存储的数字。(请注意,这意味着我们只能存储非负数,并且数据字符串的最后一个元素必须为正。)

关于数据字符串的一个有点不直观的地方是它的顺序。数据字符串的“开始”是靠近程序开始的结尾,即最外层的嵌套级别;这部分最后格式化(因为格式化程序从最内层嵌套层到最外层嵌套层运行)。但是,尽管最后格式化了,但还是先打印了,因为Mini-Flak解释器最后打印了被压入堆栈的值。相同的原则适用于整个程序。我们需要首先格式化结尾,然后格式化数据字符串格式化程序,然后格式化数据字符串,然后格式化序言,即它们在程序中存储顺序的相反顺序。

数据字符串格式化程序

)[({})(
    ([({})]({}{}))
    {
        ((()[()]))
    }{}
    {
        {
            ({}(((({}())[()])))[{}()])
        }{}
        (({}))
        ((()[()]))
    }{}
)]{}

数据字符串格式化程序由241个部分组成,每个部分具有相同的代码(一个部分的注释略有不同),每个部分都格式化数据字符串的一个特定字符。(我们不能在此处使用循环:我们需要一个不平衡的变量,)才能通过匹配它的不平衡变量来读取数据字符串(,而且我们不能将其中之一放入{…}存在的唯一循环形式的循环中。因此,我们“展开”格式化程序,然后简单地进行简介/输出操作即可输出具有格式化程序偏移量241倍的数据字符串。)

)[({})( … )]{}

格式化程序元素的最外层部分读取数据字符串的一个元素;数据字符串编码的简单性导致读取它时有点复杂。首先关闭(…)数据字符串中不匹配的内容,然后取反([…])两个值:刚从数据字符串(({}))中读取的数据和程序其余部分的返回值。我们使用复制复制其余formatter元素的返回值,(…)并使用将复制的内容添加到否定版本中{}。最终结果是数据字符串元素和格式化程序元素的返回值加在一起是基准减去基准减去返回值加上返回值或0;这是使下一个数据字符串元素产生正确值所必需的。

([({})]({}{}))

格式化程序使用顶部堆栈元素来知道它处于哪种模式(0 =数据字符串格式的格式,其他任何值=输出的偏移量)。但是,仅读取数据字符串,数据就位于堆栈格式的顶部,我们希望它们反过来。这个代码是脑高射炮交换代码的一较短的变型中,考虑一个上述bb以上一个  +  b ; 它不仅更短,而且(在此特定情况下)更有用,因为在b为0 时将b加到a的副作用并不成问题,而当b不为0时,它将为我们进行偏移计算。

{
    ((()[()]))
}{}
{
    …
    ((()[()]))
}{}

Brain-Flak仅具有一个控制流结构,因此,如果我们需要while循环以外的任何其他功能,则需要进行一些工作。这是一个“否定”结构;如果堆栈顶部为0,则将其删除,否则将0置于堆栈顶部。(它的工作原理非常简单:只要堆栈顶部没有0,就两次将1-1压入堆栈;完成后,弹出顶部堆栈元素。)

如此处所示,可以将代码放置在求反结构内。仅当栈顶为非零时,代码才运行;因此,如果我们有两个否定结构,则假设顶部的两个堆栈元素都不都是零,它们将互相抵消,但是第一个结构内部的任何代码仅在顶部堆栈元素为非零且内部代码仅当顶部堆栈元素为零时,第二个结构才会运行。换句话说,这等效于if-then-else语句。

在格式为非零的情况下运行的“ then”子句中,我们实际上没有任何关系。我们想要的是将数据和偏移量压入主堆栈(以便可以在程序末尾输出),但是它已经存在了。因此,我们只需要处理以源形式编码数据字符串元素的情况。

{
    ({}(((({}())[()])))[{}()])
}{}
(({}))

这是我们的方法。该{({}( … )[{}()])}{}结构应该熟悉为具有特定迭代次数的循环(通过将循环计数器移至工作堆栈并将其保存在其中来进行工作;它对任何其他代码都是安全的,因为对工作堆栈的访问与程序的嵌套级别)。循环的主体为((({}())[()])),它复制了顶部堆栈元素的三个副本,并向最低的堆栈元素添加了1。换句话说,它将堆栈顶部的40转换为41之上40之上的40,或者将其视为ASCII (转换为((); 重复运行这将使((()(()()(()()()等,因而产生我们的数据串一个简单的方法(假设有一个(堆栈顶部的话)。

循环完成后,请(({}))复制堆栈的顶部(以便现在开始((()…而不是开始(()…。)(,数据字符串格式化程序的下一个副本将使用前导来格式化下一个字符(将其扩展为(()(()…然后(()()(()…,依此类推,因此这会(在数据字符串中生成分隔符)。

%Wwy$%Y%ywywy$wy$%%%WwyY%$$wy%$$%$%$%$%%wy%ywywy'

数据字符串格式化程序的最后一点是令人感兴趣的。好的,因此大多数情况下,这只是向下移位了4个代码点;但是,最后的撇号可能看起来不合适。'(代码点39)将转换为+(代码点43),这不是Brain-Flak命令,因此您可能已经猜到它存在于其他目的。

这是因为数据字符串格式化程序希望(堆栈中已经有一个(它在任何地方都不包含文字40)。的'实际上是在重复开始以构成数据字符串格式化程序的块的开始,而不是结尾,因此,在将数据字符串格式化程序的字符推入堆栈后(代码即将移至打印数据字符串)本身,outro将堆栈顶部的39调整为40,以供格式化程序使用(这次正在运行的格式化程序本身,而不是其在源代码中的表示形式)以使用它。这就是为什么我们有“几乎241”份格式化程序的原因;第一个副本缺少其第一个字符。而且,单引号这个字符是数据字符串中仅有的三个与程序中某处的Mini-Flak代码不对应的字符之一;它纯粹是作为提供常量的一种方法。

简介与结尾

(((()()()()){}))
{{}
    (({})[(()()()())])
    (({})(
        {{}{}((()[()]))}{}
        (((((((({})){}){}{})){}{}){}){}())
        {
            ({}(
                (␀␀!S␠su! … many more comment characters … oq␝qoqoq)
                …
            )[{}()])
        }{}
        {}({}())
    )[{}])
    (({})(()()()()){})
}{}{}␊

简介和结尾在概念上是程序的同一部分;我们进行区分的唯一原因是,需要在输出数据字符串及其格式器之前输出outro(以便在其后面打印),而在其后面输出intro(在它们之前打印)。

(((()()()()){}))

我们首先将两个8的副本放在堆栈上。这是第一次迭代的偏移量。第二个副本是因为主循环期望偏移量之上的堆栈顶部有一个垃圾元素,而决定是否存在主循环的测试将其遗漏了,因此我们需要在其中放置一个垃圾元素,以便它并没有丢弃我们真正想要的元素;副本是做到这一点的最好方法(因此输出最快)。

数字8的其他表示形式不超过此数字。但是,当寻求最快的代码时,这绝对是最佳选择。一方面,使用()()()()速度比说更快(()()){},因为尽管两者都长8个字符,但前者的循环速度更快,因为(…)被算作2个循环,但()只算作一个循环。相比于一个大得多的考虑节省一个周期可以忽略不计,虽然:()具有比码点低得多的{},所以产生用于他们的数据片段将要快得多(和数据片段将占用较少的空间中的代码,太)。

{{} … }{}{}

主循环。这不计算迭代次数(这是一个while循环,而不是一个for循环,并使用测试进行爆发)。一旦退出,我们将丢弃顶部的两个堆栈元素。最上面的元素是一个无害的0,但是下面的元素将是“下次迭代使用的格式”,它(是一个负偏移量)是一个负数,并且当Mini的堆栈上有任何负数时-Flak程序退出,解释器尝试输出它们时崩溃。

因为此循环使用显式测试进行突破,所以该测试的结果将保留在堆栈中,因此我们将其丢弃为第一件事(其值无用)。

(({})[(()()()())])

该代码将4和f  -4 推入堆栈元素f的上方,同时将该元素保留在原位。我们正在预先计算下一次迭代的格式(同时我们拥有常数4),同时为程序的后几部分同时将堆栈设置为正确的顺序:我们将使用f作为此迭代,在此之前需要4。

(({})( … )[{}])

这会将f  − 4 的副本保存在工作堆栈中,以便我们可以将其用于下一次迭代。(f的值到那时仍会存在,但是它将在堆栈中处于一个尴尬的位置,即使我们可以将其操纵到正确的位置,我们也必须花费一些周期从中减去4,并循环打印代码以执行此减法操作。现在简单地存储它就容易得多。)

{{}{}((()[()]))}{}

测试偏移量是否为4(即f  − 4为0)。如果是这样,我们将打印数据字符串格式化程序,因此我们需要运行241次数据字符串及其格式化程序,而不是在此偏移量运行一次。代码非常简单:如果f  -4为非零,则 用一对零替换f -4和4本身;否则,将f -4和4本身替换为零。然后,无论哪种情况,都弹出顶部堆栈元素。现在,我们在堆栈上有一个高于f的数字,可以是4(如果要将此迭代打印241次)或0(如果要仅打印一次)。

(
    ((((((({})){}){}{})){}{}){}){}
    ()
)

这是一种有趣的Brain-Flak / Mini-Flak常数;这里的长线代表数字60。您可能会因为缺乏而感到困惑,()通常在Brain-Flak常数中到处都是;这不是常规数字,而是教堂数字,它会将数字解释为重复运算。例如,此处显示的教堂数字60代表60个输入副本,并将它们全部组合为一个值。在Brain-Flak中,我们可以组合的唯一东西是正数,再加上加法运算,所以我们最终将堆栈顶部的60个副本相加,然后将堆栈顶部乘以60。

附带说明一下,您可以使用Underload数字查找器,它以Underload语法生成教堂数字,也可以在Mini-Flak中找到合适的数字。欠载数字(非零)使用操作“重复的顶部堆栈元素” :和“组合顶部的两个堆栈元素” *;无论是在脑高射炮存在这些行动,所以你只是翻译:)*{},前置{},并添加足以(在开始时平衡(这是使用主堆栈和工作组的一个奇怪的组合,但它的工作原理)。

这个特定的代码片段使用教堂数字60(实际上是“乘以60”片段)以及一个增量来生成表达式60 x  +1。因此,如果我们在上一步中得到4,则将为我们提供一个值241,或者如果我们有一个0,我们只得到1的值,即,这正确地计算了我们需要的迭代次数。

选择241并非偶然。它是一个被选择为a)大约等于程序最终结束的长度的值,并且b)1大于整数的4倍。在这种情况下,整数60表示的教堂数字往往更短,因为您在复制因子方面更具灵活性。该程序稍后包含填充以使长度精确到241。

{
    ({}(
        …
    )[{}()])
}{}

这是一个for循环,就像前面看到的那样,该循环只是简单地在其中运行代码几次,使其次数等于主堆栈的顶部(它消耗;循环计数器本身存储在工作堆栈中,但是绑定到程序的嵌套级别,因此,除了for循环本身之外,其他任何东西都无法与之交互)。这实际上运行了数据字符串及其格式化程序1或241次,并且由于我们现在从主堆栈中弹出了用于控制流计算的所有值,因此我们可以在其顶部使用格式,以备要使用的格式化程序。

(␀␀!S␠su! … many more comment characters … oq␝qoqoq)

这里的评论并非完全没有兴趣。一方面,有两个Brain-Flak命令;所述)在端部被自然作为节目工作的各部分之间的过渡,故该方式的副作用产生(在开始手工加入到平衡它(尽管注释内的长度,把一个注释内一个()命令仍然是一个()命令,所以它所做的只是加1数据串的返回值和它的格式,这东西for循环完全忽略)。

更值得注意的是,注释开头的NUL字符显然没有任何偏移(即使+8和-4之间的差异不足以将a (转换为NUL)。这些都是纯填充,可将239个元素的数据字符串最多增加241个元素(它们很容易为自己付出:在计算所需的迭代次数时,生成1 vs. 239而不是1 vs. 241将花费两个以上的字节。 )。NUL用作填充字符,因为它具有最低的代码点(使数据字符串的源代码更短,因此输出速度更快)。

{}({}())

删除最上面的堆栈元素(我们正在使用的格式),在下一个元素(要格式化的程序部分的最后一个字符,即要打印的第一个字符)后面加1。我们不再需要旧的格式(新的格式隐藏在工作堆栈中);并且该增量在大多数情况下是无害的,并且'将数据字符串格式化程序的源表示形式的一端更改为a ((这是堆栈上的要求,下次运行该格式化程序时,才需要格式化数据字符串本身)。我们需要在outro或intro中进行这样的转换,因为强制每个数据字符串格式化程序元素(都将使其变得更加复杂(因为我们需要先关闭(,然后再撤消其作用),并且我们会以某种方式需要产生一个额外的(地方,因为我们只有差不多 241格式的副本,而不是所有241(所以它是最好的,像无害的字符'是一个缺少)。

(({})(()()()()){})

最后,进行循环退出测试。主堆栈的当前顶部是下一次迭代所需的格式(刚从工作堆栈中返回)。这将它复制并添加8;结果值将在下次循环时被丢弃。但是,如果仅打印简介,则偏移量为-4,因此“下一次迭代”的偏移量为-8;-8 + 8为0,因此循环将退出,而不是随后继续进行迭代。


16

128,673,515周期

在线尝试

说明

Miniflak quines注定要变慢的原因是Miniflak缺乏随机访问权限。为了解决这个问题,我创建了一个代码块,该代码块接受一个数字并返回一个基准。每个数据都像以前一样表示单个字符,并且主代码一次只查询该块一次。这实际上是作为随机存取存储器的块。


此代码块有两个要求。

  • 它必须有一个数字,并且仅输出该字符的字符代码

  • 必须容易地在Brain-Flak中一点一点地复制查找表

为了构造此块,我实际上重用了我的证明Miniflak已完成Turing的方法。对于每个基准面,都有一个代码块,如下所示:

(({}[()])[(())]()){(([({}{})]{}))}{}{(([({}{}(%s))]{}))}{}

这将从堆栈顶部的数字中减去1,如果为零,%s则将数据推入其下方。如果从堆栈中以n开头,则每块都将大小减小1,因此您将获得第n个基准。

这是很好的模块化设计,因此可以很容易地由程序编写。


接下来,我们必须设置将内存实际转换为源的机器。这包括3个部分:

(([()]())())
{({}[(
  -Look up table-
 )]{})
 1. (({}[()])[(())]()){(([({}{})]{}))}{}{([({}{}(([{}]))(()()()()()))]{})}{}

 2. (({}[()])[(())]()){(([({}{})]{}))}{}{([({}{}
      (({}[(
      ({}[()(((((()()()()()){}){}){}))]{}){({}[()(({}()))]{}){({}[()(({}((((()()()){}){}){}()){}))]{}){({}[()(({}()()))]{}){({}[()(({}(((()()()()())){}{}){}))]{}){([(({}{}()))]{})}}}}}{}
      (({}({}))[({}[{}])])
     )]{}({})[()]))
      ({[()]([({}({}[({})]))]{})}{}()()()()()[(({}({})))]{})
    )]{})}{}

 3. (({}[()])[(())]()){(([({}{})]{}))}{}{([({}{}
     (({}(({}({}))[({}[{}])][(
     ({}[()(
      ([()](((()()[(((((((()()()){})())){}{}){}){})]((((()()()()())){}{}){})([{}]([()()](({})(([{}](()()([()()](((((({}){}){}())){}){}{}))))))))))))
     )]{})
     {({}[()(((({})())[()]))]{})}{}
     (([(((((()()()()){}){}()))){}{}([({})]((({})){}{}))]()()([()()]({}(({})([()]([({}())](({})([({}[()])]()(({})(([()](([({}()())]()({}([()](([((((((()()()())()){}){}){}()){})]({}()(([(((((({})){}){}())){}{})]({}([((((({}())){}){}){}()){}()](([()()])(()()({}(((((({}())())){}{}){}){}([((((({}))){}()){}){}]([((({}[()])){}{}){}]([()()](((((({}())){}{}){}){})(([{}](()()([()()](()()(((((()()()()()){}){}){}()){}()(([((((((()()()())){}){}())){}{})]({}([((((({})()){}){}){}()){}()](([()()])(()()({}(((((({}){}){}())){}){}{}(({})))))))))))))))))))))))))))))))))))))))))))))))
     )]{})[()]))({()()()([({})]{})}{}())
    )]{})}{}

   ({}[()])
}{}{}{}
(([(((((()()()()){}){}())){}{})]((({}))([()]([({}())]({}()([()]((()([()]((()([({})((((()()()()){}){}()){})]()())([({})]({}([()()]({}({}((((()()()()()){}){}){}))))))))))))))))))

机器由四个部分组成,这些部分从1开始到3依次运行。我已经在上面的代码中将它们标记为。每个部分还使用与编码相同的查找表格式。这是因为整个程序都包含在一个循环中,并且我们不想每次运行循环时都运行每个部分,因此我们使用相同的RA结构并每次查询所需的部分。

1个

第1节是一个简单的设置节。

程序将告诉第一个查询第1部分和基准点0。基准点0不存在,因此,它不返回该值,而是仅对每个基准点减少查询一次。这很有用,因为我们可以使用结果来确定数据数量,这在以后的章节中将变得很重要。第1节通过否定结果记录数据的数量,并查询第2节和最后一个数据。唯一的问题是我们不能直接查询第2节。由于还有另一个递减,我们需要查询一个不存在的节5。实际上,每次我们查询另一个节中的节时都是这种情况。我将在我的解释中忽略这一点,但是,如果您正在查看代码,请记住5意味着返回一个部分,而4意味着再次运行相同的部分。

2

第2节将数据解码为构成数据块后面代码的字符。每次期望堆栈显示为:

Previous query
Result of query
Number of data
Junk we shouldn't touch...

它将每个可能的结果(从1到6的数字)映射到六个有效的Miniflak字符((){}[])之一,并使用“我们不应该碰的垃圾”将其放置在数据数之下。这为我们提供了一个类似的堆栈:

Previous query
Number of data
Junk we shouldn't touch...

从这里我们需要查询下一个数据,或者如果我们已经查询了所有数据,请移至第3节。上一个查询实际上不是发出的确切查询,而是查询减去该块中的数据数量。这是因为每个基准都会使查询递减一个,因此查询的结果会很混乱。为了生成下一个查询,我们添加数据数量的副本并减去一个。现在我们的堆栈看起来像:

Next query
Number of data
Junk we shouldn't touch...

如果我们的下一个查询为零,那么我们已经读取了第3节中所需的所有内存,因此我们再次向查询中添加了数据数量,并在堆栈顶部打了一个4,以移至第3节。如果下一个查询不为零,我们将5放到堆栈上以再次运行第2节。

3

与第3节一样,第3节通过查询我们的RAM来构成数据块。

为简洁起见,我将省略第3节的工作原理的大部分细节。它几乎与第2节相同,除了不是将每个数据转换成一个字符,而是将每个数据转换成代表其在RAM中条目的冗长代码块。完成第3节后,它告诉程序退出循环。


在循环运行之后,程序只需要按一下quine的第一位即可([()]())(()()()()){({}[(。我通过以下代码来实现此目的,这些代码实现了标准的Kolmogorov复杂度技术。

(([(((((()()()()){}){}())){}{})]((({}))([()]([({}())]({}()([()]((()([()]((()([({})((((()()()()){}){}()){})]()())([({})]({}([()()]({}({}((((()()()()()){}){}){}))))))))))))))))))

我希望这是清楚的。如果您对任何事情感到困惑,请发表评论。


这个过程大约需要多长时间?它在TIO上计时。
Pavel

@Pavel我不会在TIO上运行它,因为那样会非常慢,我确实使用了TIO所使用的解释器(红宝石)。在我可以访问的旧机架服务器上运行大约需要20分钟。在Crain-Flak中大约需要15分钟,但是Crain-Flak没有调试标志,因此如果不在Ruby解释器中运行它,就无法对它进行评分。
小麦巫师

@Pavel我再次运行它并计时。30m45.284s使用ruby解释器需要在一台相当低端的服务器(大约相当于一个普通的现代台式机)上完成。
小麦巫师
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.