Matlab 2019 中的 3 维内部收益率
Posted
技术标签:
【中文标题】Matlab 2019 中的 3 维内部收益率【英文标题】:3-dimensional IRR in Matlab 2019 【发布时间】:2019-10-19 18:21:30 【问题描述】:我正在尝试在 Matlab 2019a 中计算具有多个维度的 IRR。我的公式在理论上有效(暂时忽略“多重回报率”警告),但问题是对于更大的矩阵,即 noScenarios > 5 左右,代码变得非常慢。有哪些编程替代方案?我也尝试了 fsolve,但我认为它也没有更快。
请注意,由于我不是数学专家,因此像“布伦特方法”这样的简单关键词对我来说是不够的(例如 What is the Most Efficient Way to Calculate the Internal Rate of Return IRR?)。我必须知道a)如何在Matlab中实现它,以及b)如果它是非常愚蠢的证明,那么什么都不会出错?谢谢!
clc
clear
close all
noScenarios = 50;
CF = ones(300,noScenarios,noScenarios,noScenarios);
CF = [repmat(-300, 1,noScenarios,noScenarios,noScenarios); CF];
for scenarios1 = 1:noScenarios
for scenarios2 = 1:noScenarios
for scenarios3 = 1:noScenarios
IRR3dimensional(scenarios1,scenarios2,scenarios3) = irr(CF(:,scenarios1,scenarios2,scenarios3));
end
end
end
【问题讨论】:
【参考方案1】:要计算 IRR,您需要求解一个多项式方程。这必须对每个现金流量向量分别进行。因此,将irr
应用于多维矩阵不会缩短执行时间。我怀疑 Matlab 内部仍然使用循环。
您可能可以通过使用fsolve
的优化选项来获得一些速度,但不太可能有很大的改进。据推测,Matlab 开发人员已经选择了一个足够好的方法。
因此,您唯一的其他选择是并行化。如果您可以访问服务器或笔记本电脑/台式机有多个 CPU,则可以通过并行运行 irr
函数来减少运行时间。 (您可能还需要一个并行计算工具箱。)
我稍微修改了您的示例以使用随机现金流值,以便更容易检查。但是,我减少了场景和时间点的数量,以便timeit
函数可以在合理的时间内运行多个模拟。 (另外,请记住,执行时间似乎与时间点的数量呈指数关系。)
t = 150;
noScenarios = 10;
noThreads = 4;
CF = rand(t,noScenarios,noScenarios,noScenarios);
CF = [-rand(1,noScenarios,noScenarios,noScenarios); CF];
h1 = @() f1(CF, noScenarios);
fprintf("%0.4f : single thread, loop\n", timeit(h1))
h2 = @() f2(CF, noScenarios);
fprintf("%0.4f : single thread, vectorized\n", timeit(h2))
poolObj = parpool('local', noThreads);
h3 = @() f3(CF, noScenarios);
fprintf("%0.4f : parallelized outer loop\n", timeit(h3))
delete(poolObj);
poolObj = parpool('local', noThreads);
h4 = @() f4(CF, noScenarios);
fprintf("%0.4f : parallelized inner loop\n", timeit(h4))
delete(poolObj);
function res = f1(CF, noScenarios)
res = zeros(noScenarios, noScenarios, noScenarios);
for scenarios1 = 1:noScenarios
for scenarios2 = 1:noScenarios
for scenarios3 = 1:noScenarios
res(scenarios1,scenarios2,scenarios3) = irr(CF(:,scenarios1,scenarios2,scenarios3));
end
end
end
end
function res = f2(CF, noScenarios)
res = reshape(irr(CF), noScenarios, noScenarios, noScenarios);
end
function res = f3(CF, noScenarios)
res = zeros(noScenarios, noScenarios, noScenarios);
parfor scenarios1 = 1:noScenarios
for scenarios2 = 1:noScenarios
for scenarios3 = 1:noScenarios
res(scenarios1,scenarios2,scenarios3) = irr(CF(:,scenarios1,scenarios2,scenarios3));
end
end
end
end
function res = f4(CF, noScenarios)
res = zeros(noScenarios, noScenarios, noScenarios);
for scenarios1 = 1:noScenarios
for scenarios2 = 1:noScenarios
parfor scenarios3 = 1:noScenarios
res(scenarios1,scenarios2,scenarios3) = irr(CF(:,scenarios1,scenarios2,scenarios3));
end
end
end
end
当我在具有 4 个 CPU 和 16 Gb 内存的服务器上运行此代码时,我得到了以下结果。
19.9357 : single thread, loop
20.4318 : single thread, vectorized
...
5.6346 : parallelized outer loop
...
12.4640 : parallelized inner loop
如您所见,irr
的矢量化版本对循环没有任何好处。在这种情况下,它会稍微慢一些。在我的其他测试中,它偶尔会更快一些。
但是,您可以通过使用 parfor
函数并行化外循环来显着减少运行时间。它比并行化最内层循环要好,因为每个批次都有一定的执行开销。因此,少量较大的批次比大量较小的批次具有更低的开销。
以下是并行化的工作原理。首先,您使用以下命令创建一个本地工作线程池。确保不超过您拥有的 CPU 数量。 parpool
可以无限期地等待,直到创建所有本地工作人员,并且只有在 CPU 可用时才能创建本地工作人员。
poolObj = parpool('local', noThreads);
创建池可能需要几秒钟的时间。这就是为什么我把它移到我计时的功能之外。对于较大的作业,池创建时间与总执行时间相比是微不足道的。
在这里,我将池对象保存在一个变量中,然后将其删除。但是,它是可选的。默认情况下,池在 30 分钟不活动或 Matlab 终止后被销毁。
之后,您将要并行化的 for
循环替换为 parfor
调用,即 for scenarios1 = 1:noScenarios
变为 parfor scenarios1 = 1:noScenarios
。默认情况下,parfor
将使用所有可用的工作人员,但您也可以使用parfor (scenarios1 = 1:noScenarios, maxWorkers)
指定允许使用的工作人员的最大数量。但请注意,执行顺序并不能保证,即第五次迭代可能在第三次迭代之前执行。
【讨论】:
谢谢!仍在测试它,但它似乎是我需要的!一个快速的跟进,我有一个英特尔酷睿 i7,并行工具箱的偏好设置为 12 个工作人员。但是,我收到警告:“您要求至少 4 个工作人员,但集群“本地”的 NumWorkers 属性设置为最多允许 2 个工作人员。要在比这更多的工作人员上运行通信作业(最多512(本地集群),增加集群的 NumWorkers 属性值。本地集群的 NumWorkers 的默认值是本地机器上的核心数。” @LenaH 文档不准确。无论 CPU 编号如何,默认设置为 12,但它可能会检测到差异并仅创建两个带有警告的 worker。如果您有一个较旧的移动 i7,它可能有 2 个 CPU 能够运行 4 个线程(超线程),但 Matlab 可能无法利用这一点,并将工作人员的数量限制为物理 CPU 的数量。我担心将其增加到 2 以上的唯一解决方案是使用不同的机器或服务器。以上是关于Matlab 2019 中的 3 维内部收益率的主要内容,如果未能解决你的问题,请参考以下文章