【问题标题】:Not able to figure out the mistake while calculating catlan number计算catlan数时无法找出错误
【发布时间】:2016-06-21 14:52:46
【问题描述】:

在这段代码中,我使用 catlan 数计算唯一二叉搜索树的数量。 答案一直是正确的,直到来自n>=14 的输入值n=13 答案结果减一。例如。对于n=14,我的答案是 2674439,而实际答案是 2674440。 这是代码。

#include "stdafx.h"
#include "iostream"
using namespace std;
class Solution {
public:
    double arr[20000] = {};
    double fact(int n) {
        if (n == 1 || n == 0)
          return 1;
        if (arr[n] != 0)
          return arr[n];
        return arr[n] = ceil(n*fact(n - 1));
}

int numTrees(int n) {
    int res = fact(2 * n) / ((fact(n + 1))*fact(n));
    return res;
}
};


int main()
{    
  Solution s;
  cout << s.numTrees(14)<<endl;
  return 0;
}

【问题讨论】:

  • 欢迎来到 Stack Overflow!听起来您可能需要学习如何使用调试器来逐步执行代码。使用好的调试器,您可以逐行执行您的程序,并查看它与您期望的偏差在哪里。如果您要进行任何编程,这是必不可少的工具。进一步阅读:How to debug small programs.
  • 考虑28! 的值以及可以精确表示为double 的最大整数是多少。
  • 谢谢,这很有道理。

标签: c++ c++11 visual-c++ binary-search-tree


【解决方案1】:

你的中间值之一,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 一起“工作”,因为它factor0 误认为是素数,而product 与正0 因子具有@987654334 @ of 0。如果你除,你最终会在分母中得到一个0,如果他们取消我假装它永远不会发生。但是,一般来说,不要给产品类提供0。)

【讨论】:

    【解决方案2】:

    问题出在这个函数上:

    int numTrees(int n) {
       int res = fact(2 * n) / ((fact(n + 1))*fact(n));
       return res;
    }
    

    基本上,通过将 double 值转换为 int,您会失去一些精度,因此无法得出正确的值。如果你把它改成double,问题就消失了。

    修正函数:

    double numTrees(int n) 
    {
       double res = fact(2 * n) / ((fact(n + 1))*fact(n));
       return res;
    }
    

    【讨论】:

    • 好的,我转换成int是因为在线编码论坛要求返回一个int类型。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-04-27
    相关资源
    最近更新 更多