在 C++ 中计算 log-sum-exp 函数

Tay*_*lor 3 c++ underflow exp c++11

c++ 标准库中是否有任何函数可以计算指数总和的对数?如果没有,我应该如何编写自己的?你有没有在这篇 wiki 文章中提到的任何建议?

我特别关心被加数会下溢的可能性。在这种情况下,您对绝对值很大的负数求幂。我正在使用 C++11。

MSa*_*ers 6

(shuvro 代码的 C++11 变体,根据问题使用标准库。)

template <typename Iter>
std::iterator_traits<Iter>::value_type
log_sum_exp(Iter begin, Iter end)
{
  using VT = std::iterator_traits<Iter>::value_type{};
  if (begin==end) return VT{};
  using std::exp;
  using std::log;
  auto max_elem = *std::max_element(begin, end);
  auto sum = std::accumulate(begin, end, VT{}, 
     [max_elem](VT a, VT b) { return a + exp(b - max_elem); });
  return max_elem + log(sum);
}
Run Code Online (Sandbox Code Playgroud)

这个版本更通用——它可以处理任何类型的值,在任何类型的容器中,只要它有相关的运算符。特别是,它将使用std::expandstd::log除非值类型有自己的重载。

为了真正有效地防止下溢,即使对于未知的数字类型,对值进行排序也可能是有益的。如果你输入进行排序,第一个值将是max_elem,这样的第一项sum将是exp(VT{0}这大概是VT{1}。这显然没有下溢。


shu*_*vro 5

如果你想要更小的代码,这个实现可以完成这项工作:

double log_sum_exp(double arr[], int count) 
{
   if(count > 0 ){
      double maxVal = arr[0];
      double sum = 0;

      for (int i = 1 ; i < count ; i++){
         if (arr[i] > maxVal){
            maxVal = arr[i];
         }
      }

      for (int i = 0; i < count ; i++){
         sum += exp(arr[i] - maxVal);
      }
      return log(sum) + maxVal;

   }
   else
   {
      return 0.0;
   }
}
Run Code Online (Sandbox Code Playgroud)

您可以在Takeda 25 的博客(日语)上看到更强大的实现(或者通过 Google Translate查看英语版)。