当您尝试实现此公式时
使用此代码以直接方式
def P(n, p, limit):
if n == limit:
return 1
if n == -limit:
return 0
return p*P(n+1, p, limit) + (1-p)*P(n-1, p, limit)
您肯定会遇到此错误。
RecursionError:超过最大递归深度
那么如何使用代码正确实现此类问题呢?是否有用于重构并获得解决方案的技术?还是唯一的办法就是先用数学方法解决该问题,然后才将其输入计算中?
答案 0 :(得分:3)
要扩展@Prune
的建议,可以通过将RHS上的任一术语移到LHS上,将这种重复写成其他两次重复:
这些是非循环的,因为每个术语仅取决于之前或之后的术语,而不是取决于。
但是,它们不能独立求解,因为这需要了解P 1-1 和P l-1 ,其中l = limit
。
为方便起见,进行了一些替换:
请注意,由于关系仅包含 linear 项(例如,不包含P n ×P n-1 等),因此所有项在它们的展开中,变量x, y
也是线性的:
这些新引入的系数也遵循类似的递归关系:
他们的边界条件是:
现在已经知道边界条件,可以迭代计算系数。仅需从两个重复中使P 1-1 和P l-1 的表达式相等:
求解这对联立方程,得出P 1-1 和P l-1 ,从中可以计算出任何P n (使用先前计算的系数):
更新:示例Python实现
# solve a recurrence of the form
# Tn = c1 * Tn-1 + c2 * Tn-2, n >= 0
def solve_recurrence(c1, c2, t1, t2, n):
if n <= 0: return t2
if n == 1: return t1
a, b = t2, t1
for i in range(1, n):
a, b = b, c1 * b + c2 * a
return b
# solve the loop recurrence
def solve_loop(limit, p, n):
assert(limit > 0 and abs(n) <= limit)
assert(p > 0 and p < 1)
# corner cases
if n == -limit: return 0
if n == limit: return 1
# constants
a, c = 1 / p, 1 / (1 - p)
b, d = 1 - a, 1 - c
# coefficients at 2l - 1
e = 2 * limit - 1
l = solve_recurrence(a, b, 1, 0, e)
m = solve_recurrence(a, b, 0, 0, e)
s = solve_recurrence(c, d, 1, 0, e)
t = solve_recurrence(c, d, 0, 1, e)
# note that m can just be 0 but was left in for generality
# solving the simultaneous equations
# to get P at 1 - l and l - 1
x = (s * m + t) / (1 - s * l)
y = (l * t + m) / (1 - l * s)
# iterate from the closest side to improve precision
if n < 0:
return solve_recurrence(a, b, x, 0, limit + n)
else:
return solve_recurrence(c, d, y, 1, limit - n)
limit = 10, p = 0.1
的实验结果:
n | P(n) abs. error
========================================
-10 | 0.0000000E+00 --
-9 | 6.5802107E-19 0.0000000E+00
-8 | 6.5802107E-18 1.7995889E-30
-7 | 5.9879917E-17 1.7995889E-29
-6 | 5.3957728E-16 5.0003921E-28
-5 | 4.8568535E-15 8.1994202E-27
-4 | 4.3712339E-14 8.9993252E-27
-3 | 3.9341171E-13 5.1002066E-25
-2 | 3.5407061E-12 5.9938283E-25
-1 | 3.1866355E-11 5.9339980E-18
0 | 2.8679726E-10 5.3406100E-17
1 | 2.5811749E-09 3.3000371E-21
2 | 2.3230573E-08 2.6999175E-20
3 | 2.0907516E-07 2.2999591E-19
4 | 1.8816764E-06 7.9981086E-19
5 | 1.6935088E-05 1.9989978E-18
6 | 1.5241579E-04 3.5000757E-16
7 | 1.3717421E-03 1.5998487E-15
8 | 1.2345679E-02 3.2000444E-14
9 | 1.1111111E-01 6.9999562E-14
10 | 1.0000000E+00 --
注意:
solve_recurrence
中使用的迭代方法还存在一个替代的显式公式,如果与库函数结合使用,则可能会产生更准确的结果。答案 1 :(得分:0)
递归取决于将问题分解为多个部分,每个部分都更接近基本情况。您给出的公式是无限的,因为它取决于同样不确定的系列术语。简单的数学符号不能保证可计算性。
在这种情况下,您必须以非圆形术语重写递归关系。您仍然可以使用递归,但是必须有一个明确的目标。在给定的公式中,P [n]取决于P [n + 1]和P [n-1],每个 直接取决于P [n]。这在算法上是循环的。
您需要从基本案例中提取算法,从头开始并逐步进行。此递归关系的不幸结果之一是,您必须按顺序求解整个范围[-limit,limit]获得所需位置的值。基本情况值会从端点到您有视线的地方不断波动。
没有工具可以为您象征性地解开此问题。最重要的是,请注意基本情况不会出现在等式中。即使您提供了这些作为附加方程式,从这种关系中导出指数级数也超出了当今求解器的分析能力。
答案 2 :(得分:0)
我还有另一个使用符号计算的解决方案。
class X(object):
"""Representing unknown value which will be resolved in future"""
def __init__(self, value=1.0):
self.value = value
def __add__(self, other):
cls = self.__class__
return cls(value=self.value+other.value)
def __sub__(self, other):
cls = self.__class__
return cls(value=self.value-other.value)
def __mul__(self, other):
cls = self.__class__
if isinstance(other, cls):
return cls(value=self.value*other.value)
elif isinstance(other, numbers.Real):
return cls(value=self.value*other)
else:
raise TypeError(f'Cannot multiply with type {type(other)}')
def __rmul__(self, other):
return self.__mul__(other)
我们不是立即获得数值结果,而是依靠这样的事实,即结果值将以某种方式表示为
,每个中间项将表示为
这就是为什么我们为对象定义加/减/乘运算。
因此,在代码中具有这种符号的表示形式,我们可以使用与接受的答案类似的方法来解决该方程式(我们从-limit
和limit
的角度出发,并表示其他未知的术语产品)。达到基点(0
状态)时,我们将获得概率与该比率的数值之比,从而使我们可以轻松地计算结果
_memory = []
def fill_memory(n):
global _memory
_memory = [(0, X(1))]*(2*n+1)
_memory[-n] = (0, X(0))
_memory[n] = (1, X(0))
def resolve(n, p):
q = 1-p
for i in reversed(range(n)):
# resolve from right to center
prob_next = _memory[i+1]
prob_prev = _memory[i-i]
_memory[i] = (
q*prob_prev[0] + p*prob_next[0],
q*prob_prev[1] + p*prob_next[1],
)
# resolve from left to center
prob_prev = _memory[-i-1]
prob_next = _memory[-i+1]
_memory[-i] = (
q*prob_prev[0] + p*prob_next[0],
q*prob_prev[1] + p*prob_next[1],
)
# calculate base probability
solution = _memory[0]
coef = (X(value=1) - solution[1]).value
p0 = solution[0] / coef
return p0
通过这种方法,我获得了正确且非常精确的结果(我使用的是p=0.6
和N=4
(限制))
def main():
N = 4
p = 0.6
fill_memory(N)
p0 = resolve(N, p)
print(f'Probability for N={N} steps is {p0*100:.4}%')
for i in range(2*N+1):
print(i-N, '->', _memory[i-N])
4 - 83.505% 3 - 77.143% 2 - 69.231% 1 - 60.0%
如您所见,它的结果与数学求解时的结果相同。