【问题标题】:Code generation by genetic algorithms通过遗传算法生成代码
【发布时间】:2011-08-09 15:04:45
【问题描述】:

进化编程似乎是解决许多优化问题的好方法。这个想法很简单,实现起来也没有问题。

我想知道是否有任何方法可以进化地使用 ruby​​/python 脚本(或任何其他语言)创建程序?

这个想法很简单:

  1. 创建程序群
  2. 执行遗传操作(轮盘赌选择或任何其他选择),创建继承最佳程序的新程序等。
  3. 循环点 2 直到找到满足我们条件的程序

但还是有几个问题:

  1. 如何表示染色体?例如,染色体的一个细胞应该是一行代码吗?
  2. 如何生成染色体?如果它们是代码行,我们如何生成它们以确保它们在语法上是正确的等等?

可以生成的程序示例:

创建将 N 个数字作为输入并返回其均值作为输出的脚本。

如果有人尝试创建此类算法,我将很高兴看到任何链接/来源。

【问题讨论】:

  • 如果生成的程序之一会擦除驱动器可能会很有趣。当然,您需要一些方法来沙箱化,然后在打开潘多拉魔盒时要小心。我相信有一本书(虽然记不起名字了),其中这样一个程序最终会演变成这样一种方式,它将控制世界上所有的机器并开始杀死人类;)
  • 一本书?这个想法在廉价的科幻小说中被打死了。
  • 我试过一次。在程序变得具有自我意识后,它接管了打印机并使用激光射击建筑物中的每个人。
  • 什么是适应度函数?这些进化方法需要一种方法来计算它们与某些标准的匹配程度。
  • Jivlain 的答案是您想要标记为答案的答案

标签: algorithm code-generation genetic-algorithm genetic-programming evolutionary-algorithm


【解决方案1】:

语言不是问题。不管是什么语言,你都必须定义一些更高级别的突变,否则将需要很长时间才能学习。

例如,由于任何 Ruby 语言都可以根据文本字符串定义,因此您可以随机生成文本字符串并对其进行优化。最好只生成合法的 Ruby 程序。但是,这也需要很长时间。

如果您尝试构建一个排序程序并且您有“交换”、“移动”等高级操作,那么您成功的机会就会高得多。

理论上,一群猴子无限地敲打打字机,就能输出莎士比亚的所有作品。在实践中,这不是一种实用的文学写作方式。仅仅因为遗传算法可以解决优化问题并不意味着它很容易,甚至不一定是一个好方法。

【讨论】:

  • “理论上,一群猴子无限地敲打打字机,就能输出莎士比亚的所有作品。”关键词是“理论上”和“无限时间”。即使假设我们忽略空格、标点符号和大写,猴子通过随机击键得到一条线,说“成为或不成为,这就是问题”的概率大约是 26^30 中的 1 = 2.8E42 中的 1 个。如果你有 100 亿只猴子每秒输入一行,那么在 100 亿年中你得到那一行的机会仍然只有 9e14 中的 1。
  • @Jay 和 Larry,这就是为什么遗传算法仅适用于连续适应度函数的原因。如果您对适应度的唯一测试是字符串 x 是否等于字符串 y,那么遗传算法/程序无法确定字符串 x 是否更接近字符串 y。这就是迈克尔所指出的:您可以将一个程序发展为与另一个程序完全相同(逐个字母),但您不能逐个功能地发展它,因为没有渐进状态......该程序要么工作要么不't(例如抛出异常)。
  • 如果您知道所需的最终状态是什么,并以某种增量方式将每个新迭代与所需的最终状态进行比较,那么可以肯定,最终您会到达那里。但这需要知道所需的最终状态并能够确定某物是更近还是更远。如果你能做到这一点,为什么不为所需的最终状态编写代码呢?我将不得不阅读一些引用的文章,也许这里有一些我遗漏的东西,但我只是没有看到它。
【解决方案2】:

可以做到,但对于大多数类型的应用程序来说效果很差。

遗传算法仅在适应度函数连续时才有效,即您可以确定当前人群中的哪些候选人比其他候选人更接近解决方案,因为只有这样您才能从一代到下一代获得改进。当我有一个遗传算法在我的适应度函数中有一个强权重非连续分量时,我很难学到这一点。它支配了所有其他方面,并且因为它是非连续的,所以没有逐渐向更大的适应度前进,因为在这方面几乎正确的候选人并不被认为比完全不正确的候选人更适合。

