zoukankan      html  css  js  c++  java
  • 神奇的自产生程序,兼谈人工生命

    最近读到冯·诺依曼的《Theory of Self-Reproducing Automata》的中译本,被自复制自动机理论深深吸引了!生命是什么?这本书让我对生命有了新的认识。 热力学第二定律是宇宙的死亡法则:系统的熵总是趋于增加,系统总是由有序趋向无序,由有形趋向混沌,最后终结于热寂。宇宙万物都逃不过这条法则,唯有生命例外。通过与外界交换能量,生命可以保持内在的有序。只有生命可以违抗热力学第二定律。 热力学与信息论有着内在联系:一个系统越有序,它包含的信息就越多;一个系统越无序,它包含的信息就越少。信息总是沿着减少的方向流动。只有生命可以抗拒这个规律:生命可以“创造”出信息。“创造”是生命之所以为生命的本质。 生命的另一个神奇之处是,他们自己包含了自己的全部信息,他们自己可以产生一个跟自己一样的新生命。唯有生命可以做到自己创造自己。 冯·诺依曼认为,一个简单的系统是无法抗拒热力学第二定律的,这样的系统只会越来越趋向于无序,能量会向更低的方向流动,信息会越来越少。只有当系 统复杂到某一个程度的时候,不妨设为临界点 C ,一旦越过了这个临界点,系统可以自己创造自己,这样系统就可以逆热力学第二定律而上,并且变得越来越复杂。 这里的临界点 C 跟不少人提出的“技术奇异点”是一样的:人们认为,当人工智能达到了这样一种程度,即它们可以自己创造自己的时候,这就是人工智能真正觉醒的时候。“奇异 点”就是机器可以自己造出自己的时候,“奇异点”之后,机器将进化,并且变得越来越复杂,超出人类所能理解的范围,它们在某种程度上已经具备了生命的特 质,那将是人工智能的时代。 总而言之,生命之所以区别于世界万物,就在于生命可以包含自己的全部信息。所以下面就是我们的问题了: 问题(自产生程序):编写一个程序,不读取任何输入,只把自己的源代码输出。 这个问题是个非常本质的问题,跟使用什么编程语言无关(不要想到使用反射之类的东西)。 试想,如果要输出自己的源代码,那么,显然,程序中应该有“print …”语句。但 print 什么出来呢?如果硬要写的话就会变成:

    1. print "print "print ......""

    最后是一个无限循环。 一般地,我们知道,如果程序 A 能产生程序 B ,那么 A 必须包含 B 的全部信息,而且应该比 B 的信息还多,因为还要包含额外的打印语句。也就是说,一般情况下,信息是减少的。而这个自产生程序,自己要包含自己的全部信息,从某种程度上已经具有生命的意味了。 下面列出一些自产生程序及其思路。 需要注意的是,使用编程语言本身的反射功能或者读取文件等做法都被视为 cheating ,比如这样的 bash 脚本:

    1. #!/bin/sh
    2. cat $0

    或者像这样的 javascript :

    1. function a() { console.log(a.toString(), "a()"); } a()

    因为这些程序没有体现出自产生程序的递归和自指特性,或者结果严重依赖于编程语言的具体实现。 输出源代码在该语言中的转义 Python :

    1. s = "'s = ' + repr(s) + ' print(' + s + ')'"
    2. print('s = ' + repr(s) + 'nprint(' + s + ')')

      Lua 5.1 :  

    1. s = "string.format('s = %q print(%s)', s, s)"
    2. print(string.format('s = %qnprint(%s)', s, s))

    另一个 Lua 版:

    1. s = "s = %q
    2. print(string.format(s, s))"
    3. print(string.format(s, s))

    Scala :

    1. def e(s: String) = (""" + s.replace("", "\").replace(""", """) + """)
    2. val s = """"def e(s: String) = (""" + s.replace("\", "\\").replace(""", "\"") + """)""" + " val s = " + e(s) + " println(" + s + ")""
    3. println("""def e(s: String) = (""" + s.replace("", "\").replace(""", """) + """)""" + "nval s = " + e(s) + "nprintln(" + s + ")")

    用某种方法 encode 源代码,使之不包含引号,然后还原出源代码 Bash :

    1. #!/bin/sh
    2. s='x22#!/bin/shns=x27$sx27necho $(echo -e $s)x22'
    3. echo "#!/bin/sh
    4. s='$s'
    5. echo $(echo -e $s)"

    Lua 5.2 使用 load():

    1. s = "a,q,b=string.char(39),string.char(34),string.char(92) return a..'s = '..q..a..'..s..'..a..q..b..'nprint('..a..'..load(s)()..'..a..')'..a"
    2. print('s = "'..s..'"nprint('..load(s)()..')')

    Scala :

    1. val s = "%22val+s+%3D+%5C%22%22+%2B+s+%2B+%22%5C%22%5Cnprintln%28%22+%2B+java.net.URLDecoder.decode%28s%2C+%22UTF-8%22%29+%2B+%22%29%22"
    2. println("val s = "" + s + ""nprintln(" + java.net.URLDecoder.decode(s, "UTF-8") + ")")

    使用 eval :在 eval 的字符串中引用自己 Lua load() 的另一种用法:

    1. s = "print(string.format('s = %q load(s)()', s))" load(s)()

    js 的 eval():

    1. s = "q = String.fromCharCode(34); console.log('s = ' + q + s + q + '; eval(s)')"; eval(s)

    使用语言中的更强的转义机制 类似上面的第二种,但不用引号。 Lua 的 long string :

    1. x = [["x = [".."["..x.."]".."]nprint("..x..")"]]
    2. print("x = [".."["..x.."]".."]nprint("..x..")")

    Scala 的三引号:

    1. val s = """"val s = """" + s + """"nprintln(" + s + ")""""
    2. println("val s = """" + s + """"nprintln(" + s + ")")

    使用 C 的宏 先执行传入的参数,再把参数变成字符串。 gcc :

    1. #define p(a) int main(){a;puts("p("#a")");return 0;}
    2. p(puts("#define p(a) int main(){a;puts("p("#a")");return 0;}"))

    至于它们是怎么实现的,就留给读者自己琢磨了。自产生程序也称为 Quine ,可以参考 Quine Page 。

  • 相关阅读:
    sqlplus时报Linux-x86_64 Error: 13: Permission denied
    thrift之TTransport层的缓存传输类TBufferedTransport和缓冲基类TBufferBase
    Java实现 蓝桥杯 算法提高 新建Microsoft world文档
    Java实现 蓝桥杯 算法提高 新建Microsoft world文档
    Java实现 蓝桥杯 算法提高 快乐司机
    Java实现 蓝桥杯 算法提高 快乐司机
    Java实现 蓝桥杯 算法提高 队列操作
    Java实现 蓝桥杯 算法提高 队列操作
    Java实现 蓝桥杯 算法提高 文本加密
    Java实现 蓝桥杯 算法提高 合并石子
  • 原文地址:https://www.cnblogs.com/u0mo5/p/3973734.html
Copyright © 2011-2022 走看看