4

我想要一个准确地解释以下功能。

void ToBin(int n){
  if (n>1)
     ToBin( n/2 );
  printf( "%d", n%2 );
}

例如:
如果我输入7了,会发生什么(函数行为)转换为二进制111

请我想一步一步地解释我理解。


编辑
相同的功能,但打印结果更清晰。

void ToBin( int n ){
   printf("%d/2= %d -> %d\n", n, n/2 ,n%2);
   if (n>1)
      ToBin( n/2 );
}
4

10 回答 10

5

特别是7:

首先,n = 7,因此 n > 1。因此,在 n / 2 = 3 上再次调用 ToBin。

现在,n = 3,所以 n > 1。因此,在 n / 2 = 1 上再次调用 ToBin。

现在,n = 1,所以 n 不大于 1。因此,打印 1 % 2 = 1,并且控制跳回到前一帧。

这里,n = 3,打印 3 % 2 = 1,控制再次跳回一帧。

这里,n = 7,并且 7 % 2 = 1 被打印出来,并且由于函数已经离开并且没有剩余的 ToBin 帧,控制权返回给最初调用 ToBin 的函数。

通常,此功能的工作原理如下:

数字的二进制表达式等于该数字一半的二进制表达式,附加原始数字的最低位。

因此,该函数重复获取输入一半的下限的二进制表达式,直到该值是单个位。然后,作为数字的最高有效位的那个位被打印出来,随后,每个位都按重要性降序排列。

于 2013-08-07T05:09:23.333 回答
3

这是一个递归函数调用。n%2基本上只是砍掉整数的最后一位(二进制表示)。(2 因为二进制是基数 2,所以有两个可能的基数)。n/2删除最低有效位(通过整数除法)并将其传递到下一个递归级别。

因此,每个递归级别都会删除它传递的整数的最低有效位,直到在某个级别整数不是 1。如果它是 1,则if失败并且不再进行递归调用。现在递归回滚。首先printf执行最后一个递归调用,它将打印调用者传递的整数的最低有效位。因此,在级别上,数字k的第k^ 位基本上被打印出来。因为在k第 th 级k-1,递归函数调用链上的最低有效位已被删除,并且在第 1 级的调用k具有已删除最低有效位的整数k-1。因此在每个层面printf打印它传递的整数的 LSB,直到递归回滚到顶部。

这是一个图形表示。为n = 10. 那么 的二进制n1010。尝试用不同的值自己绘制此类图表,以更好地理解。

          ToBin (10 = 1010)          print 10%2 = 0
             |                           ^
             call ToBin (10/2)           |
             |                           |
             V                           |
          ToBin (5 = 101)            print 5%2 = 1
             |                           ^
             call ToBin (5/2)            |
             |                           |
             V                           |
          Tobin (2 = 10)             print 2%2 = 0
             |                           ^
             call ToBin (2/2)            |
             |                           |
             V                           |
          ToBin (1 = 1)              print 1%2 = 1
             |                           ^
             if condition fails,         |
             |  roll back.               |
             |                           |
             |                           |
             +------>------>------>------+
于 2013-08-07T05:25:43.393 回答
2

为了找到一个数字的 base-2 表示,我们寻找b0...bn这样的位

n = bn * 2^n + b_(n - 1) * 2^(n - 1) + ... + b1 * 2^1 + b0 * 2^0

现在我们专注于寻找b0, b1, ..., bn. 注意

(bn * 2^n + b_(n - 1) * 2^(n - 1) + ... + b1 * 2^1 + b0 * 2^0) % 2 = b0

因为b0 * 2^0 % 2 = b0bj * 2^j % 2 = 0何时j >= 1因为2^j % 2 = 0如果j >= 1。所以,

       n = bn * 2^n + b_(n - 1) * 2^(n - 1) + ... + b1 * 2^1 + b0 * 2^0 
=> n % 2 = (bn * 2^n + b_(n - 1) * 2^(n - 1) + ... + b1 * 2^1 + b0 * 2^0) % 2 = b0

我们发现了b0 = n % 2这个关键事实第一

现在,让我们除以 2:

n / 2 = (bn * 2^n + b_(n - 1) * 2^(n - 1) + ... + b1 * 2^1 + b0 * 2^0)
      = bn * 2^(n - 1) + b_(n - 1) * 2^(n - 2) + ... + b1 * 2^1

现在,让我们在这里停下来。让我们仔细看看n / 2. 请注意,它完全等于n仅去掉最后一位的二进制表示。那是

    n = bn b_(n-1) ... b1 b0
n / 2 =   b_n b_(n-1) ... b1

