Re: [问题] 内存爆炸

楼主: LPH66 (-6.2598534e+18f)   2017-11-16 21:28:51
你的程式有两个问题:
(1) 这个问题应该跟你所谓"内存不足"有关
RK 法它是一个数值方法, 所以你不能让 Mathematica 用精确值一直叠代
举个例: xRK[0] := 1
xF1[0] := Sqrt[xRK[0]] 算得 Sqrt[1] = 1
xF2[0] := Sqrt[xRK[0]+π/100*xF1[0]] 算得 Sqrt[1+π/100] 就会停在这里
xF3[0] := Sqrt[xRK[0]+π/100*xF2[0]]
会算得 Sqrt[1+π/100*Sqrt[1+π/100]]
xF4[0] 同理会算得 Sqrt[1+π/50*Sqrt[1+π/100*Sqrt[1+π/100]]]
所以 xRK[1] 就会是一个很长的算式:
https://i.imgur.com/NVhAzQ1.png
因为 Mathematica 的特性, 它碰到这种无法化简的东西就会保持原样在那里
所以你可以想像 xRK[100] 会是一个多么深的叠代式
这可能是造成你的程式内存不足的原因
解决方法很简单, 因为这是数值方法, 只要每一次都算出大约值就行了
也就是在你这些叠代的式子外面挂上 N[] 即可
(2) 这个问题则跟你的计算时间有关
同样也是因为这是一个叠代公式
所以直接从这里计算 xRK[100] 会非常多次地计算它下面的所有值:
xRK[100] 呼叫 xRK[99], xF1[99], xF2[99], xF3[99], xF4[99]
xF1[99] 呼叫 xRK[99]
xF2[99] 呼叫 xRK[99], xF1[99] 所以一共呼叫两次 xRK[99]
xF3[99] 一共呼叫三次 xRK[99]
xF4[99] 一共呼叫四次 xRK[99]
所以 xRK[100] 一共呼叫十一次的 xRK[99]
依此类推下去, xRK[100] 将会呼叫 11^100 次 xRK[0]
这种数字你也不用等它算完了, 宇宙大概会先毁灭 XD
解决方法是一种称做 memoization 的方法
(个人喜欢翻译为“笔记法”, 维基百科的词条叫“记忆化”)
概念很简单, 算好的东西记下来之后要就直接拿
在 Mathematica 里的 memoization 的写法有一个公式:
func[n_] := func[n] = (* 函数定义 *)
这里前面的 := 阻止它后面的东西先求值
在你呼叫它给它 n 值之后 (例如 func[5]), 后面的 n 代换成你给它的值
会变成 func[5] = (* 函数定义 /. n->5 *)
这即是一个函数特殊状况的赋值, 所以会把函数定义算出来后赋给 func[5]
以后只要再次呼叫 func[5] 就会直接提取这个值出来, 不会重算一次
用这两种方式改写你的程式之后, 算出要画表的那个 Table 几乎是瞬间完成:
https://i.imgur.com/vzcUMjm.png
作者: ar851060 (ar851060)   2017-11-19 19:23:00
喔喔喔喔居然有那么简单的方法!太感谢了
作者: louis925 (稚空)   2016-04-30 06:06:00
大推!是个需要学习算法的前奏记忆法好像也称为 Dynamical programming
楼主: LPH66 (-6.2598534e+18f)   2016-06-27 19:03:00
记忆法跟 DP 是很像但不一样的算法最重要的差别是记忆法是 top-down, 由后方呼叫启动计算当取用到前面的呼叫时只在第一次做计算, 之后就查笔记但 DP 是 bottom-up, 由前方呼叫先行计算借由排定一个计算顺序使得所有东西能够依序求出来在 Mathematica 的这种长得很像函式语言的结构下写 DP 会比写记忆法来得有挑战性
作者: pig030 (FEBUR.PHEIX)   2016-03-11 20:58:00
lph66高手!

Links booklink

Contact Us: admin [ a t ] ucptt.com