嵌套for循环解决方案

Nested for-loop solution

本文关键字:解决方案 循环 for 嵌套      更新时间:2023-10-16

我做这个程序只是出于兴趣,想把它做得更好。我的问题是,我想做一个嵌套的for循环来执行迭代,但我无法理解它,我已经尝试了很多次,但我的头脑正在融化。任何帮助都将非常感激。此外,由于某些原因,在windows和openSuse上(据我所见),程序在预期输出之后打印出一些随机字符,解决这个问题将是一个很大的奖励。谢谢!

对不起,我没有说清楚,代码的重点是能够理论上生成从AAAAAAAA到ZZZZZZZZ的所有字母组合。

不,这不是作业

#include <iostream>
using namespace std;
int main()
{
    char pass [] = {'A','A','A','A','A','A','A','A'};
    while(pass[0] != '[')
    {
        pass[7]++;
        if(pass[7]=='[')
        {
            pass[6]++;
            pass[7] = 'A';
        }
        if(pass[6] == '[')
        {
            pass[6] = 'A';
            pass[5]++;
        }
        if(pass[5] == '[')
        {
            pass[5] = 'A';
            pass[4]++;
        }
        if(pass[4] == '[')
        {
            pass[4] = 'A';
            pass[3]++;
        }
        if(pass[3] == '[')
        {
            pass[3] = 'A';
            pass[2]++;
        }
        if(pass[2] == '[')
        {
            pass[2] = 'A';
            pass[1]++;
        }
        if(pass[1] == '[')
        {
            pass[1] = 'A';
            pass[0]++;
        }
        cout << pass << endl;
    }
    return 0;
}

可能像这样:

const char char_first = 'A';
const char char_last = '[';
const unsigned int passlen = 8;
while (pass[0] != char_last)
{
  ++pass[passlen - 1];
  for (unsigned int i = passlen - 1; i != 0; --i)
  {
    if (pass[i] == char_last)
    {
        ++pass[i - 1]; // OK, i is always > 0
        pass[i] = char_first;
    }
  }
}

对于打印,包括<string>并说:

std::cout << std::string(pass, passlen) << std::endl;

我冒昧地把一些幻数变成了常数。如果您打算将其重构为一个单独的函数,您将看到这样做的优点。

由于(要输出它)您使用pass作为C字符串,因此它应该以空终止。因为不是,所以打印垃圾。所以你可以把它定义为char pass [] = {'A','A','A','A','A','A','A','A',''};或者更简单的char pass[] = "AAAAAAAAA";

我会忘记自己携带,只是转换到/从数字。你在这里所做的基本上是打印一个数字,其数字范围从' a '到']',可以通过ASCII的魔力映射到0-28(为什么密码中没有^ ?)

打印任何东西的数量,然后真正归结为

#include <iostream>
#include <cmath>
using namespace std;
std::string format(long num, int ndigits) {
        if(ndigits == 0) {
                return "";
        } else {
                char digit = 'A' + num % 28;
                return format(num / 28, ndigits - 1) + digit;
        }
}
int main()
{
        for(int i = 0 ; i < powl(28,8) ; ++i) {
                cout << format(i, 8) << endl;
        }
}

如果你认真对待循环,你可能仍然想在一个字符数组中工作,而不是产生10亿个临时字符串,但原则是一样的。

首先试着找出表达式中类似

的公共部分
    if(pass[7]=='[')
    {
        pass[6]++;
        pass[7] = 'A';
    }

你应该沿着这样的思路思考:"这里总是有一个相同的数字,那里总是有一个低一个的数字"。然后,用变量替换数字的概念,找出变量的取值范围。KerrekSB给了你一个解决方案,试着从你自己的推理中得出类似的代码。

你只需要稍微调整一下while,让它适合for循环。

while(pass[0] != '[')变为for (i=0; pass[0] != '['; i++)

则可以将所有if替换为一个:

    if(pass[i+1] == '[')
    {
        pass[i+1] = 'A';
        pass[i]++;
    }

我们是如何得出这个结论的?如果你检查所有的if语句它们之间改变的只是指标。你可以很清楚地看到这个模式所以你只需要用一个变量替换下标

