簡體   English   中英

記憶,遞歸因子函數?

[英]Memoized, recursive factorial function?

我知道如何在Python中輕松進行memoization,但我需要一種更快的方式來計算它們,所以我使用的是C ++。 但是,我不知道如何記憶。 我知道它是關於將數值存儲到數組或向量中,然后在檢索時掃描它的值,但是看看如何完成它真的很有幫助,所以我可以嘗試它的速度。

只是為了好玩,這是我前段時間寫的一個小型通用記憶。 它需要可變參數模板,當然:

template <template <typename...> class Container, typename...> struct Memo;

template <typename R, typename... Args, template <typename...> class Container>
struct Memo<Container, R, std::tuple<Args...>>
{
  Memo(std::function<R(Args...)> f) : func(f) { }

  R operator()(Args && ...args)
  {
    const auto arg = std::make_tuple(args...);
    typename CacheContainer::const_iterator it = cache.find(arg);

    if (it == cache.cend())
    {
      it = cache.insert(typename CacheContainer::value_type(arg, func(std::forward<Args>(args)...))).first;
      std::cout << "New call, memoizing..." << std::endl;
    }
    else
    {
      std::cout << "Found it in the cache!" << std::endl;
    }

    return it->second;
  }

private:

  typedef Container<typename std::tuple<Args...>, R> CacheContainer;

  std::function<R(Args...)> func;
  CacheContainer cache;
};


template <typename R, typename... Args>
Memo<std::map, R, std::tuple<Args...>> OMapMemoize(R(&f)(Args...))
{
  return Memo<std::map, R, std::tuple<Args...>>(f);
}
template <typename R, typename... Args>
Memo<std::unordered_map, R, std::tuple<Args...>> UMapMemoize(R(&f)(Args...))
{
  return Memo<std::unordered_map, R, std::tuple<Args...>>(f);
}

我不完全確定我是否得到了正確的左右,因為很久以前我寫過這個。 無論如何,這是一個測試用例:

int foo(double, char) { return 5; }

int main()
{
  auto f = OMapMemoize(foo);
  auto g = UMapMemoize(foo);

  int a, b;

  a = f(1.0, 'a');
  a = f(1.0, 'a');
  a = f(1.0, 'a');
  a = f(1.0, 'a');

  b = g(1.0, 'a');
  b = g(1.0, 'a');
  b = g(1.0, 'a');
  b = g(1.0, 'a');

  return a;
}

那么我能想到用C ++做的最好的方法可能是使用一個函數對象來存儲memoized值。 我想這可能與你的python裝飾有點類似,雖然我從來沒有真正做過任何python。 代碼看起來像這樣:

template <typename T, T (*calc)(T)>
class mem {
  std::map<T,T> mem_map;

public:
  T operator()(T input) {
    typename std::map<T,T>::iterator it;

    it = mem_map.find(input);
    if (it != mem_map.end()) {
      return it->second;
    } else {
      T output = calc(input);
      mem_map[input] = output;
      return output;
    }
  }
};

代碼定義了一個模板類,它接受一個typename和一個函數指針,然后在類上定義函數運算符,允許它被調用。 函數運算符接受輸入值檢查所述值是否在映射中,然后簡單地從映射返回它或計算它(使用函數指針),將其添加到映射然后返回它。

假設您定義了一些處理函數,例如:

int unity(int in) { return in; }

你會使用這樣的代碼:

mem<int, unity> mem_unity;
int y;
y = mem_unity(10);

因此,我們定義了一個mem類的實例,它將我們的值類型和處理函數作為模板參數,然后簡單地將此類稱為函數。

除了學生學習遞歸之外,沒有人會以這種方式計算因子。

記憶是一個非常好的主意,特別是如果你要重復調用該方法。 為什么扔掉好工作?

另一個考慮因素是計算階乘的更好方法:使用伽馬函數的自然對數。 它會更長時間地阻止溢出,因為你返回一個double值。 自然日志的增長速度將慢於價值。 如果您正在計算組合,則自然對數會將乘法和除法更改為加法和減法。

但是,無論如何,請記住您使用的任何實現。 如果你用C ++編寫它,我建議使用std:map ,其中參數x作為鍵, ln(gamma(x))作為值。

對不起,自從我寫了C ++和STL以來已經太久了。 我寧願使用具有O(1)讀訪問時間的哈希映射來迭代O(n)的鍵。

我喜歡依賴lambda捕獲(使用std=c++14

template<typename R, typename... Args>
auto memoize(std::function<R(Args...)>&& f)
{
  using F = std::function<R(Args...)>;
  std::map<std::tuple<Args...>,R> cache;
  return ([cache = std::map<std::tuple<Args...>,R>{}, 
           f = std::forward<F>(f)](Args&&... args) mutable
    {
      std::tuple<Args...> t(args...);
      if (cache.find(t) == cache.end())
        {
          R r = f(std::forward<Args...>(args)...);
          cache[t] = r;
        }
      return cache[t];
    });
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM