【问题标题】:Variadic template with list of pairs, want to forward only first elements of pairs带有对列表的可变参数模板,只想转发对的第一个元素
【发布时间】:2020-04-06 19:57:58
【问题描述】:

我正在编写一个函数来计算多项分布的 pmf (https://en.wikipedia.org/wiki/Multinomial_distribution)。

我已经成功编写了一个多项式系数的函数,并想用它来计算多项式分布,但编译失败。

我目前的尝试:

#include <algorithm>
#include <cmath>
#include <cassert>
#include <utility>
#include <initializer_list>
#include <numeric>

template <typename N>
concept Unsigned = std::is_unsigned_v<N>;

template <typename N>
concept Integral = std::is_integral_v<N>;

template <typename R>
concept Floating = std::is_floating_point_v<R>;

template <Unsigned U>
constexpr U binom(U N, U K) {
    assert(N >= K);
    U result {1};
    for (U i = U {1}; i <= N - K; i++) {
        result *= i + K;
        result /= i;
    }
    return result;
}

template <Unsigned U, Integral I>
constexpr U multinom (U N, I K) {
    assert(N == K);
    return U {1};
}

template <Unsigned U, Integral I, Integral... Is>
constexpr U multinom (U N, I K1, Is... Ks) {
    assert(N >= static_cast<U>(K1));
    return binom(N, static_cast<U>(K1)) * multinom(N - static_cast<U>(K1), Ks...);
}

template <Floating F1, Floating F2>
constexpr bool almost_equal(F1 f1, F2 f2) {
    return std::fabs(f1 - f2) < std::min({1.0e-4, f1 * 1.0e-3, f2 * 1.0e-3});
}

/*template <Unsigned U, Integral I, Floating F>
constexpr F multinom_pmf(U N, std::initializer_list<std::pair<I, F>> args) {
    assert(almost_equal(1.0, std::accumulate(args.begin(), args.end(), 0.0, [](auto& a, auto& b) {return a + b.second;})));
}*/

template <Unsigned U, Integral I, Floating F>
constexpr F multinom_pmf (U N, std::pair<I, F>... args) {
    assert(almost_equal(1.0, std::accumulate(args.begin(), args.end(), 0.0, [](auto& a, auto& b) {return a + b.second;})));
    // ???
}

int main() {
    static_assert(multinom<size_t>(5, 5) == 1);
    static_assert(multinom<size_t>(5, 3, 2) == 10);
    static_assert(multinom<size_t>(6, 2, 2, 2) == 90);
}

所需界面:

multinom_pmf<>(N, pair<I, F>... args) = multinom<>(N, /* first of args */) * std::pow(/* second of args */, /* first of args */) * ...

前)

multinom_pmf<>(5, {3, 0.6}, {2, 0.4}) = multinom<>(5, 3, 2) * std::pow(0.6, 3) * std::pow(0.4, 2) = 0.3456
multinom_pmf<>(6, {2, 0.4}, {2, 0.35}, {2, 0.25}) = multinom<>(6, 2, 2, 2) * pow(0.4, 2) * pow(0.35, 2) * pow(0.25, 2) = 0.11025

我希望函数能够检查

N == sum of first of args (which would be done inside multinom() call)
1.0 == sum of second of args

如何改进我的尝试?提前致谢。

【问题讨论】:

    标签: c++ c++17 variadic-templates c++20


    【解决方案1】:

    自我回答:我的解决方案最终丢弃了 std::pair 的东西。哈克,但有效。

    仍然不知道如何检查概率和是否为1。

    template <Unsigned U, Integral I, Floating F, typename... Ts>
    constexpr F multinom_pmf (U N, I K1, F theta1, Ts... args) {
        F prob_value = std::pow(theta1, K1) * binom(N, static_cast<U>(K1));
        if constexpr(sizeof...(args) > 0)
            return prob_value * multinom_pmf(N - static_cast<U>(K1), args...);
        else
            return prob_value;
    }
    
    int main() {
        static_assert(multinom<size_t>(5, 5) == 1);
        static_assert(multinom<size_t>(5, 3, 2) == 10);
        static_assert(multinom<size_t>(6, 2, 2, 2) == 90);
        assert(almost_equal(multinom_pmf<size_t>(5, 3, 0.4, 2, 0.6), 0.2304));
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2011-05-16
      • 2014-09-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多