你的中间值之一,28!需要 98 位精度。
双精度数为 52-53 位。
令人惊讶的部分不是在 14 处有错误,而是在 14 之前没有错误。这是因为 double 采取了一些努力来减少累积错误,你基本上是幸运的。
在这种情况下,我们用乘法做了大量的数学运算,而加法几乎没有。与主要权力合作是一个很好的举措:
struct product {
std::map<std::size_t, std::ptrdiff_t> powers;
product& operator*=( product const& rhs ) {
for (auto&& e:rhs.powers)
powers[e.first] += e.second;
return tidy(*this);
}
product& operator/=( product const& rhs ) {
for (auto&& e:rhs.powers)
powers[e.first] -= e.second;
return tidy(*this);
}
friend product operator*( product lhs, product const& rhs ) {
lhs *= rhs;
return lhs;
}
friend product operator/( product lhs, product const& rhs ) {
lhs /= rhs;
return lhs;
}
// 1/x overload:
friend product operator~( product p ) {
for (auto& e:p.powers)
e.second = -e.second;
return p;
}
product() = default;
product(product const&) = default;
product(product &&) = default;
product& operator=(product const&) = default;
product& operator=(product &&) = default;
product( std::size_t in ); // TODO
bool is_integral() const {
for (auto& e:powers)
if (e.second < 0) return false;
return true;
}
template<class Scalar=std::size_t>
Scalar numerator() const {
Scalar r = 1;
for( auto& e: powers )
for (std::ptrdiff_t i = 0; i < e.second; ++i)
r *= e.first;
return r;
}
template<class Scalar=std::size_t>
Scalar denominator() const {
Scalar r = 1;
for( auto& e: powers )
for (std::ptrdiff_t i = 0; i > e.second; --i)
r *= e.first;
return r;
}
friend product& tidy(product& p) {
for (auto it = p.powers.begin(); it != p.powers.end();) {
if (!it->second)
it = p.powers.erase(it);
else
++it;
}
return p;
}
};
这是一个小阶乘引擎:
struct factorial_t {
std::vector<product> values;
factorial_t():values(2) {}
product const& operator()( std::size_t in ) {
if (values.size() > in) {
return values[in];
}
values.push_back( (*this)(in-1)*product{in} );
return values.back();
}
};
factorial_t factorial;
这对于荒谬的值来说是完全精确的。
numTrees 然后变成:
template<class Scalar=std::size_t>
Scalar numTrees(std::size_t n) {
auto res = factorial(2 * n) / ((factorial(n + 1))*factorial(n));
return res.numerator<Scalar>();
}
剩下要做的就是编写以std::size_t为主要因素的代码。
struct factor_t {
std::vector<std::size_t> primes;
factor_t():primes{2,3,5,7,11,13,17} {}
bool make_primes( std::size_t up_to ) {
if ((primes.back()+2) > up_to)
return false;
bool added_prime = false;
for (std::size_t x = primes.back()+2; x < up_to; x += 2) {
bool found = false;
for (auto p:primes)
{
if (p*p > x) break;
if (x%p) continue;
found = true;
break;
}
if (found)
primes.push_back(x);
added_prime = added_prime || found;
}
return added_prime;
}
product operator()( std::size_t in ) {
product r;
for (auto&& prime:primes)
{
while (!(in%prime)) {
r.powers[prime]++;
in /= prime;
}
}
// are there more primes to apply?
if (make_primes(std::sqrt(in)))
{
r *= (*this)(in);
}
else if (in != 1)
{
// in is a prime
r.powers[in]++;
}
return r;
}
};
factor_t factor;
product::product( std::size_t in ):
product(factor(in))
{}
和bob is your uncle。
(有趣的是,我写的产品代码意外地与0 一起“工作”,因为它factor 将0 误认为是素数,而product 与正0 因子具有@987654334 @ of 0。如果你除,你最终会在分母中得到一个0,如果他们取消我假装它永远不会发生。但是,一般来说,不要给产品类提供0。)