解题思路:
(1)首先优质题解第一那位的代码是有问题的,我在 debug 卡住的时候试了一下他的代码,结果原封不动贴到 IDE 输出的和标准答案不符。
(2)那个兄弟的问题好像是把“判断 NPV 是否接近 0” 和“更新 IRR”两句顺序写错了,也不一定,毕竟我没细看。
(3)但是解题思路是没问题的,就是二分法找零点,因为 NPV(IRR) 这个函数的导数是负的,所以只有两种情况:有一个零点或者没有零点,不存在输出 "Too many" 的情况,判断是否有零点的方法是看 NPV(0) 是否为负值,也就是所有现金流加起来,如果是负值那说明 NPV 永远是负值。
(4)二分法找零点没啥好说的吧,高中数学。用了两个 while 循环嵌套,分别用来连续接收数据和更新二分边界。
注意事项:
唯一不理解的地方是 NPV 的计算公式那块,如果写成 for (int i = 0; i < T + 1; ++i) { NPV += (term[i] / pow(IRR, i)); } 就没法正常接收,但是先加一句 NPV = 0 就正常了,可是 double 型数据不是默认初始化为 0 吗?想不通。看了我源码的朋友可以自己试一试,最好能在评论留言教教我。
参考代码:
// 题目 1076: 内部收益率 #include <iostream> #include <cmath> #include <vector> #include <iomanip> using namespace std; int main() { int T, CF, sum; // 期数,现金流,现金流之和(用于判断是否有零点) double left, right; // 二分法的边界 double IRR, NPV; // 内部收益率(+1后的正值)和投资净现值 while (cin >> T && T) { vector<int> term; // 保存各期现金流 for (int i = 0; i < T + 1; ++i) { cin >> CF; term.push_back(CF); //直接 cin >> term[i] 会出问题 sum += CF; } if (sum < 0) { cout << "No" << endl; break; // 没有零点 } // 初始化,默认给这个范围 left = 0; right = 1e6; IRR = 1.5; while (1) { /* 两种写法,但是没有 NPV = 0 这句话就没法放进下面的 for 中,原因不明 NPV = 0; for (int i = 0; i < T + 1; ++i) { NPV += (term[i] / pow(IRR, i)); } */ NPV = term[0]; for (int i = 1; i < T + 1; ++i) { NPV += (term[i] / pow(IRR, i)); } if (fabs(NPV) <= 1e-5) { break; // 接近0时默认为找到了零点,否则可能计算量巨大 } // 二分法 if (NPV < 0) { right = IRR; } else { left = IRR; } IRR = (left + right) / 2; } cout << fixed << setprecision(2) << IRR - 1 << endl; } return 0; }
0.0分
6 人评分