计算模型

news/2024/12/17 3:02:11/文章来源:https://www.cnblogs.com/qixingzhi/p/18611409

当我们提到“计算(calculation)”这个词时,指的是“回答一个数学问题”。这个数学问题可以是“求两个正整数的乘积”,“求出一个线性方程组的所有解”,“给一列数字排序”,“给定一个无向图求最大独立集的大小”等等。在计算这些问题的时候,可以有很多不同的方法。对于第一个问题,我们可以依据“乘法本质是加法”来计算, 例如\(3\times 5\)我们只需做\(5\)\(+3\)操作;也可以用列竖式的方法,我们发现在数字比较大的时候这是比还原成加法更快的策略;还可以用快速傅里叶变换,这在数字非常大的时候是比列竖式还要快的方法。对于第二个问题,可以用高斯消元法,这在中国古代就已经发现了;现代人们还提出了比高斯消元更快的算法。由此可见,对于同一个问题有不同方法,不同的方法效率不同,流传千年的方法都还可能被改进。但是对于第三个问题,人们提出了一系列算法(快速排序、归并排序等),并证明这已经是效率最高的方法不可能再被改进了。对于第四个问题,人们发现除了暴力枚举所有的子集似乎不存在更快的算法了。由此可见,人们不仅可以提出效率更高的计算方法,还可以证明不存在更快的计算方法。讨论“计算的效率”的学科就是“计算复杂性理论(Computational Complexity Theory)”。

问题与计算(Problem & Computation)

为了讨论计算的效率,我们首先需要形式化定义“问题”和“计算”。

什么是一个“数学问题”呢?一个数学问题就是给定一些输入,要求给出一个确定的输出(答案)。输入和输出都可以编码为二进制串,所以我们可以这样定义“问题(problem)”:一个问题是一个函数\(f:\{0,1\}^\ast \to \{0,1\}^\ast\)

那么,对一个问题\(f\)的“计算(computation)”就是对于每个合法的输入\(x\in \{0,1\}^\ast\),我们都要想尽办法求出答案\(f(x)\)。这里的“想尽办法”指的是物理世界所允许的所有办法,包括在纸上打草稿、用电路模拟等等。所以“计算”是一个含义非常广泛的概念,人们可以为它建立各种不同的模型,只要其中涉及的操作满足物理定律。

计算模型(The Computation Model)

图灵机(Turing Machine)

艾伦·图灵(Alan Turing)提出了以下计算模型,称为图灵机:

图灵机模型包含\(k\)条无限长的纸带,每条纸带上有一个可移动的读写头。可以假设每条纸带可以向左右无限延伸,纸带上的每一格可以标记符号(符号的种类必须是有限个,例如\(\{0,1\}\))。第一条纸带是只读的,初始时上面已经写好了输入串,称为输入带。其余纸带初始时为空,称为工作带(类比草稿纸)。图灵机运行时,可以改写当前每条纸带上的读写头指向的位置的符号;每个读写头进行完改写操作时,可以向左移动一格、向右移动一格或不动。所有读写头改写一次并移动一次的操作称为“运行一步”。在运行了有限步以后,如果最后一条纸带上的二进制串对应着问题的答案,计算就完成了。(所以,虽然我们假设了纸带是无限长的,但每次计算用到的纸带长度一定是有限的)

当输入为\(x\)时,如果采用一种策略使得图灵机运行\(n\)步以后能得到结果,就称存在一个算法计算\(f(x)\)的时间为\(n\)。计算\(f(x)\)所需要的时间往往是和符号串\(x\)的长度有关的。 如果存在一个算法,使得图灵机在给定输入\(x\)时总能在\(T(|x|)\)步内计算得到结果,就称算法计算\(f\)的时间复杂度(time complexity)为\(T\),其中\(|x|\)是二进制串\(x\)的长度,\(T\)\(\mathbb{N}\to\mathbb{N}\)的函数。

通用图灵机(The Universal Turing Machine)

根据以上图灵机的定义可以看出,一台图灵机就相当于我们现在所说的“一个算法”或“一段计算机程序”。一个算法或一段程序就是在给定输入下会以特定的方式移动图灵机的读写头,做特定的读写, 最后给出特定的输出。例如,我们可以设计一个算法专门用来计算两个数的和,或专门用来求解线性方程组。换言之,一个特定的图灵机实现一个特定的算法功能,这个功能是通过对图灵机的编程(控制读写头的移动和读写)来实现的。

