热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

《编程原本》一2.3碰撞点

2.3碰撞点如果不知道定义而只能观察其行为,我们无法确定一个变换的一条特定轨道是否无穷,因为它完全可能在任意一点结束或者循环.如果知道一条轨道是有穷的,那就可以用一个算法来确定该轨

2.3 碰撞点

如果不知道定义而只能观察其行为,我们无法确定一个变换的一条特定轨道是否无穷,因为它完全可能在任意一点结束或者循环.如果知道一条轨道是有穷的,那就可以用一个算法来确定该轨道的形状了.因此,本章针对轨道的所有算法都有一个有关轨道有穷性的隐含前条件.
显然可以写一个简单而平凡的算法,让它保存已访问的每个元素,在每一步检查新遇到的元素是否曾经访问过.虽然可以用哈希技术来加快搜索,但这个算法至少需要线性的存储空间,而这种要求对于许多实际应用是不能接受的.还好,存在着只需要常量存储空间的算法.
下面的类比有助于理解这一算法.如果有一辆速度快的汽车和一辆速度慢的汽车在一条道路上行驶,当且仅当存在环路时快车将能追上慢车.如果没有环路,快车将比慢车先到达道路的终点.如果有环路,在慢车进入环路之后,快车一定会在环路中追上它.下面我们把这种直观想法从连续域转到离散域,还要小心地避免快车超过慢车.1
这一算法的离散版本基于找到快车遭遇慢车的点.变换f和起始点x确定的碰撞点(collisionpoint)就是那个唯一的y,它使
image

其中的n.0是满足这一条件的最小整数.这一定义给出了一个用于确定轨道结构的算法,它只需比较较快的迭代和较慢的迭代.如果要处理的是部分变换,那就必须给算法送一个定义空间谓词:

template
requires(Transformation(F) && UnaryPredicate(P) &&
Domain(F) == Domain(P))
Domain(F) collision point(const Domain(F)& x, F f, P p)
{
//前条件:p(x). f(x)有定义
if (!p(x)) return x;
Domain(F) slow = x; //slow=f0(x)
1.Knuth[1997,7页]将这一算法归功于RobertW.Floyd.
Domain(F) fast = f(x); //fast=f1(x)
// n ← 0(完成迭代)
while (fast != slow) { //slow=fn(x)∧fast=f2n+1(x)
slow = f(slow); //slow=fn+1(x)∧fast=f2n+1(x)
if (!p(fast)) return fast;
fast = f(fast); //slow=fn+1(x)∧fast=f2n+2(x)
if (!p(fast)) return fast;
fast = f(fast); //slow=fn+1(x)∧fast=f2n+3(x)
// nn + 1

}
return fast; //slow=fn(x)∧fast=f2n+1(x)
//后条件:返回终止点或碰撞点的值
}

我们将通过三个步骤来确立collisionpoint算法的正确性:(1)验证f不会被应用于定义空间之外的参数;(2)验证如果它终止,后条件一定满足;以及(3)验证它必定终止.
即使f是部分函数,它在这一过程中的使用也总是良定义的,因为fast的移动受到p调用的保护.而slow的移动不需要保护,这是由于f的规范性:slow和fast在同一轨道上,所以将f应用于slow时总有定义.
算法里的注释说明,如果经过n.0次迭代后fast变得等于slow,那就一定有fast=f2n+1(x)和slow=fn(x).进一步说,n就是满足这一条件的最小整数,因为对每个i如果没有环路,由于有穷性,p将最终返回假.如果有环路,slow最终将到达连接点(环路的第一个点).在程序里的循环语句开始处考虑当slow刚进入环路时从fast到slow的距离d,有0.d下面过程确定一条轨道是否终止:

template
requires(Transformation(F) && UnaryPredicate(P) &&
Domain(F) == Domain(P)) bool terminating(const Domain(F)& x, F f, P p)
{
//前条件:p(x). f(x)有定义
return !p(collision point(x, f, p));
}

有时我们知道一个变换或者是全的,或者从某个特定开始点出发的轨道是不终止的.对于这种情况,下面特殊版本的collisionpoint会很有用:

