【问题标题】:How to write an enumeration of all computable functions?如何编写所有可计算函数的枚举?
【发布时间】:2009-11-25 14:54:30
【问题描述】:

动机:我希望能够在没有一阶函数的语言中使用玩具函数式编程,方法是使用自然数而不是函数。

通用函数是一个函数 f : N -> (N -> N),相当于 f : N * N -> N 枚举所有可能的可计算函数。换句话说,有一个数 k 使得 f(k) 是平方函数,有一个数 j 使得 f(j) 是第 n 个素数函数等等。

要编写这样的函数,可以使用任何图灵完备的语言(编程语言编译器、lambda 演算、图灵机..​​....)并枚举所有程序。我希望不仅允许评估,还允许对加法、合成、柯里化等功能进行操作。例如,给定两个函数 f,g 的索引,我想知道函数 f+g 或由 g 组成的 f 的索引是什么。这将允许“玩具函数式编程”。

编写这样的代码库的好方法是什么?我不是在寻找难以计算 10 阶乘的简约 Turing tarpit,我也不想编写高级编译器。它应该具有一些基本功能,例如添加和编写循环的可能性,但仅此而已。

欢迎使用所有高级语言的解决方案。伪代码、Haskell 和 Python 是首选。您可以假设任意精度算术。不允许使用eval 或类似名称。

澄清:枚举函数将由所有 partial recursive (computable) 组成 - 这包括不会在某些输入上停止的函数。在这种情况下,通用功能将挂起;当然这是不可避免的。另请参阅:m 递归函数 - http://en.wikipedia.org/wiki/Μ-recursive_function

【问题讨论】:

  • 因为您似乎需要一些帮助并轻推以接受答案。最好的是 Pascal Cuoq 的stackoverflow.com/questions/1797457/…。 hirschhornsalz的最后一个也不错。我能理解你很难接受。

标签: language-agnostic math functional-programming theory computation-theory


【解决方案1】:

你想要的叫做解释器。

首先,任何具有您想要的属性的枚举都不适合您想要在前 2^32 甚至前 2^64 整数中操作的有趣函数。所以你需要更大的整数,分配在内存中的某个地方并通过指针引用。

那么为什么不在任何现有语法中使用表示程序的字符(字符串)数组呢? 如果这让您满意,可以将这样的字符串视为整数。要计算f1()+f2() 的函数的编号是由(f1 的表示)、“+”和(f2 的表示)组成的字符串。你明白了……

这种方法没有的是函数表示的唯一性,这可能暗示在您的问题中(我不确定)。我可以肯定的是,表示的唯一性与对函数表示的简单甚至可计算的组合操作是不相容的——例如,如果不是这种情况,就会有一个简单的解决方案来解决停止问题。

【讨论】:

  • 同意 - 表示的唯一性是不可能的。可以使用树来表示解析的字符串,而不是使用字符串;内部存储并不那么重要。问题是,我应该允许解释器拥有哪些功能?
  • 正确。仅(x -> 1501 * x + 67) 形式的函数就有超过 2^64 个,你永远不知道哪一个会变得有用。
  • sdcvvc,现在你好像在问一个编程语言设计问题,没有提供任何要求。
  • 不仅有超过 2^64 个 (x -> 1501 * x +67) 形式的函数,而且还有无数个 (x-> n) 形式的函数。您确实需要对要描述的功能进行一些限制。
  • @Jason Orendorff:我提出的唯一要求是:(1) 图灵完备 (2) 使用自然数尽可能简单地实现;如果表现力水平可以与 BASIC 或 Floop 相媲美(en.wikipedia.org/wiki/BlooP_and_FlooP)就足够了。任何设计都可以。 @Liberalkid:所有可计算的;在递归同构之前,只有一种可能的解决方案,即 Myhill 同构定理。
【解决方案2】:

虽然枚举某些语言中所有可能的表达式并不难,但您无法将这些限制为那些表示终止函数的表达式。

但如果您对终止不感兴趣,使用组合子(为了有用而加入一些算术原语)可能是最好的方法,因为您避免以这种方式引入变量名。

