首页 > 解决方案 > 数组的累积乘积:std::accumulate vs loop

问题描述

假设我想计算k数组第一个元素的累积乘积。

int arr[8] = {3, 5, 7, 4, 8, 9, 1, 2};
int k = 3;

就性能而言,哪一个是最佳选择?

选项1.普通for循环

int result = 1;
for (size_t i = 0; i < k; ++i) {
    result *= arr[i];
}

选项 2. 累积

result = std::accumulate(std::begin(arr),
                         std::begin(arr) + k,
                         1,
                         [](const int& x, const int &y) { return x*y; });

我对k较小的情况特别感兴趣,例如k = 3k = 4. 在这种情况下,花哨的 C++11 做事方式真的值得吗?

标签: c++accumulate

解决方案


简短的回答:使用算法而不是手写循环与性能无关!你可以在这里看到一个可能的实现。您会注意到它只是一个循环,您也可以自己编写。


更好的比较是手写循环与:

auto result = std::accumulate(std::begin(arr),
                              std::begin(arr) + k,
                              1,
                              std::multiplies<>{});

这等效于手写循环,不需要 C++11(仅适用于auto,std::beginstd::end, 并且std::multiplies<>需要 C++14,但在您使用 之前std::multiplies<int>)。

该算法的优点是表现力和可读性。您不能为循环命名,而算法名称则说明了它的作用。二元运算符和初始值也更容易替换。初始值不一定与元素的类型相同,返回类型accumulate是从中推导出来的。如果您想将更多值相乘并避免溢出,您只需将 替换11L。如果您后来决定不想相乘,而是相加,则只需替换std::multipliesstd::plus. 对于手写循环,这种重构需要深入细节并考虑整个循环,而不是只更改您想要更改的一个细节。除此之外,哪个“更好”是基于意见的。如果有的话,性能方面不会有太大的差异。


请注意,C++11 和 C++14 带来的变化不仅仅是“花哨”。在 C++11 之前,调用看起来像这样:

int result = std::accumulate(arr,                        // <- int
                             arr + k,
                             1,                          // <- int
                             std::multiplies<int>{});    // <- int 

这里用于结果的类型出现了 3 次,这使得上面关于轻松替换初始值的类型的观点没有意义。随之而来的变化auto和带有模板的函子operator()不是关于花哨的,而是它们显着减少了犯错的机会。


TL;博士

对于像你这样简单的循环,我可能会使用手写循环。它清晰易读,任何花哨的算法都不太可能使其性能更高。


推荐阅读