template
requires(Transformation(F)) Domain(F) collision point nonterminating orbit(const Domain(F)& x, F f)
{
Domain(F) slow = x; //slow=f0(x)Domain(F) fast = f(x); //fast=f1(x)
// n ← 0(完成迭代)
while (fast != slow) { //slow=fn(x)∧fast=f2n+1(x)slow = f(slow); //slow=fn+1(x)∧fast=f2n+1(x)fast = f(fast); //slow=fn+1(x)∧fast=f2n+2(x)fast = f(fast); //slow=fn+1(x)∧fast=f2n+3(x)
// nn + 1

}
return fast; //slow=fn(x)∧fast=f2n+1(x)
//后条件:返回碰撞点的值
}

要想确定环路的结构,即确定轨道的柄规模、连接点和环规模,就需要分析碰撞点的位置.当过程返回碰撞点时
image

其中n是slow走过的步数,2n+1是fast走过的步数.而且n=h+d这里的h是柄规模,0.d0是fast碰到slow时完成整个环的次数.由于n=h+d,所以2(h+d)+1=h+d+qc
通过化简得到
qc=h+d+1用下式表示h对c取模:h=mc+r其中0.r也就是
d=(q.m)c.r.10.d所以
d=c.r.1而且需要r+1步去完成整个环.这样,从碰撞点到连接点的距离就是e=r+1对环路轨道有h=0且r=0,从碰撞点到轨道开始点的距离是e=1由此可知,环路性质可以用下面过程检查:

template requires(Transformation(F)) bool circular nonterminating orbit(const Domain(F)& x, F f)
{
return x == f(collision point nonterminating orbit(x, f));
}
template requires(Transformation(F) && UnaryPredicate(P) && Domain(F) == Domain(P)) bool circular(const Domain(F)& x, F f, P p)
{
//前条件:p(x). f(x)有定义
Domain(F) y = collision point(x, f, p);return p(y) && x == f(y);
}

至此仍不可能知道柄规模h和环规模c.在知道碰撞点之后很容易确定后者,为此只需遍历环路一次并记录步数.要想确定h,先看看碰撞点的位置:
fh+d(x)=fh+c.r.1(x)=fmc+r+c.r.1(x)=f(m+1)c.1(x)
从冲突点走h+1步就到达了点f(m+1)c+h(x),它等于fh(x),因为(m+1)c对应于绕环m+1次.如果同时从x走h步并从碰撞点走h+1步,两者就会在连接点相遇.换句话说,x的轨道和过碰撞点1步的点在恰好h步后汇合,这一情况揭示了下面几个算法:

template requires(Transformation(F)) Domain(F) convergent point(Domain(F) x0, Domain(F) x1, F f)
{
// 前条件: (.n ∈ DistanceType(F))n.0∧fn(x0)=fn(x1)
while (x0 != x1) { x0 = f(x0);x1 = f(x1);
}
return x0;
}
template
requires(Transformation(F)) Domain(F) connection point nonterminating orbit(const Domain(F)& x, F f)
{
return convergent point(x,f(collision point nonterminating orbit(x, f)),f);
}
template requires(Transformation(F) && UnaryPredicate(P) && Domain(F) == Domain(P)) Domain(F) connection point(const Domain(F)& x, F f, P p)
{
//前条件:p(x). f(x)有定义
Domain(F) y = collision point(x, f, p);if (!p(y)) return y;return convergent point(x, f(y), f);
}

引理2.8如果两个元素的轨道相交,它们将有同样的环路元素.
练习2.2请设计一个算法,对于给定的变换和定义空间谓词,它能确定两个元素的轨道是否相交.
练习2.3convergentpoint的前条件保证它终止.请针对没有该条件,但已知在两点x0和x1的轨道上有共同元素的情况实现算法convergentpointguarded.



