如何在C ++中将基本嵌套循环转换为递归

时间:2017-11-26 21:26:45

标签: c++ loops recursion nested

我正在学习递归,我正在寻找一个如何将这个基本嵌套循环转换为递归函数的简单示例。感谢您的输入: 编辑:我提供了转换嵌套循环失败的尝试。我还无法设想递归过程,但我的研究表明这是递归格式。它不会显示输出,因为我不知道在何处放置cout线。

嵌套循环:

#include "stdafx.h"
#include<string>
#include<fstream>
#include<iomanip>
#include<vector>
#include<iostream>
#include<string.h>


using namespace std;

void recursive(int x, int y)
{
    for (int i = x; i > 0; i--)
        for (int j = y; j > 0; j--)
        {
            cout << i << " , " << j << endl;
        }
}
int main()
{
    int x, y;
    cout << "Enter 2 numbers:\n";
    cin >> x >> y;
    recursive(x, y);
    return 0;
}

我尝试转换为递归函数:

void recursive(int start, int N)
{

    for (int x = start; x < N; x++)
    {

        recursive(x + 1, N);

    }



    for (int y = start; y < N; y++)
    {

        recursive(y + 1, N);

    }
}

int Main()
{
    recursive(0,3);
    return 0;
}

3 个答案:

答案 0 :(得分:2)

如果允许多个递归函数,请为每个循环级别使用一个。这限制了递归深度到x + y实例而不是x * y实例。

#include <iostream>

using namespace std;

// inner loop    
void recursivey(int x, int y)
{
    if(y <= 0)
        return;
    cout << x << " , " << y << endl;
    recursivey(x, y-1);
}

// outer loop    
void recursivex(int x, int y)
{
    if(x <= 0)
        return;
    recursivey(x, y);
    recursivex(x-1, y);
}

int main()
{
    int x, y;
    cout << "Enter 2 numbers:\n";
    cin >> x >> y;
    recursivex(x, y);
    return 0;
}

答案 1 :(得分:1)

void recursive(int x, int y, int temp)
{
  if(x > 0) {
    if(y > 0) {
      cout << x << " " << y << endl;
      recursive(x,y-1,temp);
    }
    else {
      y = temp;
      recursive(x-1,y,temp);
    }
  }
}

到目前为止我已经找到了最好的解决方案,但它需要额外的变量才能让y回到它的原始价值。您必须通过recursive(x,y,y);

进行调用

答案 2 :(得分:0)

  

我正在寻找一个如何转换这个基本嵌套的简单示例   循环到递归函数

&#39;简单&#39;例子,当然。 (为什么不呢?)

我认为之前提交的所有3个贡献都可以。我相信我已经在我的系统上编译并运行它们。

向rcgldr致敬 - 该提交对于实现尾递归至关重要。 (我没有在其他问题上证实这个问题。)

魔数:

for-loops有3&#39;参数&#39;:start,stop和stride

 for (int v = start; v < stop; v += stride)

因为for循环具有严格的布局,所以可以容忍幻数,并且 也许需要。恕我直言,你使用2个幻数(0和-1)就可以了。

在具有更灵活布局的代码中(其他循环类型,递归等),应该不鼓励这样的幻数。

可测性:

  • 存根报告的垂直格式&#39;不容易 人工检查/比较。考虑一个水平输出&#39;显示 (提出了一种替代方案。)

  • 在性能测量期间需要抑制任何文本输出。 (提出了一种技术)

C风格与C ++风格

  • 到目前为止,只提交了C风格的功能。
  • 如果您的C ++代码没有课程,为什么还要麻烦?

我的环境

  • Ubuntu 15.10,g ++ - 5,GNU Emacs 26,性能测量使用-O3 (调试使用-O0)

  • 我的构建选项:

    g ++ - 5 -m64 -O3 -ggdb -std = c ++ 14 -Wall -Wextra -Wshadow -Wnon-virtual-dtor       -pedantic -Wcast-align -Wcast-qual -Wconversion -Wpointer-arith       --Wunused Woverloaded-virtual       -O3 dumy542.cc -o dumy542

    代码文件名&#34; dumy542.cc&#34;

摘要

  • 在提交的代码中,2个递归表单的性能与迭代表单相同。 (尾递归)

  • 我已经确认了尾递归 - 两个递归代码都运行了很长的&#39;没有堆栈溢出。 Ubuntu 15.10默认&#39;堆栈&#39;大小是8兆字节。演示使用1200万个递推深度。使用-O0构建代码时,代码堆栈会溢出。 (SO)当-O3时,没有SO。