不同的图灵机被编写了不同的程序,纸带条数也可能不同,用来实现不同的计算功能。艾伦·图灵(Alan Turing)最早注意到,存在一台图灵机,它可以模拟任何图灵机的计算。也就说,对于任意一台图灵机(一个算法),我们都可以在一台特定的图灵机上编程,使得它能够模拟这台图灵机的计算。这台图灵机称为“通用图灵机”。

首先,我们意识到任何一台图灵机都可以编码为一个01串。这是显然的,因为我们已经可以用自然语言描述一台图灵机的算法,只需把精确的描述语言编码成01串。这只需把图灵机的各个状态满足的读写头移动的函数编写成二进制串,就可以让不同的图灵机对应不同的01串。反过来,我们可以让任何一个01串都对应一台图灵机,只需让所有不满足我们编码规则的01串全都对应到一台最简单的在任何输出下都直接停机输出0的图灵机上。这样,我们就可以让任何一个01串唯一对应一台图灵机。由于我们可以让01串与自然数一一对应,所以我们认为我们可以让每台图灵机与自然数一一对应。这样,我们就可以依次枚举世界上所有的图灵机,记为\(M_0,M_1,\cdots,M_n,\cdots\)

如果存在通用图灵机,那么它满足:通用图灵机是上面列出的图灵机中的一个,它接收一个二元组\((x,\alpha)\)(对应的二进制串)作为输入,输出图灵机\(M_\alpha\)在输入\(x\)下的输出。下面我们证明确实能构造这样一台通用图灵机\(U\)\(U\)有3条纸带(1条输入带,2条工作带)。当它接收到输入\((x,\alpha)\)时,开始用第一条工作带模拟\(M_\alpha\)的计算。\(M_\alpha\)\(k\)条工作带,我们把这\(k\)条工作带在读写头处对齐,然后压缩成一条纸带——压缩后的纸带后每个位置上变成了一个关于符号的\(k\)元组。很容易把每个\(k\)元组按照特定规则再次编码成01串,所以确实可以把这\(k\)条纸带的所有信息压缩在\(U\)的第一条工作带上。每当\(M_{\alpha}\)上的读写头执行了读写时,我们就在\(U\)的第一条工作带上对应地模拟这一修改;如果\(M_\alpha\)的读写头发生了移动,我们就遍历整条工作带进行修改,使得这些压缩后的纸带依然是在读写头处对齐的。 当\(M_\alpha\)给出了输出后,我们把输出誊写到\(U\)的第二条工作带上。这样我们就完成了模拟,可见通用图灵机是存在的。

然而我们注意到,每一次读写头的移动都会消耗通用图灵机用\(O(kT)\)的复杂度来模拟(\(T\)\(M_\alpha\)的复杂度,它和纸带的有效长度是同一量级的)。这样一个\(O(T)\)的算法就需要\(O(T^2)\)的时间在通用图灵机上模拟。是否存在一个更好的模拟方法,能实现更好的模拟的复杂度呢?人们发现了一个巧妙的方法,可以在\(O(T\log T)\)的复杂度下实现模拟。我们在通用图灵机上引入冗余符号\(\times\)。对于(未压缩的)每条纸带,假设这条纸带上有\(s\)个有效字符,我们可以补充空字符\(\square\)使其长度恰好为\(2^t-1\)。以读写头为起点(\(0\))把这\(2^t-1\)依次向右排列,我们可以把纸带看作从\(0\)开始向左右延申的长度倍增的块(长度为1、长度为2、长度为4、……)。这样\(0\)及其右侧有\(t\)个块,左侧对称地也有\(t\)个块。在左侧的块中所有位置填上冗余字符\(\times\)。我们在读写头移动时,维护这样一个对称平衡的性质:如果右侧的块是满的(没有\(\times\)),那么左侧的块是空的 (全是\(\times\));如果右侧块是空的,那么左侧块是满的;如果右侧块恰好为一半,那么左侧块也恰好为一半;位置0始终不为空。初始时,纸带是满足对称平衡的。当纸带整体需要向左移动一格时,假设原来是对称平衡的,我们查看位置0左侧的第一个块,如果该块是空的或是只填了一半,那么用位置0的字符填入使得该块变成一半满或全满;加入该块已经满了,那么再考虑该块左边的块,如果它半满或为空则可以把右侧的块填入,否则再考虑它左边的块……依次类推,我们把位置0左移了一格并保持左侧的所有块都处于空、半满或全满的状态。接着只需按照对称平衡的原则重新分配右侧的字符即可。这样的模拟方法看似移动一次也最坏可能要\(O(T)\)的复杂度,但我们可以从整体分析:观察到,如果某次挪动向左右涉及到了第\(r\)个块,那么中间的\((r-1)\times 2\)个块一定是半满的。这就意味着当第\(r\)个块被移动以后,需要至少再经过\(2^r\)次读写头移动操作才会再次挪动第\(r\)个块。所以对于一个复杂度为\(O(T)\)的算法,第\(r\)个块最多被移动\(O(\dfrac{T}{2^r})\)次,移动一次的复杂度为\(O(2^r)\)。所以总体来看每个块被移动的复杂度为\(O(T)\),总共有\(O(\log T)\)个块,综上移动的总复杂度为\(O(T\log T)\)

