使用C ++中的两个输入记忆函数

时间:2012-04-11 19:33:59

标签: c++

我有一个接受两个输入的函数f(a,b)。我不知道提前使用a和b的哪个值。我对记忆有点浪费我很好(我关心速度)。我希望能够检查f(a,b)的输出是否已经传递,如果是,则再次传递该输出而不重新运行f(a,b)过程。

使用装饰器在Python中很容易做到,但是C ++在我的脑海中已经过时了。

5 个答案:

答案 0 :(得分:6)

我会使用std::map(或者std::unordered_map),其密钥为std::pair,或者使用地图地图。

在这种情况下,C ++ 11的改进可能会有所帮助。或者也许是一些提升的东西。

答案 1 :(得分:3)

海报问道:

  

我希望能够检查f(a,b)的输出是否已经传递,如果是,则再次传递该输出而不重新运行f(a,b)过程。

使用std::map在C ++中非常容易。该函数具有两个参数的事实意味着我们可以使用std::pair来描述它们。

#include <map>
#include <iostream>

uint64_t real_f(int a, int b) {
  std::cout << "*";
  // Do something tough:
  return (uint64_t)a*b;
}

uint64_t memo_f(int a, int b) {
  typedef std::pair<int, int> key;
  typedef std::map<key, uint64_t> map;
  static map m;
  key k(a,b);
  map::iterator it = m.find(k);
  if(it == m.end()) {
    return m[k] = real_f(a, b);
  }
  return it->second;
}

int main () {
  std::cout << memo_f(1, 2) << "\n";
  std::cout << memo_f(3, 4) << "\n";
  std::cout << memo_f(1, 2) << "\n";
  std::cout << memo_f(3, 4) << "\n";
  std::cout << memo_f(5, 6) << "\n";
}

上述程序的输出是:

*2
*12
2
12
*30

没有星号的行代表缓存结果。

答案 2 :(得分:1)

使用C ++ 11,您可以使用任务和期货。让f成为您的职能:

int f(int a, int b)
{
    // Do hard work.
}

然后你会安排函数执行,它会返回一个返回值的句柄。此句柄称为 future

template <typename F>
std::future<typename std::result_of<F()>::type>
schedule(F f)
{
    typedef typename std::result_of<F()>::type result_type;
    std::packaged_task<result_type> task(f);
    auto future = task.get_future();

    tasks_.push_back(std::move(task)); // Queue the task, execute later.
    return std::move(future);
}

然后,您可以按如下方式使用此机制:

auto future = schedule(std::bind(&f, 42, 43)); // Via std::bind.
auto future = schedule([&] { f(42, 43); });    // Lambda alternative.

if (future.has_value())
{
    auto x = future.get();  // Blocks if the result of f(a,b) is not yet availble.
    g(x);
}

免责声明:我的编译器不支持任务/期货,因此代码可能有一些粗略的边缘。

答案 3 :(得分:0)

关于这个问题的要点是计算f(a,b)和保持某种查找表以缓存结果之间CPU和RAM的相对费用。

由于128位索引长度的穷举表尚不可行,我们需要将查找空间缩小到可管理的大小 - 如果没有在您的应用程序内部考虑,则无法完成此操作:

  • 功能输入的真正使用空间有多大?它有一个模式吗?
  • 时间成分怎么样?您是否期望重复计算彼此接近或沿时间线分布?
  • 分发怎么样?您是否假设索引空间的一小部分消耗了大部分函数调用?

我只是从一个固定大小的(a,b, f(a,b))元组数组开始,然后进行线性搜索。根据您上面提到的模式,您可能希望

  • 窗口 - 滑动它(在缓存未命中时删除最旧):这适用于本地化重新发生
  • 具有(a,b,f(a,b),count)元组和元组,其中最小的计数被驱逐 - 这对于非本地化的事件很有用
  • 有一些关键功能确定缓存中的位置(这适用于微小的索引空间使用)
  • Knuth或Google可能想到的任何其他内容

如果后者变得越来越复杂,您可能还希望将重复计算与查找机制进行对比:std::map和freinds不是免费的,即使它们是高质量的实现。

答案 4 :(得分:0)

唯一简单的方法是使用 std::mapstd::unordered_map 不起作用。我们不能使用 std::pair 作为无序映射中的键。您可以执行以下操作,

std::map<pair<int, int>, int> mp; 

int func(int a, int b)
{
  if (mp.find({a, b}) != mp.end()) return mp[{a, b}];
  // compute f(a, b)...
  mp[{a, b}] = // computed value;
  return mp[{a, b}];
}