【问题标题】:Trying to combine like terms for a templated polynomial class using recursion in c++尝试使用 C++ 中的递归为模板多项式类组合类似的术语
【发布时间】:2012-10-09 17:48:41
【问题描述】:

我正在自学 C++。

我正在尝试组合多项式。为此,我定义了简单的类: Polynomial<T>Term<T>Coefficient<T>(也可能只是 complex<T>) 使用简单的值组合。我已经定义了所需的运算符重载。

多项式通过对其项进行排序进行比较 (std::sort)。

我正在处理combineLikeTerms();该方法调用时会先调用 另一个将对该术语向量进行排序的成员方法。例如:

4x^3 + 5x^2 + 3x - 4 

将是一个可能的结果排序向量。

问题

我在这个向量上使用了两个迭代器,我试图合并相邻的项 相同的顺序

假设我们排序后的初始向量是这样的:

4x^3 - 2x^3 + x^3 - 2x^2 + x ...

函数完成迭代后,临时堆栈向量将 看起来像这样 2x^3 + x^3 - 2x^2 + x ...如果我们看起来仍然有类似的术语 这需要再次重构。

我该怎么做?我正在考虑使用递归。

// ------------------------------------------------------------------------- //
// setPolynomialByDegreeOfExponent()
// should be called before combineLikeTerms
template <class T>
void Polynomial<T>::setPolynomialByDegreeOfExponent()
{
    unsigned int uiIndex = _uiNumTerms - 1;
    if ( uiIndex < 1 )
    {
        return;
    }
    struct _CompareOperator_
    {
        bool operator() ( math::Term<T> a, Term<T> b )
        {
            return ( a.getDegreeOfTerm() > b.getDegreeOfTerm() );
        } // operator()
    };
    stable_sort( _vTerms.begin(), _vTerms.end(), _CompareOperator_() );
} // setPolynomialByDegreeOfExponent

// ------------------------------------------------------------------------- //
// addLikeTerms()
template <class T>
bool Polynomial<T>::addLikeTerms( const Term<T>& termA, const Term<T>& termB, Term<T>& result ) const
{
    if ( termA.termsAreAlike( termB ) )
    {
        result = termA + termB;
        return true;
    }
    return false;
} // addLikeTerms

// ------------------------------------------------------------------------- //
// combineLikeTerms()
template <class T>
void Polynomial<T>::combineLikeTerms()
{
    // First We Order Our Terms.
    setPolynomialByDegreeOfExponent();
    // Nothing To Do Then
    if ( _vTerms.size() == 1 )
    {
        return;
    }
    Term<T> result; // Temp Variable
    // No Need To Do The Work Below This If Statement This Is Simpler
    if ( _vTerms.size() == 2 )
    {
        if ( addLikeTerms( _vTerms.at(0), _vTerms.at(1) )
    {
        _vTerms.clear();
            _vTerms.push_back( result );
        }
        return;
    }
    // For 3 Ore More Terms
    std::vector<Term<T>> vTempTerms; // Temp storage
    std::vector<Term<T>>::iterator it = _vTerms.begin();
    std::vector<Term<T>>::iterator it2 = _vTerms.begin()+1;
    bool bFound = addLikeTerms( *it, *it2, result );

    while ( it2 != _vTerms.end() )
    {
        if ( bFound )
        {
            // Odd Case Last Three Elems
            if ( (it2 == (_vTerms.end()-2)) && (it2+1) == (_vTerms.end()-1)) )
            {
                vTempTerms.push_back( result );
                vTempTerms.push_back( _vTerms.back() );
                break;
            }
            // Even Case Last Two Elems
            else if ( (it2 == (_vTerms.end()-1)) && (it == (_vTerms.end()-2)) )
            {
                vTempTerms.push_back( result );
                break;
            }
            else
            {
                vTempTerms.push_back( result );
                it += 2;    // Increment by 2
                it2 += 2;          "
                bFound = addLikeTerms( *it, *it2, result );
            }
            }
                else {
                // Push Only First One
                vTempTerms.push_back( *it );
                it++;   // Increment By 1
                it2++;         "
                // Test Our Second Iterator
                if ( it2 == _vTerms.end() )
                {
                    vTempTerms.push_back( *(--it2) );  // same as using _vTerms.back()
                }
                else
                {
                    bFound = addLikeTerms( *it, *it2, result );
                }
            }
        }
        // Now That We Have Went Through Our Container, We Need To Update It
        _vTerms.clear();
        _vTerms = vTempTerms;
        // At This point our stack variable should contain all elements from above,
        // however this temp variable can still have like terms in it.
        // ??? Were do I call the recursion and how do I define the base case
        // to stop the execution of the recursion where the base case is a
        // sorted std::vector of Term<T> objects that no two terms that are alike...
        // I do know that the recursion has to happen after the above while loop
    } // combineLikeTerms