不幸的是,程序的正确性是完全不连续的。一个在 A 行因错误 X 停止的程序是否比在 B 行因错误 Y 停止的程序更好?您的程序可能距离正确只有一个字符,但仍会因错误而中止,而返回恒定硬编码结果的程序至少可以通过 一个 测试。

这甚至没有涉及代码本身在修改下不连续的问题......

【讨论】:

  • 你是说遗传编程不存在吗? en.wikipedia.org/wiki/Genetic_programming - 这种方法目前在大量搜索问题中使用得相当成功
  • @JohnIdol:我的印象是,这让学者们为有可能写出有趣的论文而高兴得泪流满面,但几乎没有产生任何实际结果都没有的实际结果琐碎的或受限于特定的问题类别,这些类别适合于适当的适应度函数。
  • 由于我不是学者,我可以理解这可能是您的印象(因为在我研究这些东西之前我也有同样的想法),但是有很多情况下基因编程解决了很多问题比标准遗传算法更有效地获得更好的结果。例如,这是将这种方法应用于元胞自动机 [citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.55.7754] 的第一个著名示例,它的日期为 1996 年。从那时起我们已经走了很长一段路。
  • @JohnIdol:这就是维基百科文章所说的,但我注意到很多链接到的项目似乎停滞不前。至于你链接的那篇论文,我想说它证明了我的观点:适应度函数很方便地连续,正在进化的程序非常简单(布尔函数甚至图灵完备吗?),它解决的问题纯粹是学术相关的.
  • 这个问题没有纯粹的学术相关性,因为 CA 同步在数字传输、密码学和其他方面的噪声容限中具有直接应用(即便如此,我看不出纯粹的学术相关性会如何产生任何影响)区别,因为我们谈论的是通用程序)。由于我没有参加十字军东征或其他任何活动,因此我不会再推动这一点,但是如果您有兴趣在进一步发表演讲之前了解更多信息,则可以使用该材料。
【解决方案3】:

正如您所说,遗传算法的最大卖点是非常简单。他们没有最好的性能或数学背景,但即使你不知道如何解决你的问题,只要你能把它定义为一个优化问题你就能把它变成进入GA。

程序并不真正适合遗传算法,因为代码不是很好的染色体材料。我见过有人用(更简单的)机器代码而不是 Python 做了类似的事情(尽管它更像是一个生态系统模拟,然后是一个 GA 本身),如果你使用自动机 / LISP 或类似的东西来编码你的程序,你可能会有更好的运气那个。


另一方面,考虑到 GA 的魅力,以及基本上每个看着他们的人都会问同样的问题,我敢肯定已经有人在某个地方尝试过 - 我只是不知道他们中是否有人成功.

【讨论】:

  • ..数以千计的人成功编写了 GA,这些 GA 成功进化了计算机程序(= 遗传编程,GP),可以与人类编码的解决方案竞争或胜过 :-)
  • 哦,顺便说一句:从完全随机的“程序”的原始池中看到有意义的行为只是纯粹的乐趣;-)
  • 只是一点点守时。遗传算法确实具有数学背景(甚至不是那么容易):它们最大化适应度函数,并且通过交叉和变异避免局部最优。
【解决方案4】:

祝你好运。

当然,您可以编写一个“突变”程序来读取程序并随机添加、删除或更改一些字符。然后你可以编译结果,看看输出是否比原来的程序好。 (但是我们定义和衡量“更好”。)当然,99.9% 的结果将是编译错误:语法错误、未定义的变量等。当然其余的大部分都是非常不正确的。

尝试一些非常简单的问题。比如说,从一个程序开始,它读取两个数字,将它们相加,然后输出总和。假设目标是一个读取三个数字并计算总和的程序。这样的程序有多长和多复杂当然取决于语言。假设我们有一些非常高级的语言,让我们只用一行代码就可以读取或写入一个数字。那么启动程序只有4行:

read x
read y
total=x+y
write total

达到预期目标的最简单的程序是这样的

read x
read y
read z
total=x+y+z
write total

所以通过随机突变,我们必须添加“read z”和“+z”,包括空格和换行符,总共 9 个字符。让我们简化我们的变异程序,假设它总是准确插入 9 个随机字符,保证它们在正确的位置,并且它从 26 个字母加上 10 个数字加上 14 个特殊字符的字符集中进行选择 = 50 个字符。它会选择正确的 9 个字符的几率是多少? 50^9 中的 1 = 2.0e15 中的 1。 (好吧,如果不是“read z”和“+z”,而是插入“read w”和“+w”,程序就可以工作,但是我假设它神奇地插入了正确数量的字符,这很容易并且总是把它们插在正确的地方。所以我认为这个估计还是很慷慨的。)

