Решение для вложенного цикла - PullRequest
0 голосов
/ 03 ноября 2011

Я сделал эту программу просто из интереса и хотел сделать ее лучше. Моя проблема в том, что я хочу сделать вложенный цикл for для выполнения итераций, но я не могу разобраться с этим, я пробовал много раз, но моя голова тает. Любая помощь будет принята с благодарностью. Также по какой-то причине в windows и openSuse (из того, что я видел) программа выводит некоторые случайные символы после ожидаемого вывода, решение этого было бы отличным бонусом. Спасибо!

Извините, я не прояснил ситуацию, смысл кода в том, чтобы иметь возможность теоретически генерировать каждую комбинацию букв от AAAAAAAA до ZZZZZZZZ.

1) Нет, это не домашнее задание

#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;
}

Ответы [ 7 ]

3 голосов
/ 03 ноября 2011

Может быть так:

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;

Я позволил себе преобразовать несколько магических чисел в константы.Если вы когда-нибудь захотите перестроить это в отдельную функцию, вы увидите достоинства этого.

3 голосов
/ 03 ноября 2011

Поскольку (для ее вывода) вы используете pass в качестве строки C, она должна заканчиваться нулем.Поскольку это не так, мусор печатается.Таким образом, вы можете определить это как: char pass [] = {'A','A','A','A','A','A','A','A','\0'}; или проще char pass[] = "AAAAAAAAA";

2 голосов
/ 03 ноября 2011

Я бы забыл о самостоятельном ношении и просто конвертировал в / из номеров.Здесь вы в основном печатаете числа, чьи цифры варьируются от «A» до «]», сопоставляются с 0-28 с помощью магии ASCII (почему нет ^ в паролях?)

Печать номерачего-то действительно сводится к

#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;
        }
}

Возможно, вы все равно захотите работать в массиве символов вместо создания миллиарда временных строк, если вы серьезно относитесь к циклу, но принцип остается тем же.

0 голосов
/ 03 ноября 2011

Чтобы сделать вложенные циклы, нужно вывернуть их наизнанку.

Вы написали код следующим образом: просмотрите все возможности для последнего символа, затем измените второй последний раз и вернитесь назад, и т. Д. Это все равно, что считать от 1 до 10 и поставить 1 в столбце десятки и т. д.

Вложенные циклы работают по-другому: изучите возможности для первого символа, позволяя внутренним циклам каждый раз заботиться о возможностях для других символов. то есть, «перечислите все те числа в порядке, которые начинаются с 0 в месте миллионов, затем те, которые начинаются с 1 и т. д.». В самом внешнем цикле вы просто устанавливаете это значение для первой цифры, а вложенные циклы заботятся об остальных.

0 голосов
/ 03 ноября 2011

Для начала, это определенно не тот случай для вложенного цикла. По факту, весь ваш код сводится к:

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

(Но мне интересно, если вы действительно не хотите сделать вывод до того, как приращение.)

Теперь все, что вам нужно сделать, это определить тип прохода и соответствующий функции; Вы могли бы даже рассмотреть положить все в классе, так как все функции работают на тот же экземпляр данных.

Судя по вашему коду, pass должно быть std::string с 8 персонажи; инициализация может быть написана:

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> со значениями в диапазон [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;
}
0 голосов
/ 03 ноября 2011

Вам просто нужно немного поиграть со своим временем и настроить его на цикл.

while(pass[0] != '[') становится for (i=0; pass[0] != '['; i++)

тогда вы можете заменить все if только одним:

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

        pass[i+1] = 'A';
        pass[i]++;
    }

Как мы пришли к такому выводу? Хорошо, если вы проверите все свои операторы if, все, что меняется между ними, это индексы. Вы можете ясно видеть этот шаблон, поэтому вы просто заменяете индексы переменной.

0 голосов
/ 03 ноября 2011

Сначала попробуйте найти общие части в выражениях, выглядящих так:

    if(pass[7]=='[')
    {
        pass[6]++;
        pass[7] = 'A';
    }

Вы должны подумать в одной строке: «Здесь всегда одно и то же число, а число там на одну меньшую».Затем вы заменяете это число на переменную и выясняете, в каком диапазоне находится переменная.KerrekSB дал вам решение, попробуйте получить аналогичный код по вашим собственным соображениям.

...