有人可以帮我找到下一步吗?我很高兴听到显示的代码中的任何错误/效率问题。 我爱c++

【问题讨论】:

  • 哇。也许你可以把这个问题说得更中肯。这将是 TL;我担心 DR。 (另外,格式化也有帮助)
  • 你的多项式类是在哪里定义的?为什么它不作为其系数的向量在内部存储?需要模板吗?它的参数类型会不会是 double 类型?是的,我在您的示例中看到系数都是整数,但 double 更有意义,或者这是“使用模板的练习”。
  • 我冒昧地将散文删减了一些。我觉得这会帮助人们看到问题。
  • @FrancisCugler 我把它拿出来是因为我读它的方式你问这里递归是否合适。抱歉,如果这歪曲了您的问题。无论如何,我认为你有两个答案应该可以继续
  • @FrancisCugler 在 C++ 中,以_ 开头后跟一个大写字母的标识符和包含__ 的标识符是为编译器开发人员保留的,您永远不应该使用它们。您的类和函数有这么多标识符,那么为什么要使用规则禁止的 _CompareOperator_ 之类的东西呢?

标签: c++ algorithm templates recursion vector


【解决方案1】:

这是我对现代 C++ 的看法。

请注意对有效系数

的删除项的额外优化

自包含样本:http://liveworkspace.org/code/ee68769826a80d4c7dc314e9b792052b

更新:发布了这个http://ideone.com/aHuB8

的c++03版本
#include <algorithm>
#include <vector>
#include <functional>
#include <iostream>

template <typename T>
struct Term
{
    T coeff;
    int exponent;
};

template <typename T>
struct Poly
{
    typedef Term<T> term_t;
    std::vector<term_t> _terms;

    Poly(std::vector<term_t> terms) : _terms(terms) { }

    void combineLikeTerms()
    {
        if (_terms.empty())
            return;

        std::vector<term_t> result;

        std::sort(_terms.begin(), _terms.end(), 
                [] (term_t const& a, term_t const& b) { return a.exponent > b.exponent; });

        term_t accum = { T(), 0 };

        for(auto curr=_terms.begin(); curr!=_terms.end(); ++curr)
        {
            if (curr->exponent == accum.exponent)
                accum.coeff += curr->coeff;
            else
            {
                if (accum.coeff != 0)
                    result.push_back(accum);
                accum = *curr;
            }
        }        
        if (accum.coeff != 0)
            result.push_back(accum);

        std::swap(_terms, result); // only update if no exception
    }
};

int main()
{
    Poly<int> demo({ { 4, 1 }, { 6, 7 }, {-3, 1 }, { 5, 5 } });

    demo.combineLikeTerms();

    for (auto it = demo._terms.begin(); it!= demo._terms.end(); ++it)
        std::cout << (it->coeff>0? " +" : " ") << it->coeff << "x^" << it->exponent;

    std::cout << "\n";
}

