加快Matlab中的符号递归

时间:2014-08-31 15:36:53

标签: matlab symbolic-math

我对二叉树进行了向后递归。在每个节点处,未知C以这样的方式进入,即在起始节点处我们得到一个公式A(1,1),该公式取决于C。代码如下:

A=sym(zeros(1,Steps));
B=zeros(1,Steps);
syms C; % The unknown that enters A at every node
tic
for t=Steps-1:-1:1

 % Values needed in A and B
 Lambda=1-exp(-(1./S(t,1:t).^b).*h);
 Q=((1./D(t))./(1-Lambda)-d)/(u-d);
 R=normcdf(a0+a1*Lambda);

 % the backward recursion for A and B
 A(1:t)=D(t)*C+D(t)*...
     (Q.*(1-Lambda).*A(1:t) ...
      + (1-Q).*(1-Lambda).*A(2:t+1));

 B(1:t)=Lambda.*(1-R)+D(t)*...
     (Q.*(1-Lambda).*B(1:t)...
      + (1-Q.*(1-Lambda).*B(2:t+1))); 
end

C = solve(A(1,1)==sym(B(1,1)),C);

如果Steps = 104,此代码大约需要4秒。但是,如果我们删除C并将矩阵A设置为常规双矩阵,则只需大约0.02秒。因此,使用syms会将计算时间增加200倍。这对我来说似乎太过分了。有什么建议可以加快速度吗?

我在2013年MacBook air 13英寸弹簧上使用Matlab 2013b。此外,如果您对上述部分之前的代码感兴趣(不确定是否相关):

a0 = 0.9;
a1 = -3.2557;
b = 1.2594;
S0=18.57;
sigma=0.6579;
h=1/104;
T=1;
Steps=T/h;
f=transpose(normrnd(0.04, 0.001 [1 pl]));
D=exp(-h*f); % discount values
pl=T/h; % pathlength - amount of steps in maturity

u=exp(sigma*sqrt(h));
d=1/u;

u_row = repmat(cumprod([1 u*ones(1,pl-1)]),pl,1);
d_row = cumprod(tril(d*ones(pl),-1)+triu(ones(pl)),1);
path = tril(u_row.*d_row);
S=S0*path;

1 个答案:

答案 0 :(得分:0)

除非我遗漏了某些内容,否则无需使用符号数学或使用未知变量。您可以有效地假设递归关系中的C = 1并在最后求解实际值。这里有完整的代码以及其他一些改进:

rng(1); % Always seed your random number generator

a0 = 0.9;
a1 = -3.2557;
b = 1.2594;
S0 = 18.57;
sigma = 0.6579;
h = 1/104;
T = 1;
Steps = T/h;
pl = T/h;
f = 0.04+0.001*randn(pl,1);
D = exp(-h*f);
u = exp(sigma*sqrt(h));
d = 1/u;

u_row = repmat(cumprod([1 u*ones(1,pl-1)]),pl,1);
d_row = cumprod(tril(d*ones(pl),-1)+triu(ones(pl)),1);
pth = tril(u_row.*d_row);
S = S0*pth;

A = zeros(1,Steps);
B = zeros(1,Steps);
tic
for t = Steps-1:-1:1
    Lambda = 1-exp(-h./S(t,1:t).^b);
    Q = ((1./D(t))./(1-Lambda)-d)/(u-d);
    R = 0.5*erfc((-a0-a1*Lambda)/sqrt(2)); % Faster than normcdf

    % Backward recursion for A and B
    A = D(t)+D(t)*(Q.*(1-Lambda).*A(1:end-1) + ...
                   (1-Q).*(1-Lambda).*A(2:end));
    B = Lambda.*(1-R)+D(t)*(Q.*(1-Lambda).*B(1:end-1) + ...
                            (1-Q.*(1-Lambda).*B(2:end))); 
end
C = B/A
toc

在我的MacBook Pro上运行大约需要0.005秒。你当然可以做出其他改进。在多个地方使用的变量组合很多(例如1-LambdaD(t)*(1-Lambda)),可以计算一次。 Matlab可能会尝试优化这一点。您可以尝试将LambdaQR移出循环 - 或者至少在外部计算部分内容并将结果保存在数组中。