乌洛波罗斯(衔尾蛇)
不知道你有没有见过这个图案
这是”衔尾蛇”,许多古老的民族都有关于它的神话,柏拉图形容其为“一头在自我吞食状态的宇宙始祖生物“,衔尾蛇是”不死之身”,且“拥有完美的生物结构”。千百年来,衔尾蛇被赋予了数不清的意义,其中就包括较易理解的“循环”,“无限”,以及偏向神学概念的“生死循环”与“轮回”,还有现代科学意义上的“熵增”,甚至惊天动地的“宇宙真理”。
按照热力学第二定律,时间的前进是一种熵增的过程,如果宇宙是一个封闭的空间,那么随着时间的推移,“熵”,即混乱度会随着时间的推进而增大。就比如随着时间的进行,伽利略卫星可能破碎然后变成杂乱的木星环…
宇宙的未来似乎就是这样了,即所谓“热寂”,“故事的结局不是火,而是冰。”
但衔尾蛇为我们指出了世界的另一种可能——从无序到有序,衔尾蛇头尾循环,它的运行不曾有终点,只要时间足够,衔尾蛇就能回到先前的任一状态。
这不是胡话,试想一下,你拿起一瓶矿泉水扔向空中,总有或多或少的时候水瓶会直接立在地上。那么对无数瓶矿泉水进行这样的操作,总有或多或少的机会让所有水瓶经过抛掷后稳稳立在地上,就好像它们从来没被动过。
同样的,如果让10个数字不停的随机变化,经过足够长的时间后,总会有这10个数分别排列成1到10。
由此我们甚至可以粗略地说:如果一件事有可能发生,那么他就一定能发生。
猴子排序算法
这个算法原自埃米尔·博雷尔在1909年出版的《为未来竞争》一书中提到的“无限猴子定理“(又一说为出自安德雷·柯尔莫哥洛夫在1933年出版的《概率论》中的”零一律“)。该定理内容大致为:给一只猴子一台打印机,虽然这只猴子根本不识字,但会乱按,经过一段时间后,在它乱按出来的单词里总能找到一些至少看起来是有意义的部分,比如一两个简短的单词,由此可以推出:只要给它足够长的时间,猴子甚至能完整地写出一本莎士比亚全集。
所以,以无限猴子理论为基础的猴子算法的实质就是…乱排!
没错,就是纯粹的乱弄,只有当瞎猫撞见死耗子了才能排序成功。
下面我们来试着实现以下…
void algorithm(void)
{
int temp,index,buffer,mark,numbuffer[DATA_AMOUNT];
srand(time(NULL));
while(true)
{
// 乱序
for(int i = 0;i < DATA_AMOUNT;i++)
{
index = RANDOM(0, DATA_AMOUNT - 1);
temp = nums[index];
nums[index] = nums[i];
nums[i] = temp;
}
// 检查
mark = 0;
buffer = nums[1];
for(int i = 0;i < DATA_AMOUNT - 1;i++)
{
if(nums[i] > nums[i+1]) mark = 1;
}
if(!mark) break;
}
}
完整代码:
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <time.h>
#include <limits.h>
#define RANDOM(N,M) (rand() %((M)-(N)+1)+(N))
#define DATA_AMOUNT 10
#define ALGORITHM_NAME "猴子排序"
int nums[DATA_AMOUNT];
void algorithm(void);
int main(void)
{
for(int i = 0;i < DATA_AMOUNT;i++) nums[i] = RANDOM(0, 1000);
int start,end;
start = clock();
algorithm();
end = clock();
printf("algorithm \"%s\" used %f sec to process %d data\n",ALGORITHM_NAME,(float)(end - start)/CLOCKS_PER_SEC,DATA_AMOUNT);
for(int i = 0;i < DATA_AMOUNT - 1;i++)
{
if(nums[i] > nums[i+1])
{
puts("FAILED");
abort();
}
}
puts("PASSED");
return 0;
}
void algorithm(void)
{
int temp,index,buffer,mark,numbuffer[DATA_AMOUNT];
srand(time(NULL));
while(true)
{
// 乱序
for(int i = 0;i < DATA_AMOUNT;i++)
{
index = RANDOM(0, DATA_AMOUNT - 1);
temp = nums[index];
nums[index] = nums[i];
nums[i] = temp;
}
// 检查
mark = 0;
buffer = nums[1];
for(int i = 0;i < DATA_AMOUNT - 1;i++)
{
if(nums[i] > nums[i+1]) mark = 1;
}
if(!mark) break;
}
}
你别不信,这鬼算法真的能用:
猴子算法的存在意义
如你所见,猴子算法是一种非常看脸的算法,在最理想的条件下,猴子算法甚至能瞬间完成排序。但更多的时候,猴子算法的耗时趋近于无穷大。
猴子算法的意义更多的是理论上的而非应用上,它打破了排序算法时间复杂度的极限,在理想条件下猴子算法的时间复杂度甚至是 O ( 1 ) O(1) O(1),尽管这个常数通常会大得离谱。
[2] https://baike.baidu.com/item/%E6%97%A0%E9%99%90%E7%8C%B4%E5%AD%90%E5%AE%9A%E7%90%86/2221476
[3] https://baike.baidu.com/item/%E5%9C%86%E7%8E%AF%E4%B9%8B%E7%90%86/2698298
[4] https://zhuanlan.zhihu.com/p/216250712