不可计算性(Uncomputability)

所以现在我们知道,任何一个计算过程(或一个程序)都可以在一台三条纸带的通用图灵机上完成。一个自然的问题是,是不是所有的问题\(f:\{0,1\}^\ast\to \{0,1\}^\ast\)都可以在这台图灵机上完成计算呢?这里的完成计算是指图灵机在每个输入上都能运行有限步之后停机并输出正确的答案。

在讨论通用图灵机的过程中,我们发现“图灵机”本身可以被编码为一个二进制串。这意味着一台图灵机可以作为一个“输入数据”传入另一台图灵机。利用这一特点,我们可以构造一些这样的\(f\)来说明,并不是所有问题都是图灵机可计算的,存在一些问题是图灵机不可计算的。

不可计算问题的构造和证明实数不可数、哥德尔不完备性定理很类似,都使用了对角线方法(diagonal method)。我们可以列一张表格,第\(i\)行第\(j\)列内填入图灵机\(M_i\)在输入\(j\)后的输出\(M_i(j)\)。我们可以利用这张表格的对角线构造一个问题:\(f(\alpha)=\left\{\begin{matrix}0,&M_\alpha(\alpha)=1\\1,&\text{otherwise}\end{matrix}\right.\)。假设这个问题可以用通用图灵机\(U\)来计算,假设通用图灵机对应着编号\(u\)的图灵机\(M_u\),那么\(M_u(u)=f(u)\)。而如果\(M_u(u)=1\),则\(f(u)=0\);如果\(M_u(u)\neq 1\),则\(f(u)=1\)。矛盾。所以不存在这样的\(U\)。这意味着\(f\)是不可计算的。

下面这个称为“停机问题(The Halting Problem)”的函数也是不可计算的:\(\text{HALT}(x,\alpha)=\left\{\begin{matrix}1,&\text{if }M_\alpha(x)\text{ halts}\\0,&\text{otherwise}\end{matrix}\right.\)。假设存在一台图灵机\(M_h\)可以计算停机问题,那么我们可以把上一段中的\(f\)等价地写作:\(g(\alpha)=\left\{\begin{matrix}0,&M_h(\alpha,\alpha)=1\land M_{\alpha}(\alpha)=1\\1,&\text{otherwise}\end{matrix}\right.\)。我们可以构造一台图灵机\(U\),输入\(\alpha\)时首先计算\(M_h(\alpha,\alpha)\)。若\(M_h(\alpha,\alpha)=1\),则\(M_\alpha(\alpha)\)会停机输出结果,我们能够判断\(M_\alpha(\alpha)\)是否等于1,是则输出0否则输出1;若\(M_h(\alpha,\alpha)\neq 1\),输出0。这样我们就得到\(f\)是可计算的,矛盾。因此\(M_h\)不存在,也即停机问题是图灵机不可计算的。

丘奇-图灵论题(Church-Turing Thesis)

除了图灵机以外,人们还提出了很多别的计算模型。例如,丘奇(Church)提出了\(\lambda\)-calculus,哥德尔提出了递归函数(recursive functions)。然而人们逐渐发现,这些不同的计算模型其实都是等价的,也即模型A能计算的函数都能用模型B来计算,模型B能计算的函数也都能用模型A来计算。图灵机不可计算的函数用其他计算模型也不可计算。因此,我们不必谈论“图灵机可计算”或“递归函数可计算”这样依赖于计算模型的概念,而可以直接谈论“可计算”的概念,因为计算这一概念是独立于具体计算模型而存在的。

在所有这些等价的计算模型中,图灵机模型是最简单直接的,因为它不依赖于任何形式化系统和数学假设,而是把计算直接和物理过程联系起来。图灵机的定义强调计算的物理可实现性。

我们已经看到有函数是图灵机不可计算的,但这样的函数是极其不自然的,几乎可以肯定它在物理世界也是不可计算的。到目前为止,我们还没有发现任何“直觉上可计算”的函数是图灵机不可计算的——“物理可实现的计算都是图灵机可计算的”,这就是丘奇-图灵论题。我们可能永远无法证明这一点,因为我们无法严格定义什么是“物理可实现的计算”或“直觉上可行的计算”,但丘奇-图灵论题已经成为计算理论的共识。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/854072.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

python 安装talib ta-lib

ta-lib的whl轮子文件下载地址在这里 https://github.com/cgohlke/talib-build/releases根据自己电脑版本下载 pip install ./ta_lib-0.5.1-cp312-cp312-win_amd64.whl

第7次作业

第7次作业 一、实验名称:安装PKI平台熟悉PKI 证书申请、证书签发等操作过程 二、实验目的 通过这个实验加深对非对称加密、数字签名、数字证书等一些基本概念的了解 三、实验原理与理论基础 数字证书:采用公钥密码体制,即利用一对互相匹配的密钥进行加密、解密。每个用户拥有…

java 选择排序,涵盖工作原理、算法分析、实现细节、优缺点以及一些实际应用场景

选择排序的详细解析更深入地探讨选择排序的各个方面,包括其工作原理、算法分析、实现细节、优缺点以及一些实际应用场景。 动画演示 1. 基本概念选择排序是一种简单的比较排序算法。它的核心思想是将数组分为两个部分:已排序部分和未排序部分。每一轮从未排序部分找到最小(…

云浮知识付费系统课程数据

本文将深入探讨在线教育与知识付费行业的现状与发展趋势,并以实际数据和案例加以说明,特别关注教育和软件行业所面对的机遇和挑战。文章将以专业知识背景和技术应用为导向,展示在线学习平台如何在提高效率和实现个性化教育方面扮演关键角色。图源 www.tuzhi.ltd随着互联网和…

【Java脚本】如何在java程序中调用python脚本

以python代码来实现将pdf文档转换为word文档为例,我们想要在Java程序中调用Python脚本,并实现文件的传递与返回,可以采用以下几种方法: 1.使用ProcessBuilder或Runtime.getRuntime().exec() Java可以通过ProcessBuilder或Runtime.getRuntime().exec()来启动一个外部进程,因…

java 冒泡排序,涵盖背景、算法步骤、代码示例、复杂度分析、优化方式及其优缺点

冒泡排序的背景知识冒泡排序是一种简单的排序算法,由于其简单易懂的特点,它通常被用作教学目的。冒泡排序在最坏情况下的效率并不高,但在某些特定条件下,它的表现可以相对较好。下面是更深入的细节。 动画演示1. 算法步骤详解1.1 基本逻辑冒泡排序的核心逻辑是比较和交换。…

【DA梦数据库系列】SQL注入小记

一、判断DMSQL二、基础语法2.1 注释2.2 常用运算符三、函数特性3.1 进制转换3.2 字符串截取3.3 字符串拼接3.4 其他四、SQLi 获取数据4.1 基本信息4.2 查询全局变量4.3 查询系统表4.4 查询视图五、联合查询注入六、报错注入6.1 除0错误6.2 溢出错误七、布尔盲注八、时间盲注九、…

ABC384E题通过历程

在赛时的时候,我们写出了一份非常牛逼的代码: # include <bits/stdc++.h> using namespace std; #define int long long const int N = 510; int a[N][N]; int vis[N][N]; struct node {bool operator()(int a, int b) {return a > b;} }; int dx[] = {0,1,-1,0}; i…

java学习12.16

springboot+vue3前后端分离的新技术的大部分写项目时的明显bug已经找到问题并纠正,可以赶上旧技术的进度了。再写几个项目就能在速度上超越旧的技术。

11CSS属性-颜色表示-chrome调试-浏览器渲染

一、今日内容 首先对这些属性进行一个演练 想要深刻理解所有常用的CSS属性,最好先学会以下几个最基础最常用的CSS属性font-size 文字大小 color前景色(文字颜色) background-color背景色 width宽度 height高度二、对CSS常用属性进行演练 1、字体的默认大小是16px的还可以看到…

使用win10 wsl子系统将 rust 程序静态编译为linux可执行文件

chapter Ⅰ 事情起因 最近在学习rust, 想把一部分java服务迁移至rust编写,但由于公司服务器都是linux系统,所以在找windows下交叉编译为linux可执行文件的方法,把bing首页搜索的结果试了个遍都不行, 始终报错“musl-gcc”无法找到,搜遍全网都无法解决。 最终在我不断尝试的…

Spring Cloud 负载均衡配置 (@LoadBalanced)

一、负载均衡 目前是两种 Ribbon 和 spring-cloud-loadbalancer - Ribbon已经闭源 配置策略较多 :轮询,随机,权重,自定义 四种负载均衡方案 配置方式yml 注解- spring-cloud-loadbalancer属于springcloud,项目兼容性更好 配置策略: 更注重 自定义(默认为轮询) 配置方式注…