【讨论】:

  • 一段完美的代码(当然)! @FrancisCugler:这更适合您现有的代码,因为这里的combineLikeTerms 界面与您的完全一样。但重点和我描述的一样:不需要回避之类的。
  • 您的combineLikeTerms: for() 应包含++first,否则将无法按预期工作。
  • @OlafDietsche 哦,啊,呵呵。通过完全删除第二个迭代器来修复,无论如何这都是不需要的。这种方式不那么令人困惑:)。谢谢
  • @FrancisCugler 如果您需要帮助/保证,这可以在 C++03 中完成:ideone.com/aHuB8
  • @jogojapan 抱歉,我在看文字时也遇到了一些问题,我的显示器快死了,我的新显示器应该会在周五到周一的某个时间出现。夹子没有握住,所以我的整个桌面看起来像一个沙漏,一切都歪了
【解决方案2】:

您需要将多项式视为一对序列(系数、变量):

[(coefficient1,variable1),(coefficient2,variable2),(coefficient3,variable3),...]

正如您所描述的,您从左到右遍历它,只要 variable 部分相同,就将两个相邻的对组合成一个(这当然假设列表已经按 变量部分!)。

现在当列表中有三个或更多元素共享它们的变量时会发生什么?好吧,那就继续组合它们。真的不需要递归或任何复杂的东西。

在迭代期间的任何时候,您都可以将当前对的 variable 部分与 variable 部分 last seen 结合起来。如果它们相同,则将它们组合并继续。如果你得到的下一对仍然与最后一次看到具有相同的可变部分,那么你再次组合它们。如果您正确执行此操作,则不应留下任何重复项。

以下是如何执行此操作的示例。它的工作原理是创建一个新的对列表,然后遍历输入列表,对于输入列表的每个项目,它决定是否将其与最后推送到新列表的项目组合,或者通过向新列表添加新元素:

#include <utility>
#include <vector>
#include <iostream>

typedef std::vector<std::pair<float,std::string>> Polynomial;

Polynomial combine_like_terms(const Polynomial &poly)
{
  if (poly.empty())
    return poly;

  /* Here we store the new, cleaned-up polynomial: */
  Polynomial clean_poly;

  /* Now we iterate: */    
  auto it = begin(poly);
  clean_poly.push_back(*it);
  ++it;
  while (it != end(poly)) {
    if (clean_poly.back().second == it->second)
      clean_poly.back().first += it->first; // Like term found!
    else
      clean_poly.push_back(*it); // Sequence of like-terms ended!
    ++it;
  }
  return clean_poly;
}

int main()
{
  Polynomial polynomial {
    { 1.0 , "x^2" },
    { 1.4 , "x^3" },
    { 2.6 , "x^3" },
    { 0.2 , "x^3" },
    { 2.3 , "x" },
    { 0.7 , "x" }
  };

  Polynomial clean_polynomial = combine_like_terms(polynomial);
  for (auto term : clean_polynomial)
    std::cout << '(' << term.first << ',' << term.second << ")\n";
  std::cout.flush();

  return 0;
}

如果需要,您可以轻松地再次对其进行模板化——我使用float 作为系数,使用strings 作为变量部分。它实际上只是一个代码示例,展示了如何在不使用递归或并行使用大量迭代器的情况下轻松完成此操作。

哦,代码是为 C++11 编写的。同样,它只是一个模型,可以针对 C++03 进行调整。

【讨论】:

  • 哈。我们想法一致。我考虑将其设为非变异算法。但我选择只使用 OP 描述的界面:)
  • 非常好的回复 :)),但是我没有 c++11 或 vs 2012 :( 我还在 Vista 上大声笑.... :( 但至少我仍然使用自动自我使用 2010 年以来的关键字。是的,我认为有一种比递归更简单的方法,但是,当我出于某种原因尝试使用上述两种方法进行操作时,我最终将其中一个术语添加了两次,当我分支到使用如果语句那么我有问题,如果大小是偶数或奇数。大声笑这应该有很大帮助:)
猜你喜欢
  • 1970-01-01
  • 2011-11-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多