Объяснение возврата по модулю

Мне нужно некоторое объяснение относительно следующего фрагмента кода:

Он используется для преобразования десятичных чисел в код двоичных чисел: это из учебника, но это меня озадачивает.

void binary(int);

void main(void) {
int number;

cout << "Please enter a positive integer: ";
cin >> number;
if (number < 0) 
    cout << "That is not a positive integer.\n";
else {
    cout << number << " converted to binary is: ";
    binary(number);
    cout << endl;
    //cin.get();
}
}

void binary(int number) {
int remainder;

if(number <= 1) {
    cout << number;
    return;
}

remainder = number%2;
binary(number >> 1);    
cout << remainder;
//cin.get();
}

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

Что я вижу:

Он принимает число, и если число ‹= 1, оно печатает это число (0 или 1).

Но перед этим он сначала вычисляет модуль этого числа и добавляет его в остаток. Затем он перемещается немного вправо от числа или делает то же самое, пока число не станет меньше или равно 1.

Но затем он сохраняет «остаток cout» несколько раз (в зависимости от того, сколько 0/1 там рассчитано). Но как это возможно? Является ли остаток буфером? (я думал, что он перезаписывается (потому что это int), но похоже, что биты продолжают добавляться, а затем печататься несколько раз) ???

Может ли кто-нибудь объяснить мне это медленно?


person Plumbum7    schedule 17.03.2012    source источник
comment
Этот вопрос действительно о рекурсии. Как только вы поймете концепцию рекурсии, код обретет смысл.   -  person masaers    schedule 17.03.2012


Ответы (3)


Int не перезаписывается, потому что оно [int] перераспределяется как автоматическая переменная каждый раз, когда вы вызываете функцию binary() рекурсивно, поэтому, если вы вызываете ее n раза рекурсивно, вы фактически выделяете n разных intс за remainder.

Итак, это «запоминается», потому что это разные локальные переменные. Распределение обычно производится в стеке вызовов

Как это работает: давайте посмотрим на стек примера: binary(5):

|number=5, remainder = 1|
-------------------------

теперь вы повторно вызываете binary() с помощью number = 5/2=2

|number=2, remainder = 0|
|number=5, remainder = 1|
-------------------------

и снова с number = 2/2 = 1 теперь вы повторно вызываете binary() с number = 5/2=2 и получаете:

|number=1, remainder = 1|
|number=2, remainder = 0|
|number=5, remainder = 1|
-------------------------

Теперь условие остановки истинно, потому что number <= 1 -, поэтому вы печатаете number [что равно 1] и извлекаете первый элемент из стека вызовов:

|number=2, remainder = 0|
|number=5, remainder = 1|
-------------------------

и выведите 0, так как это остаток наверху стека вызовов.

и сделайте то же самое для следующего «элемента» в стеке вызовов:

|number=5, remainder = 1|
-------------------------

и выведите последний остаток, 1.

person amit    schedule 17.03.2012

Любое число можно записать как sum(ai*2^i), так и sum(bi*10^i). Я объясню для десятичных дробей, так как это понятнее. Учитывая 12345,

чтобы получить цифры, вы делаете

12345 % 10 = 5 (op1)
12345 / 10 = 1234.5 = 1234 in a int (op2)
1234 % 10 = 4 (restart)
1234 / 10 = 123.4 = 123 in a int

так далее...

в этом случае

op1 is equivalent to remainder = number%2; (modulo by the base)
op2 is equivalent to number >> 1; (division by the base. bitshifting is division by 2)

restart означает перезапуск с результатом деления. Вот почему у нас есть рекурсивный вызов с использованием binary(number >> 1); Предположим, я вызываю эту функцию с abcdef в базе 2.

binary(abcdef)
  binary(abcde)
     binary(abcd)
        binary(abc)
           binary(ab)
               binary(a)
                  cout << number;//a
               cout << remainer;//b 
           cout << remainer;//c
        cout << remainer;//d
     cout << remainer;//e
  cout << remainer;//f
person UmNyobe    schedule 17.03.2012

Поскольку binary — рекурсивная функция, у вас происходит что-то вроде этого (каждый отступ — это дополнительный уровень рекурсии):

binary call #0
calculate remainder0
    recursive binary call #1
    calculate remainder #1
        recursive binary call #2
        calculate remainder #2
            recursive binary call #3
            print number
        print remainder #2
    print remainder #1
print remainder #0

Рекурсия используется исключительно для вывода остатков в порядке, обратном вычислению, как вы можете видеть выше. Не запутайтесь с аргументом number — не имеет значения, копируется ли он при каждом рекурсивном вызове или нет. Вы могли бы точно так же использовать глобальную переменную, и она бы работала (тогда она была бы «модифицирована на месте», что вы подразумеваете под «перезаписью»).

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

Вот эквивалент без рекурсии:

#include <iostream>
#include <sstream>
#include <algorithm>

using namespace std;

string binary(int);

int main(void) {
    int number;

    cout << "Please enter a positive integer: ";
    cin >> number;
    if (number < 0)
        cout << "That is not a positive integer.\n";
    else {
        cout << number << " converted to binary is: ";
        cout << binary(number) << endl;
    }
    return 0;
}

string binary(int number) {
    stringstream ss;

    do {
        ss << number % 2;
        number >>= 1;
    } while (number >= 1);

    string s = ss.str();
    reverse(s.begin(), s.end());
    return s;
}
person Irfy    schedule 17.03.2012