成对函数求值算法(C++,STL)

Algorithm for function evaluation by pairs (C++, STL)

本文关键字:C++ STL 算法 函数      更新时间:2023-10-16

我需要成对将自定义func应用于 STL 容器 ->即:

// if c => {a,b,c,d,e,f,g}; // a,b,c,.. are just aliases for some object
my_algorithm(c.begin(),c.end(),[](auto a, auto b){ a + b }); // c++14

应该解析成这样:

temp1 = a + b;
temp2 = c + d;
temp3 = e + f;
temp4 = temp1 + temp2;
temp5 = temp3 + g;
result = temp4 + temp5;

(我确定这种算法有一个正确的名称,但我不知道这可能是什么(

我已经尝试过std::accumulate,我不确定它的实现是否由标准定义,但就我而言,使用我的编译器,它似乎可以解决这个问题(我认为这称为成对求和,对吧?

temp1 = a + b;
temp2 = temp1 + c;
temp3 = temp2 + d;
// etc

这更不像我能得到的一样

auto temp = c[0];
std::for_each(c.begin()+1,c.end(),[&temp](auto a){temp + a); // c++14

我浏览了STL和Boost,但没有找到相关的东西。有没有提供这种算法的库?如果没有,有什么想法可以很好地实现STL兼容吗?

编辑补充一点,我对添加传统意义上的元素并不真正感兴趣 - 在这种情况下,顺序并不重要。我的函数将执行更复杂的加权求和,如果以这种方式执行,将给出不同的结果。不过,我的问题更笼统。

以下是我对 C++11 标准的 STL 兼容解决方案的尝试:

#include <cassert>
#include <cmath>
#include <cstddef>
#include <array>
#include <iostream>
#include <iterator>
namespace detail {
  // Returns first power of two which is strictly less than n
  unsigned int pot_half(std::ptrdiff_t n) {
    assert(n > 1);
    return 1 << (static_cast<unsigned int>(ceil(log2(n))) - 1);
  }
} // end namespace detail
struct tree_fold_on_empty_range : std::exception {};
template <typename Iterator, typename F>
auto tree_fold(const Iterator & begin, const Iterator & end, F && func) -> decltype(func(*begin, *end)) {
  std::ptrdiff_t diff = end - begin;
  switch (diff) {
    case 0: throw tree_fold_on_empty_range{}; // or, return {}; ?
    case 1: return *begin;
    case 2: return func(*begin, *(begin + 1));
    default: {
      Iterator mid{begin};
      std::advance(mid, detail::pot_half(diff));
      return func(tree_fold(begin, mid, func), tree_fold(mid, end, func));
    }
  }
}
int main() {
  for (uint n = 2; n < 20; ++n) {
    std::cout << n << " -> " << detail::pot_half(n) << std::endl;
  }
  std::cout << std::endl;
  std::array<int, 8> test{1, 2, 3, 4, 5, 6, 7, 8};
  std::cout << tree_fold(test.begin(), test.end(), [](int a, int b){ return a + b; }) << std::endl;
  std::cout << tree_fold(test.begin(), test.end(), [](int a, int b){ return a - b; }) << std::endl;
}

也住在科里鲁,

它给出这个作为最终输出:

36
0

我相信这表明它是正确的:

1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 = 36
((1 - 2) - (3 - 4)) - ((5 - 6) - (7 - 8)) =
((-1) - (-1)) - ((-1) - (-1)) =
0 - 0 = 0

请注意,在不是 2 的幂的范围内,"正确"行为有点模棱两可。在我的版本中,我所做的总是以小于 n 的 2 的第一次幂拆分长度范围n。所以如果你给它一个2的幂,你总是得到一个完美平衡的二叉树。如果你给它 6,你会得到这样的东西:

        /
    /       /
  /  /

然而,没有什么说总是除以二也是不正确的,所以你会得到这样的树结构

        /
    /       /
  /       /

所以IMO你的问题有点不够明确。也许这对你来说并不重要,只要深度O(log n)

自 2015 年 11 月以来,我一直在所谓的 VectorFuncRange 容器中工作,该容器在 C++14 中以 STL 风格解析该容器。

做了我自己的测试版,它可以很好地模仿 std::vector 容器,但使用 func_range(( 方法在 O(log n( 中返回一个范围内的函数计算,评估为树。我赞同即使在内部评估为树,它们也只是向量,并且具有 O(1( 随机访问、摊销 O(1( 和最坏情况 O(log n( 等push_back。一些 std::vector 方法还没有由我编程,如 emplace_back(( 和不同的结构,但用作向量的主要方法是。出于测试原因,我将 rang_func(( 与 range_func_dumb(( 进行比较,第二个版本按线性顺序评估函数。

VectorFuncRange.h 我当前的版本: http://pastebin.com/dnwznUqg以 5 种不同方式执行此操作的测试代码,具有整数、矩阵和其他类型以及许多函数:http://pastebin.com/YdRfN0CQ

曾考虑过放入公共 Git,但我想我应该在此之前组织更多的代码,我不知道其他人是否有兴趣贡献。

你应该看看std的第二种形式::transform:http://www.cplusplus.com/reference/algorithm/transform/

在 C++ 11 附近的伪代码中,算法的 STL 实现可能如下所示:

c = {a,b,c,d,e,f,g} // container of elements of type 'my_obj'
tmp = {a,b,c,d,e,f,g} // copy of 'c' to not impact 'c' while executing algorithm
while (tmp.size() > 1)
{
    // partition 'tmp' into even index elements 'c1' and odd index elements 'c2'
    // first iteration would look like this :
    // c1 = {a,c,e,g}
    // c2 = {b,d,f,identity} where 'idendity' is a new element (when 'tmp' size is odd) to match 'g' without impacting final result... identity = 0 for integers addition :)
    // overwrite first elements of 'tmp' with intermediate results
    std::transform(c1.cbegin(), c1.cend(), c2.cbegin(), tmp.begin(), std::plus<my_obj>()); // replace std::plus with any other binary operation including any proper lambda
    // cut 'tmp' ununsed upper half
    tmp.resize(size_t(0.5 * (tmp.size() + 1)));
}
my_obj result = tmp[0];

显然,在开始时复制"c"并在每次迭代时将"tmp"分成两半是有代价的。您决定如何从这里进行优化:)

考虑到一些建议的解决方案(特别是Chris Beck的解决方案(,我想出了这个算法,我现在正在尝试进一步优化。我已将其移至其他线程,因为我认为代码打开了许多值得自行讨论的问题。