【讨论】:

    【解决方案3】:

    正如 Pascal 所说,您需要的是解释器,但可以做得更好:直接将处理器用作解释器。

    将数字 N(例如,作为一些大的 int 数组)直接提供给缓冲区并将此缓冲区作为机器码执行。

    对于您的计算机能够执行的每个可能的功能,都存在一个 N。不幸的是。不是每个 N 都是有效的程序(这不是要求的)或终止程序(这是不可能的)。

    另一方面,此功能将生成魔兽世界、Microsoft Office 17(包括 Service Pack 6)和 Windows 9 等宝石。

    【讨论】:

    • 不确定 MS office 17 是不是一颗宝石,但您可以原谅这一次。 +1
    【解决方案4】:

    这不是一个简单的问题。我想你必须从一个能够一个一个生成所有函数的函数生成器开始。这将导致枚举。

    既然你必须处理多个无尽的维度......让我们考虑一下。

    让我们将问题简化为具有 n 个参数的函数和基本操作 +、-、*、/。
    让我们只用一个操作来构建所有函数:

    一个+一个
    a + b
    一个 - 一个
    a - b
    一个*一个
    a * b
    一个/一个
    a/b

    我想很容易看出其中一些函数更有意义,因为其他一些函数可能相等,但至少,它是一个可以通过循环生成的映射。

    现在在下一次迭代中,可以轻松地添加到这些功能中的每一个

    • 包含所有操作的现有参数之一
    • 所有操作的第三个参数

    之后,您将获得大量功能,您可以对其重复第二步。

    由于 是一个估计所有更复杂函数的函数,如 sin 和 log(泰勒级数),这些也应该包含在这个函数空间中。

    这有帮助吗?随意编辑这篇文章!

    只需重新阅读您的帖子。如果您想枚举所有编程函数而不仅仅是数字一次,我想它会更复杂。我想那么通过压缩函数的源并将zip文件视为一个大数字来使用映射“函数数字”是有意义的。反过来,你可以尝试解压缩任何数字,看看它是否会创建一个有用的功能 :-) 但我猜你会有很多数字,甚至不是 zip 文件。

    但它会满足您的要求,即每个函数都有一个代表它的数字:-)

    【讨论】:

      【解决方案5】:

      您可以使用任何编程语言,这样您就可以确定某物是否是程序,并按字典顺序列出所有程序。为了避免至少一点组合爆炸,您可以以规范化的形式分配用户定义的名称(变量、函数等)。显然,这将产生大量的功能,而且要挑选出哪些功能真正有用并不容易。任何自动修剪方法都会排除您实际需要的某些功能,或者无法将组合爆炸修剪到足够有用的程度,或者两者兼而有之。

      这样做的另一个缺点是从数字到函数非常困难:很难找到比枚举大约四百千万亿个函数更好的方法来找到函数 433,457,175,432,167,463。

      另一种方法是通过将符号映射到数字并有效地将它们连接起来,将函数编码为数字。

      假设符号为 +、-、:=、==、

      一旦你掌握了这个,函数之间所有可能的关系都将可以表示为算术关系,但这是一个极其复杂的关系,在实践中很难做到。

      简而言之,虽然这在理论上是可行的,但在实践中就太笨拙了。用您选择的非功能性语言为功能性语言编写解释器可能会更容易。

      【讨论】:

      • 你对“4 位字符编程语言”的定义不是 .. 汇编吗?
      【解决方案6】:

      要编写这样一个函数,可以采取 任何图灵完备的语言 (编程语言编译器,lambda 微积分,图灵机..​​....)和 枚举所有程序

      我不太确定这是否可以做到……感觉这与图灵-教会论点背道而驰。为了枚举所有程序,首先您需要一个算法来确定哪些程序是有效的,哪些不是,这是不可能的......除非您不关心这一点并允许您的语言中出现错误的程序。

      但也许正式系统的Godelization 可以帮助你...我会尝试使用 Lisp,将代码作为数据会有很大帮助。

      【讨论】:

      • 是的,这是不可能的,但是所有编程语言都以相同的方式处理它(对吗?),通过允许一些“无效”程序(例如不会终止的程序)同时拒绝那些有明显错误的程序.符合给定语法的程序集总是可数的……对吧?
      • 确定哪些程序有效哪些无效并非不可能(好吧,也许在 C++ 中,但不是在简单的图灵机语言中)。不可能决定哪些程序停止,哪些不停止。但是您不需要知道,为了运行每个停止的程序 - 您所做的是运行程序 1 的第一步,然后运行程序 2 的第一步,然后运行 ​​1 中的第 2 步,然后运行 ​​3 中的第 1 步, 2 之 2、1 之 3 等。如果一个程序在自己运行时停止,那么它会在以这种方式运行时停止。显然你需要大量的内存,不过......
      • ...所以另一种方法是依次运行前一百万个程序的前一百万步,然后重新开始并运行前两百万个程序的前两百万步依次类推。您不必存储每个程序的状态,只需为您感兴趣的程序,标记已停止。当然,这需要很多时间,因为您正在重新工作。但是,如果您正在运行存在的每个程序,那应该是可以预料的 :-)
      • 无论如何,关键是您可以枚举有效程序,只需确保选择一种易于确定程序是否有效的语言。使用合适的沙盒虚拟机;一个指令集,其中任何未明确定义为操作的东西都是无操作的;而 jump-out-of-bounds 意味着“停止”,每个字节序列都是一个有效的程序。简单 :-) 这与评估所有可计算函数不同,因为(a)“所有程序”包括非暂停程序,以及(b)如果将程序用作函数,那么它是否暂停取决于输入。
      • 对于 fortran 来说至关重要的是,可数字母表的有限序列集是可数的。程序是有限字母表(例如 ASCII 或 Unicode)的有限序列,因此更好。如果程序可以无限长,那么就会有无数个(即超过 Aleph_0),就像实数一样。但他们不能,所以没有。
      【解决方案7】:

      不确定我是否理解。但有一件事 - 你不能枚举所有可能的可计算函数。简短的回答:因为否则会有一个通用的防病毒软件。长答案:因为如果存在这样的枚举,那么您将在该集合中还有计算枚举本身的函数。就像罗素悖论一样。

      您的问题的不同答案是 - 您想要“列出”所有可能的可计算函数;为此,您可能希望将它们表示为素数,并将它们的组合用作乘法。这将保证唯一性。因式分解将为您提供相反的功能。

      【讨论】:

      • 对于“枚举”和“可计算”的通常定义,您可以枚举所有可计算函数。修复一个通用图灵机,从那些用长度为 1 的初始功能区计算的函数开始,然后移动到用长度为 2 的初始功能区计算的那些函数,......计算枚举的函数在某个地方有什么关系在枚举中?
      • 另外,不幸的是函数组合不是可交换的,而乘法是。
      • 不可能枚举所有递归函数。可以枚举所有部分递归的。
      • -1:答案仍然错误。所有部分递归函数的枚举包括所有递归函数,包括枚举部分递归函数的(可计算的)函数。图灵的 Entscheidungsproblem 告诉我们,对于这个序列中的哪些函数是可计算的,没有递归测试。
      猜你喜欢
      • 1970-01-01
      • 2012-12-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-10-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多