推荐阅读
  • 本文介绍了设计师伊振华受邀参与沈阳市智慧城市运行管理中心项目的整体设计,并以数字赋能和创新驱动高质量发展的理念,建设了集成、智慧、高效的一体化城市综合管理平台,促进了城市的数字化转型。该中心被称为当代城市的智能心脏,为沈阳市的智慧城市建设做出了重要贡献。 ... [详细]
  • 本文介绍了P1651题目的描述和要求,以及计算能搭建的塔的最大高度的方法。通过动态规划和状压技术,将问题转化为求解差值的问题,并定义了相应的状态。最终得出了计算最大高度的解法。 ... [详细]
  • 本文详细介绍了Java中vector的使用方法和相关知识,包括vector类的功能、构造方法和使用注意事项。通过使用vector类,可以方便地实现动态数组的功能,并且可以随意插入不同类型的对象,进行查找、插入和删除操作。这篇文章对于需要频繁进行查找、插入和删除操作的情况下,使用vector类是一个很好的选择。 ... [详细]
  • 本文讨论了clone的fork与pthread_create创建线程的不同之处。进程是一个指令执行流及其执行环境,其执行环境是一个系统资源的集合。在调用系统调用fork创建一个进程时,子进程只是完全复制父进程的资源,这样得到的子进程独立于父进程,具有良好的并发性。但是二者之间的通讯需要通过专门的通讯机制,另外通过fork创建子进程系统开销很大。因此,在某些情况下,使用clone或pthread_create创建线程可能更加高效。 ... [详细]
  • 本文介绍了Windows操作系统的版本及其特点,包括Windows 7系统的6个版本:Starter、Home Basic、Home Premium、Professional、Enterprise、Ultimate。Windows操作系统是微软公司研发的一套操作系统,具有人机操作性优异、支持的应用软件较多、对硬件支持良好等优点。Windows 7 Starter是功能最少的版本,缺乏Aero特效功能,没有64位支持,最初设计不能同时运行三个以上应用程序。 ... [详细]
  • 预备知识可参考我整理的博客Windows编程之线程:https:www.cnblogs.comZhuSenlinp16662075.htmlWindows编程之线程同步:https ... [详细]
  • 加密世界下一个主流叙事领域:L2、跨链桥、GameFi等
    本文介绍了加密世界下一个主流叙事的七个潜力领域,包括L2、跨链桥、GameFi等。L2作为以太坊的二层解决方案,在过去一年取得了巨大成功,跨链桥和互操作性是多链Web3中最重要的因素。去中心化的数据存储领域也具有巨大潜力,未来云存储市场有望达到1500亿美元。DAO和社交代币将成为购买和控制现实世界资产的重要方式,而GameFi作为数字资产在高收入游戏中的应用有望推动数字资产走向主流。衍生品市场也在不断发展壮大。 ... [详细]
  • ALTERTABLE通过更改、添加、除去列和约束,或者通过启用或禁用约束和触发器来更改表的定义。语法ALTERTABLEtable{[ALTERCOLUMNcolu ... [详细]
  • Java学习笔记之面向对象编程(OOP)
    本文介绍了Java学习笔记中的面向对象编程(OOP)内容,包括OOP的三大特性(封装、继承、多态)和五大原则(单一职责原则、开放封闭原则、里式替换原则、依赖倒置原则)。通过学习OOP,可以提高代码复用性、拓展性和安全性。 ... [详细]
  • 开发笔记:实验7的文件读写操作
    本文介绍了使用C++的ofstream和ifstream类进行文件读写操作的方法,包括创建文件、写入文件和读取文件的过程。同时还介绍了如何判断文件是否成功打开和关闭文件的方法。通过本文的学习,读者可以了解如何在C++中进行文件读写操作。 ... [详细]
  • 本文介绍了最长上升子序列问题的一个变种解法,通过记录拐点的位置,将问题拆分为左右两个LIS问题。详细讲解了算法的实现过程,并给出了相应的代码。 ... [详细]
  • 本文介绍了深入浅出Linux设备驱动编程的重要性,以及两种加载和删除Linux内核模块的方法。通过一个内核模块的例子,展示了模块的编译和加载过程,并讨论了模块对内核大小的控制。深入理解Linux设备驱动编程对于开发者来说非常重要。 ... [详细]
  • 本文介绍了如何使用Express App提供静态文件,同时提到了一些不需要使用的文件,如package.json和/.ssh/known_hosts,并解释了为什么app.get('*')无法捕获所有请求以及为什么app.use(express.static(__dirname))可能会提供不需要的文件。 ... [详细]
  • Java自带的观察者模式及实现方法详解
    本文介绍了Java自带的观察者模式,包括Observer和Observable对象的定义和使用方法。通过添加观察者和设置内部标志位,当被观察者中的事件发生变化时,通知观察者对象并执行相应的操作。实现观察者模式非常简单,只需继承Observable类和实现Observer接口即可。详情请参考Java官方api文档。 ... [详细]
  • JDK源码学习之HashTable(附带面试题)的学习笔记
    本文介绍了JDK源码学习之HashTable(附带面试题)的学习笔记,包括HashTable的定义、数据类型、与HashMap的关系和区别。文章提供了干货,并附带了其他相关主题的学习笔记。 ... [详细]
author-avatar
小佛瞌睡蓝
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有