c语言图灵机算法,图灵机的时间化简概述(3/4)

3.3 软件层次的化简

3.3.1 算法层次的化简

算法实际上已经在比较小的粒度上映射到图灵机的状态转换函数。一个设计良好的算法,对于大部分输入,在计算的过程中,能够生成较短且具有正确输出的格局序列。

目前的研究表明,对于P类问题,可以将指数级算法化简为多项式级算法;将高阶多项式级算法化简为低阶多项式算法或同阶的但是具有更小的常量因子的算法。对于NP类问题,可以将枚举算法化简为启发式的剪枝算法;将求精确解的算法化简为计算速度更快的求近似解的算法。在大多数情况下,算法层次的化简对于解决具体问题具有决定性的意义。也就是说,如果使用了一个设计很糟的算法,即使在其它方面做很多的优化,也不能获得令人满意的计算速度。

3.3.2 编程语言层次的化简

针对不同领域的问题,研究者提出并实现了很多种程序设计语言。比如,在科学计算领域的Fortran语言,在系统编程领域的C语言,在大规模编程领域的C++语言等等。各种语言为着特定的领域而设计,因此,在特定领域具有其它语言没有的优势。

3.3.3 编译器层次的化简

编译器层次的化简,又称为编译器优化,是指,在给定某个算法的源程序实现的前提下,对于特定的目标机器生成尽可能优的目标代码。

编译器优化的理由至少包括:

源程序是面向程序员的,有一些代码方便程序员,但是对于计算本身来说是冗余的(内联函数,释放后指针置0)。

源程序抽象级别一般较高,离机器的具体执行过程比较远。在编译过程中,代码一步一步地向底层代码转换,越来越接近机器的底层实现(指令集机器),就可以更多地利用机器底层信息进行优化。

“机械式转换”生成的目标代码一般会有很多冗余的部分。

因为编译器优化是在图灵机已经基本确定的情况下进行的化简(由于算法实现和目标机器已经基本决定了图灵机的状态转换函数),所以,相对算法层次的化简,编译器优化的程度是很有限的。该层次的化简可以大概分为三类:消除冗余的计算(消除冗余)、编译时执行部分计算(执行计算)和将某些子计算过程替换成等价的更快的计算过程(替换化简)。

以状态依赖图为模型可以很好的说明编译器优化。在程序执行的过程中,存在状态依赖。程序的最终输出依赖于它的上一个状态,进而依次依赖于以前的状态,最终依赖于程序的输入和初始化后的状态。这种状态依赖对应着生命期被分割后的数据依赖,可以用有向无环图(DAG,

Directed Acyclic Graph)来表示。

比如,对于图1所示的例子,对应的数据依赖图DAG如图2所示,其中箭头从一个子计算指向它所依赖的子计算或数据。如果对整个程序构造一个完整的DAG,那么,对DAG的一个拓扑排序,可以对应到一个状态转换序列。该状态转换序列在很小的粒度上对应到图灵机计算的一个完整的格局序列。图6给出了化简后的最终代码。

消除冗余

在DAG中,查询无入边的节点。如果该节点代表的子计算不影响最终的输出结果,那么,该计算就是冗余的和可以消除的。消除该节点进行迭代查询,可以消除更多的冗余计算。比如,首先节点13中c的值不影响输出,所以影响c的计算是冗余的;接着,因为节点12中j的值不影响输出,所以影响j的计算也是冗余的。化简后的DAG如图3所示。

这类优化包括活跃分析和死代码删除、类型优化、位宽分析和数值范围分析等。

活跃分析和死代码删除是指,通过数据流信息,如果分析出一段代码对于程序的输出没有影响,那么这段代码就可以删除。

类型优化是对活跃分析的细化,是指,如果一个值具有较大的类型(比如int类型),但是并不需要使用其高字节(比如127不需要使用一个字节),那么可以将这个值得类型修改成较小的类型(比如char类型)。这样,对该值的访问代码就只需要访问较少的字节。

位宽分析是对类型优化的细化,通过分析一个变量的值的可能域,给该变量分配尽可能紧凑的存储空间。

