本网站由以下捐款支持:OEIS基金会.

备忘录

来自OeisWiki
跳转到:航行,搜索

这篇文章需要更多工作.

请帮助扩展它!


术语记忆(不是“记忆”的拼写错误)是最简单的动态编程形式。它涉及存储(“记忆”)值并在以后调用它们,而不是重新生成值。一些语言,比如枫树,数学软件马克西玛,具有允许值记忆的语法功能,而无需显式写出有关保存和恢复值的说明,请参阅下文。记忆可以大大减少计算时间,尤其是在递归定义函数的情况下。然而,它并不是在所有情况下都有效,并且存在时间-内存折衷。

示例

阶乘数(单递归函数)

对于以下示例,我们将使用阶乘的用定义的函数递推关系 ,使用0!=1,尽管大多数计算机代数系统有用于阶乘的内置命令。

假设在定义阶乘函数后,程序计算42!它给了你答案,然后你要求47分!有了记忆功能,就没有必要再开始新;相反,程序可以直接跳过计算.

枫树

枫树函数和过程,通过添加选项记忆.然后将值存储在一个“记忆表”中并可用,可以使用函数删除该表忘记()。也可以通过将结果显式分配给给定参数的函数来创建和填充该表,即f(x):=值作为示例,我们给出了阶乘的功能:

>f:=x->x*f(x-1):#到目前为止,对f(…)的任何调用都会产生无限递归>f(0):=1:#现在f(n)对任何非负整数n产生正确的结果>f(5);120>op(4,op(f));#这就产生了“记忆表”表格([0=1])>op(3,op(f));#注意,虽然没有设置“记住”选项运算符,箭头>f:=proc(n)选项记忆;n*f(n-1)结束:f(0):=1:f(5);120>op(4,op(f));#现在,所有结果,也包括中间计算的结果,都会自动存储表格([0=1,1=1,2=2,3=6,4=24,5=120])

数学软件

数学软件通过定义函数并设置其自身的值来实现记忆:[1]

fac[0]=1fac[n]:=fac[n]=n*fac[n-1]

马克西玛

马克西玛,实现是通过在参数周围使用方括号定义的“数组函数”完成的。请参阅讨论“计数素数”例如。

PARI/GP公司

PARI/GP公司没有内置的备忘录。可以直接完成;下面是一个详细的实现:

fac(n)={/*使用全局变量factable存储结果*/如果(n<2,返回(1));if(factable==‘factable,factable=[1]);/*如果尚未创建表*/if(#factable<n,factable=concat(factable,vector(n-#factable));/*将其扩展到所需大小*/如果(factable[n],factable[n],/*返回它,如果结果已经计算*/fac_table[n]=n*fac(n-1))/*否则计算并存储它*/}

由于我们旨在说明一个涉及递归定义的示例,因此结果仍然是以递归方式计算的。因此,初始调用fac(100)将产生100个嵌套调用。然而,随后调用fac(200)公司不需要200个,但只需要100个嵌套调用,因为递归将在已经计算和存储的fac(100)处停止。通过这种方式,可以避免由于堆栈大小有限而导致的参数最大大小的限制:计算一些中间结果就足以降低递归性水平。

请参见http://oeis.org/search?q=program%3记忆更多示例。

波尔

波尔有一个记忆模块。

圣人

圣人,通过将记忆装饰器“CachedFunction”应用于函数来激活记忆。

@缓存函数定义f(x):如果x<=1,则返回1,否则返回x*f(x-1)对于(0..9)中的i:打印f(i)


如果只需要对值进行循环,则更经济使用迭代器不需要记忆表。

定义F(最大值=1000):n=0f=1当n<=最大值时:产量fn+=1f*=n对于f(9)中的f:打印f

方案

方案可以定义一种新的语法形式,称为definec,它在其他方面作为定义函数的常规define工作,但仅限于定义范围必须限制为非负整数的一元函数,为“缓存”(或“记忆”)任何计算值添加“包装器代码”。注意,下面的实现并不要求将值限制为整数,因此完全可以缓存lambda-forms和closures。

下面的实现还使用了缓存大小的硬上限(全局变量*MAX-cache-size-for-DEFINEC*),这意味着如果使用大于该值的参数调用函数,则以普通方式计算结果,而不使用任何缓存。通过将*MAX-CACHE-SIZE-FOR-DEFINEC*定义为零,可以关闭此内存保护功能。

(定义*MAX-CACHE-SIZE-FOR-DEFINEC*290512)(定义-语法-定义(语法规则()((定义(名称参数)e0…)(定义名称(letrec((_缓存_(向量#f#f#f#f#f#f#f#f#f#f#f#f#f#f))(姓名(λ(arg)(条件((空?参数)缓存);;这是为了调试,允许程序员查看带有nil-argument的currenct缓存((>=arg*最大尺寸-尺寸-定义*);;如果超过限制,则无缓存!e0。。。)(否则(如果(>=arg(向量长度_缓存_));;如果超过当前缓存大小?(设置!_cache_;;然后增加缓存的大小。(矢量增长_高速缓存_(最小值*最大值-尺寸-FOR-DEFINEC*(最大值(1+arg)(*2(向量长度_缓存_)))))))(或(矢量参考缓存参数);;值已在缓存中(非#f)(λ(res);;或者我们必须第一次计算。。。(向量集!缓存参数资源);;然后将结果存储到缓存中资源;;然后将其返回给来电者。)(开始e0…)。。。这里实际发生了什么(计算))))) ; 康德))) ; letrec-定义名称) ; letrec公司) ;; (定义名称…))) ;; 句法规则)

例如,上面的代码适用于MIT/GNU方案(7.7.0版之后)。对于使用更传统的后置式“不卫生”宏(例如7.7.0版之前的MIT Schemes)的旧Scheme(或Lisp)系统,请参阅中的代码[1]

斐波那契数(双重递归函数)

这个斐波那契数是由双递归定义的,这意味着每个递归级别的函数调用数加倍,因此很快就变得令人望而却步了!记忆可以使我们避免所需的时间和空间急剧膨胀。(当然,我们在这里假设我们不知道比奈公式.)

阿克曼函数

这个阿克曼函数[2]是一个全部的 可计算函数那不是原始递归.

不能通过Ackermann函数的简单递归应用进行计算(减少为在任何可控制的时间内增加1)。计算此类函数的一种实用方法是使用中间结果的记忆。

笔记

外部链接

  • 保罗·布莱克(Paul E.Black),“记忆化”,收录于《算法和数据结构词典》(Dictionary of Algorithms and Data Structures)[在线],保罗·布雷克(Pall E.Black,ed.),美国国家标准与技术研究所。2008年8月14日。(2012年3月26日访问)网址:http://www.nist.gov/dads/HTML/memoize.HTML