matlab - Matlab 2019 中的 3 维内部收益率
问题描述
我正在尝试在 Matlab 2019a 中计算具有多个维度的 IRR。我的公式在理论上有效(暂时忽略“多重回报率”警告),但问题是对于更大的矩阵,即 noScenarios > 5 左右,代码变得非常慢。有哪些编程替代方案?我也试过 fsolve 但我认为它也不是更快。
请注意,由于我不是数学高手,所以像“布伦特方法”这样的简单关键词对我来说是不够的(例如,计算内部收益率 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
解决方案
要计算 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)
。但是请注意,执行顺序并不能保证,即第五次迭代可能在第三次迭代之前执行。
推荐阅读
- angular - 从父级引用 tooltip-content html 标签
- excel - 如果标识符相同,则数组内的值总和
- bash - Printf %d 字符串格式化数字
- javascript - 使用 AWS 服务的 chrome 扩展
- javascript - 无法使用 jQuery 过滤具有两个选项卡组的 div
- apache-kafka - 如何手动将代理 ID 添加到 zookper cli
- python - Bottle 的 request.get_cookie("cookie-name") 适用于所有浏览器,但在 Firefox 上返回 None
- html - CSS 网格在一行中展开单列
- java - 如何使用 maven 插件重新定位、重新安装和重新发布依赖项 jar 中的每个包?
- jasper-reports - 如何在 Jaspersoft 中为非空字段创建计数器?