数值范围分析是对位宽分析的细化,通过分析一个变量的值的可能域,进而分析其在机器表示上可能发生变化的位,从而节省冗余的计算。

执行计算

在DAG中,如果子计算可以在编译时完成,就执行该计算。比如,因为节点3中i的值是可以在编译时计算的,可以得到i的值为6;接着,可以将节点3中的i可以替换成它的常量值。化简后的DAG如图4所示。

这类优化包括常量折迭、常量传播、拷贝传播等。

常量折迭是指,如果一个表达式中的所有子表达式的值都是编译时可确定的,就可以在编译时将该表达式的值计算出来,从而节省最终代码执行时所需要的时间。

常量传播是指,如果一个变量的值在某处被使用,并且该变量的值在此处是编译时可确定的,就可以在编译时将此处使用替换成对应的常量值。

拷贝传播是指,如果一个变量的值在某处被使用,并且可以在编译时确定这个变量的值是否来源于另一个变量,如果是,就可以将该处使用替换成对另一个变量的使用。

替换化简

在DAG中,对节点进行了共享,实际上进行了子计算共享的化简。比如,因为计算节点10中b的值时,发现在计算节点9中a的值时已经得到了x+z的值,所以可以直接将a的值赋给b,从而节省计算。这时,节点9中的a、节点10中的b与节点11中的x的值是相等的,因此,这三个变量的使用都可以用a来代替。因为移位操作可能比加法操作更快,所以当一个变量加到自身时,可以改成将该变量左移一位。此外,DAG因为揭示了资料依赖,所以可以为并行计算提供信息。化简后的DAG如图5所示。在图5中,节点5和节点8的计算是可以并行的。

这类优化包括公共子表达式、循环优化、代数化简、窥孔优化、并行优化等。

公共子表达式是指,如果一个表达式出现多次,并且可以在编译时确定每次出现的计算结果是相等的,那么,就可以一次计算出该表达式的值,并用该值替换表达式的所有出现。

循环优化是指,可以将一些循环不变量外提到循环外面,这样,就可以避免在每次循环时都计算一个循环不变量,造成计算冗余。

代数化简是指,可以根据代数恒等公式,将代价较高的表达式替换成代价较低且效果等价的新的表达式。

窥孔优化是指,可以根据机器指令的特点,将代价较高的代码段替换成代价较低且效果等价的新的代码段。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
图灵机是一种理论模型,用于描述计算机算法的工作原理。它由英国数学家Alan Turing在1936年提出,被认为是计算机科学的基础之一。图灵机包括一个无限长的纸带、一个读写头和一套规则。纸带上可以写入符号,并且根据规则进行读写头的移动和符号的修改,从而模拟计算过程。 在Matlab中,你可以使用编程语言来模拟图灵机的行为。你可以定义纸带、读写头以及规则,并通过代码来实现图灵机的操作和计算。Matlab提供了丰富的数学和算法库,可以方便地实现图灵机的模拟。 以下是一个单的示例代码,演示如何在Matlab中实现一个单的图灵机: ```matlab % 定义图灵机的初始状态 tape = repmat('0',1,100); % 初始纸带,全部填充为0 head_position = 1; % 初始读写头的位置 state = 'A'; % 初始状态 % 定义图灵机的规则 rules = struct(); rules.A_0 = struct('write','1','move','R','next_state','B'); rules.A_1 = struct('write','0','move','L','next_state','B'); rules.B_0 = struct('write','0','move','R','next_state','A'); rules.B_1 = struct('write','1','move','L','next_state','B'); % 执行图灵机的计算 for i = 1:1000 % 运行1000步 current_symbol = tape(head_position); % 获取读写头当前位置的符号 rule = rules.(sprintf('%s_%c',state,current_symbol)); % 根据当前状态和符号获取规则 % 执行规则 tape(head_position) = rule.write; % 写入新的符号 if strcmp(rule.move,'R') % 移动读写头 head_position = head_position + 1; else head_position = head_position - 1; end state = rule.next_state; % 更新状态 end disp(tape) % 输出最终的纸带内容 ``` 这只是一个单的示例,你可以根据自己的需要扩展和修改代码来实现不同的图灵机模拟。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值