2.0e15 中的 1 个概率很小。即使程序每秒运行一千次,并且您可以快速测试输出,机会仍然只有每秒 2.0e12 中的 1 次,或每小时 5.4e8 中的 1 次,每天 2.3e7 中的 1 次。让它运行一年,成功的机会仍然只有 62,000 分之一。

即使是一个中等能力的程序员也应该能够在十分钟内做出这样的改变?

请注意,更改必须至少以正确的“数据包”形式出现。也就是说,如果一个突变产生了“reax z”,那离“read z”只有一个字符,但它仍然会产生编译错误,因此会失败。

同样添加“read z”但将计算更改为“total=x+y+w”是行不通的。根据语言的不同,您可能会收到未定义变量的错误,或者充其量它会有一些默认值,如零,并给出不正确的结果。

我想,您可以对增量解决方案进行理论化。也许一个突变添加了新的读取语句,然后一个未来的突变更新了计算。但如果没有计算,额外的阅读是毫无价值的。如何评估程序以确定额外的读取是“朝着正确方向迈出的一步”?我认为做到这一点的唯一方法是让智能体在每次突变后阅读代码,看看变化是否朝着预期目标前进。如果你有一个聪明的设计师可以做到这一点,那一定意味着他知道期望的目标是什么以及如何实现它。此时,只进行所需的更改而不是等待它随机发生会更有效率。

这是一个非常简单的程序,语言非常简单。大多数程序是什么,成百上千行,所有这些都必须协同工作。编写工作程序的任何随机过程的几率都是天文数字。

在一些非常专业的应用程序中,可能有一些方法可以做类似的事情,在这些应用程序中,您并不是真正进行随机突变,而是对解决方案的参数进行增量修改。就像,我们有一个公式,其中包含一些我们不知道其值的常数。我们知道对于一小部分输入的正确结果是什么。所以我们对常数进行随机更改,如果结果更接近正确答案,则从那里更改,如果不是,则返回之前的值。但即便如此,我认为进行随机更改很少有成效。尝试根据严格的公式更改常量可能会更有帮助,例如从 1000 开始更改,然后 100 再 10 等。

【讨论】:

  • @Jay,您可以通过具有符合编译器的严格变异规则来进化一个程序来编译,这不一定是困难的部分......困难的部分是进化一个可以工作的程序(即不'在运行或执行一些有用的事情时不会崩溃)。否则你已经做了一个很好的分析。
  • 我想了一下,但我的帖子太长了。您可以让突变生成标记而不是单个字母,即完整的关键字、函数名称、变量名称等。但是仍然很难有任何类似于随机过程的东西仍然保证语法正确性。比如,你可以让它只生成完整的关键字,比如“if”和“else”,而不是“ib”和“els”。但即使是像确保所有 ELSE 都与 IF 配对这样的简单规则,也需要对程序结构进行分析,这将使我们远离“随机突变”。
  • @Jay,“规则”真的很容易执行。树中的每个节点都有给定数量的子节点,例如 IFGT(如果大于)有四个子节点:IFGT(A,B,CaseA,CaseB)。子元素可以是其他函数,例如 ADD(A,B)、SWAP(A,B,) 或 MULT(A,B)... 所以现在你可以编写一个程序:IFGT(1,2,MULT(5 ,6),ADD(8,2)) 其中 1、2、5、6 和 8 都是终端节点。现在您可以轻松地改变该程序,但是如果您添加非数字节点,例如 SUBSTR(S1,S2),事情会变得更加困难。所以制作一个可以编译的程序并不难...
  • 但现在您显然正在从“随机突变”转向“人工智能”,这是一个完全不同的问题。当您开始说您将要构建程序的更高级别地图时,确定在地图中的任何给定点哪些类型的节点是有效的,管理这些节点的操作结果之间的相互关系等,这是不再是“通过随机突变和过滤器编写程序”,而是在“突变器”中构建了很多智能。
  • @Jay 程序的层次不比编译器高。 DNA就是一个很好的例子:A只连接T,C只连接G,即每个DNA“节点”只能连接到一组特定的节点,这与GP中的节点相同。这可能对您有所帮助:从技术上讲,您可以用最少的 CPU 指令集表达任何计算机程序:ADD、OR、XOR。您几乎可以保证它们的任何组合将始终“编译”,但即使它确实编译,您也无法测量它是否更接近于打开文件(即文件不能打开 50%或 75% 开放)。
【解决方案5】:

如果您确定要这样做,您需要genetic programming,而不是遗传算法。 GP 允许您发展树状结构的程序。你要做的就是给它一堆原始操作(while($register)、read($register)、increment($register)、decrement($register)、divide($result $numerator $denominator)、print , progn2(这是 GP 所说的“顺序执行两个命令”))。

你可以产生这样的东西:

progn2(
  progn2(
    read($1)
    while($1
      progn2(
        while($1
          progn2( #add the input to the total
            increment($2)
            decrement($1)
          )
        )
        progn2( #increment number of values entered, read again
          increment($3)
          read($1)
        )
      )
    )
  )
  progn2( #calculate result
    divide($1 $2 $3)
    print($1)
  )
)  

您将使用它与实际解决方案的接近程度作为您的适应度函数。其中有一个问题,无论如何您都必须按照传统方式计算*。然后有一些东西可以将其翻译成(您选择的语言)的代码。请注意,由于其中存在潜在的无限循环,因此您必须在一段时间后切断执行(无法解决停止问题),并且它可能无法正常工作。嘘。另请注意,我提供的代码将尝试除以零。

*有一些方法可以解决这个问题,但通常不会太远。

【讨论】:

  • 请注意,您可以指望 GP 实际产生的任何解决方案都比这更加模糊:)
  • GP 进化了计算机程序。这些传统上在内存中表示为树结构,但也通常表示为线性结构(一系列命令)或图形。无论哪种表示对您有用 - 没有什么能将 GP 限制为树结构。
  • you have to calculate that traditionally anyway 不一定,尤其是当您试图找到一个反函数时(这通常比原始函数更难计算)。例如,如果您正在进化一个平方根函数f(x),您可以通过f(x*x)x 的接近程度来衡量适应度。