感兴趣的演示

  • 任何第3个cmd-line-param(即&#39; m&#39;)的存在都可以启用文本输出

    ./ dumy542 3 15 m - 运行3 x 15,报告持续时间,                         和3行文本输出

    ./ dumy542 3 60米 - 适合我的全屏

注意 - 尝试使用&#39; large&#39;来启用测试输出输出将触发断言,以方便您的恢复。 (你可以删除它)

  • 只有1或2 cmd行参数,文本输出被抑制

    ./ dumy542 3 12345678 - 每次测试3次,每次测试12次以上

代码的第一部分介绍了迭代代码,以及2个尾递归代码示例。所有都基于提交的代码,我已经重构 使用ssDiag(支持抑制文本输出)和更多的横向&#39;显示技术。

注意 - 已添加一个断言(对这些示例),以防止编译器优化代码。

测试包装(使用C ++计时定时测量)如下所示。

#include <chrono>
// 'compressed' chrono access --------------vvvvvvv
typedef std::chrono::high_resolution_clock  HRClk_t; // std-chrono-hi-res-clk
typedef HRClk_t::time_point                 Time_t;  // std-chrono-hi-res-clk-time-point
typedef std::chrono::nanoseconds            NS_t;    // std-chrono-nanoseconds
using   namespace std::chrono_literals;      // support suffixes like 100ms, 2s, 30us

#include <iostream>
#include <iomanip>
#include <sstream>
#include <string>
#include <cassert>

uint64_t Kount = 1; // diagnostic only


// C style C++ function by MGT
// refactored (by DMoen) to use ssDiag and 'horizontal' display
void iterate_MGT(int x, int y, std::stringstream* ssDiag )
{
   for (int i = x; i > 0; i--) // magic numbers xStop (0) & xStride (-1)
   {
      if(nullptr != ssDiag) { (*ssDiag) << "\n  i=" << i << ": "; }
      else { assert(++Kount); } // with side-effects

      for (int j = y; j > 0; j--) // magic numbers yStop (0) & yStride (-1)
      {
         if(nullptr != ssDiag) { (*ssDiag) << j << "  "; }
         else { assert(++Kount); }  // with side-effects
      }
   }
}


// C style C++ function(s) by 'rcgldr'
// refactored (by DMoen) to use ssDiag and 'horizontal' display
void recurseY(int x, int y, std::stringstream* ssDiag)
{
   if(y <= 0) return; // magic number yStop (0)

   if(nullptr != ssDiag) { (*ssDiag) << y << "  " ; }
   else                  { assert(++Kount);    }  // with side-effects

   recurseY(x, y-1, ssDiag);  // magic number y-stride (-1)
}

void recurseX(int x, int y, std::stringstream* ssDiag)
{
   if(x <= 0) return; // magic number xStop (0)

   if(nullptr != ssDiag) { (*ssDiag) << "\n  i=" << x << ":  " ; }
   else                  { assert(++Kount);    }  // with side-effects

   recurseY (x,   y, ssDiag);
   recurseX (x-1, y, ssDiag); // magic number x-stride (-1)
}



// C++ class - created from tail recursive code (submitted by 'rcgldr')
//  by DMoen to use ssDiag and 'horizontal' display
class Recursive_t
{
public:
   static void recurse (int x, int y, std::stringstream* ssDiag)
      {
         Recursive_t rcgldr(x, y, ssDiag); // ctor
         rcgldr.recurseX();                // go
      }                                    // dtor on exit

private:
   int                m_x;
   int                m_y;
   int                m_yStart;
   std::stringstream* m_ssDiag;

   enum Constraints : int  // no need for magic numbers
   {
      xStop = 0, xStride = -1,
      yStop = 0, yStride = -1,
      ConstraintsEnd
   };

   Recursive_t () = delete;  // disallow default ctor

   Recursive_t (int xStart, int yStart, std::stringstream* ssDiag)
      : m_x      (xStart)
      , m_y      (yStart)
      , m_yStart (yStart)
      , m_ssDiag (ssDiag)
      {
      }

   void recurseX() // outer loop
      {
         if(m_x <= xStop)  return;

         if(nullptr != m_ssDiag) { (*m_ssDiag) << "\n  i=" << m_x << ":  " ; }
         else                    { assert(++Kount); } // with side-effects

         recurseY();
         m_x += xStride;
         recurseX();
      }

   void recurseY()  // inner loop
      {
         if(m_y <= yStop) { m_y = m_yStart; return; }

         if(nullptr != m_ssDiag) { (*m_ssDiag) << m_y << "  " ; }
         else                    { assert(++Kount); } // with side-effects

         m_y += yStride;
         recurseY();
      }

}; // class Recursive_t

测试代码和主要内容:

class T542_t
{
   int               m_xStart;
   int               m_yStart;

   std::string        m_rslt;
   std::stringstream* m_ssRslt;

   std::string        m_diag;
   std::stringstream* m_ssDiag;

   const uint         m_capacity;

public:


   T542_t()
      : m_xStart(0)
      , m_yStart(0)
        //
        // m_rslt - default std::string ctor ok
      , m_ssRslt (nullptr)
        // m_diag - default std::string ctor ok
      , m_ssDiag (nullptr)
      , m_capacity (1000)
      {
         m_rslt.reserve(m_capacity);
         m_ssRslt = new std::stringstream(m_rslt); // always
         //
         m_diag.reserve(m_capacity);
         // m_ssDiag only when needed

         resetTest();

         m_rslt.clear();
         assert(m_rslt.capacity() == m_capacity);
         assert(m_rslt.empty());
         assert(m_rslt.size() == 0);
      }

   ~T542_t() = default;


   int exec (int argc, char* argv[])
      {
         switch (argc)
         {

         case 2: // one parameter
         {
            m_xStart = std::stoi(std::string(argv[1]));
            m_yStart = m_xStart;
            return (test("\n  one command line parameter 'N':  duration rslts only"));   // NOTE 1
         }
         break;

         case 3: // two parameters
         {
            m_xStart = std::stoi(std::string(argv[1]));
            m_yStart = std::stoi(std::string(argv[2]));
            return (test("\n  two command line parameters, X & Y: duration rslts only"));   // NOTE 1
         } break;

         case 4: // three parameters
         {
            m_xStart = std::stoi(std::string(argv[1]));
            m_yStart = std::stoi(std::string(argv[2]));

            m_diag.clear();
            assert(m_diag.capacity() == m_capacity);

            m_ssDiag = new std::stringstream(m_diag);

            return (test("\n  three command line parameters, X & Y: duration + intermediate results"));   // NOTE 1
         } break;

         case   1:
         default :
         {
            std::cerr
               << "\n  Usage: "
               << "   argc (" << (argc-1) << ") not expected, use 1, 2, or 3 cmd line parameters.\n"
               //
               << "\n     2  (one parameter)    : test with NxN, show duration only\n"
               << "\n     3  (two parameters)   : test with MxN, show duration only\n"
               //
               << "\n     5  (three parameters) : test with MxN, show intermediate results\n"
               //
               << "\n   other                   : Usage\n"
               << std::endl;
            return 0;
         }

         } // switch

         assert(m_xStart);
         assert(m_yStart);


      } // exec (int argc, char* argv[])

private:

   int test(std::string note)
      {
         int64_t a_lim = m_xStart * m_yStart;
         assert(a_lim > 0); // wrap around possible: example:  3 x 1234567890
         std::cout << note << ",  Using  " << m_xStart << " & " << m_yStart
                   << "  (" << digiComma(std::to_string(a_lim))
                   << ")\n" << std::endl;

         if(nullptr != m_ssDiag) { // when displaying output, make sure
            assert (m_yStart <= 80);                // short line and small total
            assert((m_xStart * m_yStart) <= 2000);
         }

         if(true) // for consistent start up - ubuntu app 'load' delay? cache flush?
         {
            assert(nullptr != m_ssRslt);
            (*m_ssRslt) << "\n  \n  ::xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx ";
            testRecurse2  (); testRecurse1  (); testIterate ();
            resetTest(); // discard results
         }

         testIterate (); showTest();  resetTest();
         testRecurse1 ();  showTest();  resetTest();
         testRecurse2 ();  showTest();  resetTest();

         return 0;
      } // int test()


   void showTest()
      {
         std::cout << m_ssRslt->str() << std::endl;
         if(nullptr != m_ssDiag)
            std::cout << m_ssDiag->str() << std::endl;
      }


   void testIterate()  // C style C++ function, submitted by MGT, iterative function
      {
         assert(nullptr != m_ssRslt);
         (*m_ssRslt) << "\n  (C style C++ function)\n  ::iterate_MGT (int, int, ss&)             ";

         Time_t start = HRClk_t::now();

           ::iterate_MGT (m_xStart, m_yStart, m_ssDiag); // invoke the C++ function
         //^^ --- file scope

         auto  duration_ns = std::chrono::duration_cast<NS_t>(HRClk_t::now() - start);
         (*m_ssRslt) << "    duration:  " << std::setw(10)
                     << digiComma(std::to_string(duration_ns.count())) << " ns ";
      } // testIterate()



   void testRecurse1 () // C style C++ function, submitted by rcgldr, tail-recursive
      {
         assert(nullptr != m_ssRslt);
         (*m_ssRslt) << "\n  (C style C++ function)\n  ::recurseX (int, int, ss&)                 ";

         Time_t start = HRClk_t::now();

           ::recurseX(m_xStart, m_yStart, m_ssDiag);
         //^^ --- file scope

         auto  duration_ns = std::chrono::duration_cast<NS_t>(HRClk_t::now() - start);
         (*m_ssRslt) << "   duration:  " << std::setw(10)
                     << digiComma(std::to_string(duration_ns.count())) << " ns ";
      } // void testRecurse1 ()



   void testRecurse2 () // C++ class, tail-recursive, method
      {
         assert(nullptr != m_ssRslt);
         (*m_ssRslt) << "\n  (C++ class method)\n    Recursive_t::recurse (int, int, ss&)     ";

         Time_t start = HRClk_t::now();

           Recursive_t::recurse(m_xStart, m_yStart, m_ssDiag);
           //^^^^^^^^^ class scope

         auto  duration_ns = std::chrono::duration_cast<NS_t>(HRClk_t::now() - start);
         (*m_ssRslt) << "   duration:  " << std::setw(10)
                     << digiComma(std::to_string(duration_ns.count())) << " ns ";
      } // void testRecurse2 ()



   void resetTest() { resetRslt(); resetDiag(); Kount = 1; }

   void resetRslt() { m_rslt.clear(); // clear bufer
      if (nullptr != m_ssRslt) { m_ssRslt->str(m_rslt); m_ssRslt->clear(); } }

   void resetDiag() { m_diag.clear(); // clear buffer
      if (nullptr != m_ssDiag) { m_ssDiag->str(m_diag); m_ssDiag->clear(); } }


   // convenience - insert comma's into big numbers for readability
   std::string digiComma(std::string s)
      {  //vvvvv--sSize must be signed int of sufficient size
         int32_t sSize = static_cast<int32_t>(s.size());
         if (sSize > 3)
            for (int32_t indx = (sSize - 3); indx > 0; indx -= 3)
               s.insert(static_cast<size_t>(indx), 1, ',');
         return(s);
      }

}; // class T542_t

int main(int argc, char* argv[])
{
   T542_t  t542;
   return  t542.exec(argc, argv);
}

示例结果

  • 命令行:./ dumy542 3 15 m

    三个命令行参数,X&amp; Y:持续时间+中间结果,使用3&amp; 15(45)

    (C风格C ++函数) :: iterate_MGT(int,int,ss&amp;)持续时间:6,319 ns

    i = 3:15 14 13 12 11 10 9 8 7 6 5 4 3 2 1
    i = 2:15 14 13 12 11 10 9 8 7 6 5 4 3 2 1
    i = 1:15 14 13 12 11 10 9 8 7 6 5 4 3 2 1

    (C风格C ++函数) :: recurseX(int,int,ss&amp;)持续时间:6,639 ns

    i = 3:15 14 13 12 11 10 9 8 7 6 5 4 3 2 1
    i = 2:15 14 13 12 11 10 9 8 7 6 5 4 3 2 1
    i = 1:15 14 13 12 11 10 9 8 7 6 5 4 3 2 1

    (C ++类方法) Recursive_t :: recurse(int,int,ss&amp;)持续时间:6,480 ns

    i = 3:15 14 13 12 11 10 9 8 7 6 5 4 3 2 1
    i = 2:15 14 13 12 11 10 9 8 7 6 5 4 3 2 1
    i = 1:15 14 13 12 11 10 9 8 7 6 5 4 3 2 1

  • 命令行:./ dumy542 3 12345678

    两个命令行参数,X&amp; Y:持续时间仅为rslts,使用3&amp; 12345678(37,037,034)

    (C风格C ++函数) :: iterate_MGT(int,int,ss&amp;)持续时间:46,369,155 ns

    (C风格C ++函数) :: recurseX(int,int,ss)持续时间:43,499,411 ns

    (C ++类方法)   Recursive_t :: recurse(int,int,ss&amp;)持续时间:42,898,124 ns

持续时间备注

  • 多次运行会产生不同的结果,通常会更改哪个功能显得更快。此特定结果显示两次递归比迭代更快。