对于初学者来说,这绝对不是嵌套循环的情况。事实上,整个代码可以归结为:

pass = initialPattern();
while ( isValidPattern( pass ) ) {
    nextPattern( pass );
    std::cout << pass << std::endl;
}

(但是我想知道你是不是真的想在增量)。

现在你要做的就是定义pass和relevant的类型函数;你甚至可以考虑将所有内容放入类中,因为所有函数都在相同的数据实例

从你的代码判断,pass应该是带8的std::string字符;初始化可以这样写:

std::string pass( 8, 'A' );

isValidPattern显然只查看第一个字符。(我不当然这是正确的,但这就是你的代码所做的。)比如:

bool
isValidPattern( std::string const& pattern )
{
    return pattern[0] != '[';
}

,但类似于:

struct NotIsUpper
{
    bool operator()( char ch ) const
    {
        return ! ::isupper( static_cast<unsigned char>( ch ) );
    }
};
bool
isValidPattern( std::string const& pattern )
{
    return pattern.size() == 8
        && std::find_if( pattern.begin(), pattern.end(), NotIsUpper() )
                == pattern.end();
}

似乎更合适。(当然,如果你在做任何一种使用文本编码,您已经有NotIsUpper和它的兄弟你的工具箱

最后,nextPattern似乎只是一个多位数增量,其中数据以大端顺序存储。因此,下面的(经典)算法似乎是合适的:

void
nextPattern( std::string& pattern )
{
    static char const firstDigit = 'A';
    static char const lastDigit = 'Z';
    static std::string const invalidPattern( 1, '[' );
    std::string::reverse_iterator current = pattern.rbegin();
    std::string::reverse_iterator end = pattern.rend();
    while ( current != end && *current == lastDigit ) {
        *current = firstDigit;
        ++ current;
    }
    if ( current != end ) {
        ++ *current;
    } else {
        pattern = invalidPattern;
    }
}

在标准中没有正式的保证按顺序升序编码,为了最大程度的可移植性,实际上,您可能应该使用std::vector<int>,其值在range [0, 26),并将其映射到输出前的字母。这如果你把所有这些操作放在一个类中,会变得微不足道,因为内部表示对客户端代码不可见。比如:

class PatternGenerator
{
    std::vector<int> myData;
public:
    explicit PatternGenerator()
        : myData( 8, 0 )
    {
    }
    void next()
    {
        static int const lastDigit = 26;
        std::vector<int>::reverse_iterator current = pattern.rbegin();
        std::vector<int>::reverse_iterator end = pattern.rend();
        while ( current != end && *current == lastDigit - 1 ) {
            *current = 0;
            ++ current;
        }
        if ( current != end ) {
            ++ *current;
        } else {
            myData.front() = lastDigit;
        }
    }
    bool isValid() const
    {
        return myData.front() < lastDigit;
    }
    friend std::ostream& operator<<(
        std::ostream& dest, PatternGenerator const& obj )
    {
        static char const characterMap[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
        for ( std::vector<int>::iterator current = obj.myData.current();
                current != obj.myData.end():
                ++ current ) {
            dest << characterMap[*current];
        }
        return dest;
    }
};

(注意,像isValid这样的事情变得更简单,因为它们可以依赖于类不变量。)

考虑到这一点,你所要写的就是:

int
main()
{
    PatternGenerator pass;
    while ( pass.isValid() ) {
        std::cout << pass << std::endl;
        pass.next();
    }
    return 0;
}

要进行嵌套循环,需要将其从内到外翻转。

你是这样写代码的:检查最后一个符号的所有可能性,然后更改倒数第二个符号一次,然后返回,等等。这就像从1开始数到10,然后在十位上加一个1,等等。

嵌套循环以另一种方式工作:遍历第一个符号的可能性,允许内部循环每次处理其他符号的可能性。也就是说,"列出所有的数字,按顺序,在百万个数位中以0开头的,然后是以1开头的,等等"。在最外层的循环中,您只需为第一个数字设置该值,其余部分由嵌套循环处理。