【解决方案6】:

我只想给你一个建议。我不知道你会有多成功,但也许你可以尝试用基因编程进化一个core war 机器人。你的健身功能很简单:让机器人在游戏中竞争。您可以从众所周知的机器人开始,也许还有一些随机的,然后等待看看会发生什么。

【讨论】:

    【解决方案7】:

    这是很有可能的,@Jivlain 在他的(很好的)回答中正确地指出,遗传编程是您正在寻找的(而不是简单的遗传算法)。

    遗传编程是一个尚未接触到广泛受众的领域,部分原因是@MichaelBorgwardt 在他的回答中指出的一些复杂性。但这些只是复杂性,不可能做到这一点远非事实。对该主题的研究已经进行了 20 多年。

    Andre Koza 是这方面的主要研究人员之一(请查看他的 1992 work),他展示了 as early as 1996 在某些经典计算问题(例如进化程序元胞自动机同步)。

    这是 Koza 和 Poli 于 2003 年发送的一个很好的 Genetic Programming tutorial

    对于最近的参考资料,您可能想看看A field guide to genetic programming (2008)。

    【讨论】:

    • 遗传编程教程链接失效了,你知道它从那以后移到哪里了吗?
    • @Wolf,我修复了链接。几年前重新设计geneticprogramming.com 时,它可能被破坏了。说到这一点,genericprogramming.com 本身就是另一个很好的资源——它有很多关于不同 GP 技术的可访问的高级摘要。
    【解决方案8】:

    自从提出这个问题以来,遗传编程领域已经取得了一些进展,并且已经进行了一些额外的尝试,以在传统遗传编程的树结构之外的配置中进化代码。这里只是其中的几个:

    • PushGP - 旨在发展模块化功能,如人类编码人员使用的那样,该系统中的程序将所有变量和代码存储在不同的堆栈上(每个变量类型一个)。通过从堆栈中推送和弹出命令和数据来编写程序。
    • FINCH - 一个进化 Java 字节码的系统。这已被用于改进游戏代理。
    • 各种算法已经开始发展 C++ 代码,通常包含纠正编译器错误的步骤。这有好坏参半的结果,但并非完全没有希望。 Here's an example
    • Avida - 代理使用非常简单的汇编代码演化程序(主要是布尔逻辑任务)的系统。基于较旧的(且用途较少的)Tierra

    【讨论】:

      猜你喜欢
      • 2015-02-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-05-09
      • 2012-11-19
      • 2013-02-15
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多