【问题标题】:What is the cleanest way to do a `std::partial_sum` with a `0` in front?什么是最干净的方法来做一个 `std::partial_sum` 前面有一个 `0`?
【发布时间】:2021-10-29 19:22:28
【问题描述】:

在 C++ 中,有一个函数 std::partial_sum 来计算前缀和。代码

#include <iostream>
#include <vector>
#include <iterator>
#include <numeric>

int main() {
    std::vector<int> a = {1, 2, 3, 4, 5};
    std::partial_sum(a.begin(), a.end(), a.begin());
    return 0;
}

将覆盖 a 到 1 3 6 10 15,这是预期的。

但是,在大多数情况下,我想使用前缀 sum,我希望前面有一个 0 来表示“空和”,以便我可以使用 a[2] - a[0] 来查询前两个元素的总和。 (这允许我使用一个简单的嵌套 for 循环来查找所有子数组的总和)。有没有办法用std::partial_sum 函数来实现它?我不知道这是否可能,因为输出大小将是输入大小 + 1。

注意:我不是在寻找事先改变a 的内容或类型的方法。

如果a 的大小是一个问题:

#include <iostream>
#include <vector>
#include <iterator>
#include <numeric>

int main() {
    std::vector<int> a = {1, 2, 3, 4, 5, -1};
    std::partial_sum(a.begin(), a.end() - 1, a.begin());
    return 0;
}

这样的事情也对我有用。

【问题讨论】:

  • "这样我就可以使用 a[2] - a[0]" 那么你肯定会想要别的东西作为你的输出迭代器......
  • @KarlKnechtel 假设我想覆盖a的内容
  • 你可以输出到不同的容器就好了(并且因为你可以,不同的输出大小不是问题)。但是,您的原始代码版本使用了ostream_iterator,这不会给您留下可以轻松使用[] 的东西。无论如何,我能想到的最简单的解决方法是先将0 值输出到迭代器,然后再将其传递给partial_sumstd::accumulate 允许您指定一个初始值,但它只计算最终结果,而不是给出所有部分结果。
  • @KarlKnechtel 明白了。现在如果我想用{0, 1, 3, 6, 10, 15} 覆盖a 而没有另一个容器怎么办?那有可能吗?
  • 我认为这不能正常工作(即使在 std::vector 上使用 insert_iterator),因为例如初始 0 将覆盖您要添加的第一个值,然后才能将其添加到运行总计中。

标签: c++ std stdvector prefix-sum


【解决方案1】:

有没有办法通过std::partial_sum 函数来实现?

只需在调用std::partial_sum 之前将0 写入输出迭代器即可。应该小心,因为输出比输入大一个,这不会在原地工作,因为它会在读取第一个输入之前写入第一个输出。

template<class InputIt, class OutputIt>
constexpr OutputIt my_partial_sum(InputIt first, InputIt last, OutputIt d_first)
{
    *d_first++ = typename std::iterator_traits<InputIt>::value_type{};
    return std::partial_sum(first, last, d_first);
}

如果您希望能够做到这一点,您可以进一步调整possible implementationstd::partial_sum

template<class InputIt, class OutputIt>
constexpr OutputIt partial_sum(InputIt first, InputIt last, OutputIt d_first)
{
    using value_type = typename std::iterator_traits<InputIt>::value_type;

    if (first == last) {
        *d_first++ = value_type{};
        return d_first;
    }

    value_type sum{};
    value_type next = *first;

    *d_first++ = sum;
 
    while (++first != last) {
       next = *first;
       sum = std::move(sum) + next;
       *d_first++ = sum;
    }
    return d_first;
}

但我认为更简单的方法是将 0 添加到您的容器中。

template <typename Container>
void my_partial_sum(Container& c) {
    c.emplace(c.begin());
    std::partial_sum(c.begin(), std::prev(c.end()), c.begin());
}

【讨论】:

  • 覆盖输入范围时不会中断吗?
  • @rubenvb 输出比输入大一,但 OP 知道这一点
  • @Caleth 不仅仅是大小,第一个元素在partial_sum 使用之前被覆盖。但是答案已经被接受了,所以 OP 似乎意识到这个解决方案不能用于幼稚的就地处理。
  • 我确实在谈论这个替换输入范围的第一个元素之前它被std::partial_sum 使用,如果它不等于元素类型的值初始化变量,则给出不同的答案。
  • @j6t 是的,感谢您指出这一点。事实上,我想我会坚持只在之后添加 0,因为这只是一个声明。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-10-03
  • 2014-05-03
相关资源
最近更新 更多