这是第二个关键事实

所以,让我们把我们学到的东西放在一起。

  1. 的二进制表示n是附加n / 2了二进制表示的最后一位的二进制表示。这是来自第二个关键事实n

  2. 的二进制表示的最后一位数字n可以通过计算来计算n % 2这是第一条关键事实

除了一种情况:当n = 0. 在这种情况下, 的二进制表示n0。如果我们尝试使用除以 的规则2,我们将永远不会停止除以 2。因此,我们需要一个规则来捕获 when n = 0

因此,要计算 的二进制表示n,首先计算 的二进制表示n / 2,然后附加 的结果,n % 2但一定要处理 时的情况n = 0。让我们把它写成代码:

// print binary representation of n
void ToBin(int n) {
    if(n > 1) { // this is to handle zero!
        ToBin(n / 2); // this will print the binary representation of n
    }
    print(n % 2); // this will print the the last digit
}
于 2013-08-07T05:22:45.953 回答
2
order    call    print
  1    ToBin(7)    1
          ↓        ↑
  2    ToBin(3)    1
          ↓        ↑
  3    ToBin(1) →  1

这就是递归

也许你应该找到它的递归方程。

于 2013-08-07T05:19:47.167 回答
1
ToBin(7)
n=7 if(7>1)
    ToBin(7/2=3) call ToBin(3)
    {
     ToBin(3)
     n=3 if(3>1)
     ToBin(3/2=1) call ToBin(1)
     {
       ToBin(1)
       n=1 if(1>1)
         false
       print n%2=> 1%2= 1
      }     
    print n%2=> 3%2= 1
    }
print n%2=> 7%2= 1
于 2013-08-07T05:19:21.390 回答
1

你输入 7 所以

call 1) 7>1 true ,call ToBin(7/2)=ToBin(3.5) , pending print(7%2)=1
call 2) 3>1 true ,call ToBin(3/2)=ToBin(1.5) , pending print(3%2)=1
call 3) 1>1 false ,dont call ToBin(1/2), print(1%2)=1

,

print 1
pop function activation record for call 3)
pending print 1
pop function activation record for call 2)
pending print 1
pop function activation record for call 1)
So your output is 111
于 2013-08-07T05:11:52.343 回答
1

递归可能比其他情况更难理解。首先,请注意,如果我们重新排序语句:

void ToBin(int n){
  printf( "%d", n%2 );
  if (n>1)
     ToBin( n/2 );
}

它现在将以相反的顺序打印数字。当然,这不是您想要的,但现在递归调用已结束,转换为迭代形式更容易:

void ToBin(int n) {
  do {
    printf( "%d", n%2 );
    n = n/2;
  } while(n > 1);
}

从这段代码中,你可以看到:

  • 打印模二的数字。
  • 将数字除以二。
  • 如果数字大于 1,则重复。

考虑给定一个以 10 为底的数字,我们可以除以 10。余数和被除数分别是最低有效位和移动一位数的数字。这也适用于二进制:我们不是除以 10,而是除以 2。

本质上,算法是这样的:

  • 打印最低有效数字。
  • 将数字移动一位数。
  • 如果数字大于一,请重复。

如果您以递归形式编写此代码,然后交换递归和打印,您将按照从最重要到最不重要的顺序打印带有数字的数字。瞧。

于 2013-08-07T05:13:54.947 回答
0

其递归打印余数(% 操作)按以下方式除以 2

ToBin()正在调用自身,直到输入参数n大于 1

 2 | 7
 ------     remainder   /|\
 2 | 3  ---> 1           |    down to top
 ------                  |
   | 1  ---> 1           |
     -------------------->
于 2013-08-07T05:13:10.653 回答
0

如果 n 为 7,则每一层的递归调用为

 1.ToBin(7)--> 2. ToBin(3) --> 3. ToBin(1)

现在返回值,在第一种情况下它是 1,因为 7%2 是 1,


1 在第二种情况下 3%2 是 1,在第三种情况下 1 也是 1%2 是 1。

因此 111 是输出

注意:每个递归调用在其堆栈中都有自己的 n,因此 n 在第一次调用时为 7,在第二次调用时为 3,在第三次调用时为 1。

于 2013-08-07T05:13:22.963 回答
0

该函数ToBin使用递归(函数调用自身)并使用此算法

要将基数为 10 的整数转换为等效的基数为 2(二进制)的数字,该数字除以 2,余数为最低有效位。(整数)结果再次除以二,其余数是下一个最低有效位。重复此过程,直到商变为零。

于 2013-08-07T05:13:41.483 回答