神奇的自产生程序,兼谈人工生命
2013-12-26 21:19
204 查看
最近读到冯·诺依曼的《Theory of Self-Reproducing Automata》的中译本,被自复制自动机理论深深吸引了!生命是什么?这本书让我对生命有了新的认识。 热力学第二定律是宇宙的死亡法则:系统的熵总是趋于增加,系统总是由有序趋向无序,由有形趋向混沌,最后终结于热寂。宇宙万物都逃不过这条法则,唯有生命例外。通过与外界交换能量,生命可以保持内在的有序。只有生命可以违抗热力学第二定律。 热力学与信息论有着内在联系:一个系统越有序,它包含的信息就越多;一个系统越无序,它包含的信息就越少。信息总是沿着减少的方向流动。只有生命可以抗拒这个规律:生命可以“创造”出信息。“创造”是生命之所以为生命的本质。 生命的另一个神奇之处是,他们自己包含了自己的全部信息,他们自己可以产生一个跟自己一样的新生命。唯有生命可以做到自己创造自己。 冯·诺依曼认为,一个简单的系统是无法抗拒热力学第二定律的,这样的系统只会越来越趋向于无序,能量会向更低的方向流动,信息会越来越少。只有当系 统复杂到某一个程度的时候,不妨设为临界点 C ,一旦越过了这个临界点,系统可以自己创造自己,这样系统就可以逆热力学第二定律而上,并且变得越来越复杂。 这里的临界点 C 跟不少人提出的“技术奇异点”是一样的:人们认为,当人工智能达到了这样一种程度,即它们可以自己创造自己的时候,这就是人工智能真正觉醒的时候。“奇异 点”就是机器可以自己造出自己的时候,“奇异点”之后,机器将进化,并且变得越来越复杂,超出人类所能理解的范围,它们在某种程度上已经具备了生命的特 质,那将是人工智能的时代。 总而言之,生命之所以区别于世界万物,就在于生命可以包含自己的全部信息。所以下面就是我们的问题了: 问题(自产生程序):编写一个程序,不读取任何输入,只把自己的源代码输出。 这个问题是个非常本质的问题,跟使用什么编程语言无关(不要想到使用反射之类的东西)。 试想,如果要输出自己的源代码,那么,显然,程序中应该有“print …”语句。但 print 什么出来呢?如果硬要写的话就会变成:
print "print "print ......""
最后是一个无限循环。 一般地,我们知道,如果程序 A 能产生程序 B ,那么 A 必须包含 B 的全部信息,而且应该比 B 的信息还多,因为还要包含额外的打印语句。也就是说,一般情况下,信息是减少的。而这个自产生程序,自己要包含自己的全部信息,从某种程度上已经具有生命的意味了。 下面列出一些自产生程序及其思路。 需要注意的是,使用编程语言本身的反射功能或者读取文件等做法都被视为 cheating ,比如这样的 bash 脚本:
#!/bin/sh
cat $0
或者像这样的 javascript :
function a() { console.log(a.toString(), "a()"); } a()
因为这些程序没有体现出自产生程序的递归和自指特性,或者结果严重依赖于编程语言的具体实现。 输出源代码在该语言中的转义 Python :
s = "'s = ' + repr(s) + '\nprint(' + s + ')'"
print('s = ' + repr(s) + 'nprint(' + s + ')')
Lua 5.1 :
s = "string.format('s = %q\nprint(%s)', s, s)"
print(string.format('s = %qnprint(%s)', s, s))
另一个 Lua 版:
s = "s = %q
print(string.format(s, s))"
print(string.format(s, s))
Scala :
def e(s: String) = (""" + s.replace("\", "\\").replace(""", "\"") + """)
val s = """"def e(s: String) = ("\"" + s.replace("\\", "\\\\").replace("\"", "\\\"") + "\"")""" + "\nval s = " + e(s) + "\nprintln(" + s + ")""
println("""def e(s: String) = (""" + s.replace("\", "\\").replace(""", "\"") + """)""" + "nval s = " + e(s) + "nprintln(" + s + ")")
用某种方法 encode 源代码,使之不包含引号,然后还原出源代码 Bash :
#!/bin/sh
s='x22#!/bin/shns=x27$sx27necho $(echo -e $s)x22'
echo "#!/bin/sh
s='$s'
echo $(echo -e $s)"
Lua 5.2 使用 load():
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"
print('s = "'..s..'"nprint('..load(s)()..')')
Scala :
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"
println("val s = "" + s + ""nprintln(" + java.net.URLDecoder.decode(s, "UTF-8") + ")")
使用 eval :在 eval 的字符串中引用自己 Lua load() 的另一种用法:
s = "print(string.format('s = %q load(s)()', s))" load(s)()
js 的 eval():
s = "q = String.fromCharCode(34); console.log('s = ' + q + s + q + '; eval(s)')"; eval(s)
使用语言中的更强的转义机制 类似上面的第二种,但不用引号。 Lua 的 long string :
x = [["x = [".."["..x.."]".."]nprint("..x..")"]]
print("x = [".."["..x.."]".."]nprint("..x..")")
Scala 的三引号:
val s = """"val s = """" + s + """"nprintln(" + s + ")""""
println("val s = """" + s + """"nprintln(" + s + ")")
使用 C 的宏 先执行传入的参数,再把参数变成字符串。 gcc :
#define p(a) int main(){a;puts("p("#a")");return 0;}
p(puts("#define p(a) int main(){a;puts("p("#a")");return 0;}"))
至于它们是怎么实现的,就留给读者自己琢磨了。自产生程序也称为 Quine ,可以参考 Quine Page 。
print "print "print ......""
最后是一个无限循环。 一般地,我们知道,如果程序 A 能产生程序 B ,那么 A 必须包含 B 的全部信息,而且应该比 B 的信息还多,因为还要包含额外的打印语句。也就是说,一般情况下,信息是减少的。而这个自产生程序,自己要包含自己的全部信息,从某种程度上已经具有生命的意味了。 下面列出一些自产生程序及其思路。 需要注意的是,使用编程语言本身的反射功能或者读取文件等做法都被视为 cheating ,比如这样的 bash 脚本:
#!/bin/sh
cat $0
或者像这样的 javascript :
function a() { console.log(a.toString(), "a()"); } a()
因为这些程序没有体现出自产生程序的递归和自指特性,或者结果严重依赖于编程语言的具体实现。 输出源代码在该语言中的转义 Python :
s = "'s = ' + repr(s) + '\nprint(' + s + ')'"
print('s = ' + repr(s) + 'nprint(' + s + ')')
Lua 5.1 :
s = "string.format('s = %q\nprint(%s)', s, s)"
print(string.format('s = %qnprint(%s)', s, s))
另一个 Lua 版:
s = "s = %q
print(string.format(s, s))"
print(string.format(s, s))
Scala :
def e(s: String) = (""" + s.replace("\", "\\").replace(""", "\"") + """)
val s = """"def e(s: String) = ("\"" + s.replace("\\", "\\\\").replace("\"", "\\\"") + "\"")""" + "\nval s = " + e(s) + "\nprintln(" + s + ")""
println("""def e(s: String) = (""" + s.replace("\", "\\").replace(""", "\"") + """)""" + "nval s = " + e(s) + "nprintln(" + s + ")")
用某种方法 encode 源代码,使之不包含引号,然后还原出源代码 Bash :
#!/bin/sh
s='x22#!/bin/shns=x27$sx27necho $(echo -e $s)x22'
echo "#!/bin/sh
s='$s'
echo $(echo -e $s)"
Lua 5.2 使用 load():
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"
print('s = "'..s..'"nprint('..load(s)()..')')
Scala :
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"
println("val s = "" + s + ""nprintln(" + java.net.URLDecoder.decode(s, "UTF-8") + ")")
使用 eval :在 eval 的字符串中引用自己 Lua load() 的另一种用法:
s = "print(string.format('s = %q load(s)()', s))" load(s)()
js 的 eval():
s = "q = String.fromCharCode(34); console.log('s = ' + q + s + q + '; eval(s)')"; eval(s)
使用语言中的更强的转义机制 类似上面的第二种,但不用引号。 Lua 的 long string :
x = [["x = [".."["..x.."]".."]nprint("..x..")"]]
print("x = [".."["..x.."]".."]nprint("..x..")")
Scala 的三引号:
val s = """"val s = """" + s + """"nprintln(" + s + ")""""
println("val s = """" + s + """"nprintln(" + s + ")")
使用 C 的宏 先执行传入的参数,再把参数变成字符串。 gcc :
#define p(a) int main(){a;puts("p("#a")");return 0;}
p(puts("#define p(a) int main(){a;puts("p("#a")");return 0;}"))
至于它们是怎么实现的,就留给读者自己琢磨了。自产生程序也称为 Quine ,可以参考 Quine Page 。
相关文章推荐
- Linux基础系列-可执行程序的产生过程
- “WinMgmt.exe产生了错误,将会被Windows关闭,需要重新启动程序”的错误提示解决方法
- PHP程序漏洞产生的原因和防范方法
- 程序运行产生SIGABRT信号的原因
- 第十一周项目—阅读程序,说出在测试函数中不同情况的调用产生的结果(5 d)
- 让程序崩溃时产生coredump
- PHP程序漏洞产生的原因分析与防范方法说明
- 计算随机数边产生平方,平方根,自然对数的小程序
- PHP执行shell脚本运行程序不产生core文件的方法
- Cocos2d-x Eclipse下程序运行产生错误Effect initCheck() returned -1
- [Java 09 多线程] 线程是指一个进程在执行过程中可以产生更小的程序单元
- 让程序崩溃时产生coredump
- 关于PHP程序使用file_get_content()函数进行抓取PHP程序与smarty结合编译过程中产生的静态文件,抓取不了?连接超时?(地址映射)
- DLL中单例模式存在的非模态CPropertySheet对话框在程序关闭时产生内存泄漏
- WINDOWS如何调试Flash程序保存SWF其产生的LOG
- 小程序-----Java代码产生6个随机数,其中第一个不能为0
- SDI程序三元组的产生(VC2005)
- 编译.NET程序自动产生版本号的规则
- 程序崩溃时用widbg产生dump