教程--使用位运算实现除法

2,926 阅读6分钟

Division using Bitwise Operations

在这篇文章中,我们将看到如何使用位运算符>> ,而不是使用常规的除法运算符/ ,或乘法运算符* ,或模运算符% ,来除掉一个数字。

内容表

  1. 除法简介
  2. 所需的位操作的回顾
  3. 使用左移运算符的除法
  4. 时间和空间的复杂性
  5. 总结

现在我们将探讨顺位运算的算法。

除法简介

因此,这里的任务是将一个给定的数字与另一个数字相除,并返回底限值,即只是小数商,但我们应该使用位运算符,而不是像* / % 这样的常规运算符来除数。

让我们通过一个例子来看看,考虑967
96 / 7 = 13.71 ,其底限值为

13

所以,我们需要写一个函数来做这件事,但要使用逐位运算符。

回顾一下所需的位操作

在我们进入问题之前,让我们快速回顾一下位数移位运算符,因为我们要用它来解决这个问题。

比特左移运算符

左移运算符用于将给定的数字向左移动一定数量的比特,如下图所示。
Division using Bitwise Operations
但是当我们左移时会发生什么呢?让我们仔细看看。
Division using Bitwise Operations
我们知道二进制数字系统是基于2的幂。看一下表格,我们可以看到下面每个比特的位置权重是前一个比特的两倍,即它的幂增加了1。同样,当一个数字被向左推了n位时,意味着该数字被乘以2的n次方。

如图所示。25 << 1 = 50 (25 * 2 power 1)
25 << 3 = 200 (25 * 2 power 3)

因此,一般来说,如果你把一个数字向左移了n位,它就会被乘以2的n次方。

位向右移运算符

右移运算符将比特向右移动。这意味着它的作用与左移运算符完全相反,即每当我们将一个数字向右移1位时,它就将该数字除以2。

例子96 >> 1 = 48

现在,我们已经对移位运算符有了足够的了解,让我们用它们来除以一个数字和另一个数字。

使用左移运算符的除法

让我们把两个数字a = 96b = 7 。当我们用a 除以b 时,我们是在计算b 的多少倍等于a 或者b 的多少倍可以装入a 。在这种情况下,我们可以把13个b's放在a ,即a / b = 13 (注意:这里我们只计算下限值)。

我们知道,每个数字都可以表示为2的幂的总和,而且当我们把一个数字向左移动n位时,它将被乘以2 power n 。因此,我们要做的是将除数b 向左移动,并检查它是否小于或等于红利a 。如果它小于或等于红利,我们就从红利中减去它,然后把2 power n 的值加入我们的答案中。这样做,我们得到的答案是2的幂的总和,这将使我们得到所需的商。

这里我们选择31,因为一般来说,整数数据类型的大小是32位,即从0开始一直到31。

操作步骤

  1. 首先,设置答案变量,即商为0

  2. 检查是否有任何一个数字是负数,并将其存储在一个单独的变量中。

  3. 使两个数字都为正数。

  4. n = 31 最重要的位开始,循环到n = 0 最不重要的位。

    • 检查除数的移位n ,是否小于或等于红利。

      • 如果是,从红利中减去它,然后更新红利。
      • 在答案中加入2n

      ( 注意:在这里,每次条件为真的时候,红利都会被减少到提醒的位置。)

  5. 最后,用第2步的结果检查商应该是正数还是负数后,返回商。

def bit_div(a,b):

    ans = 0 # the quotient is intialized

    neg = a < 0 or b < 0 # Checking if one of the numbers is negative

    a = abs(a) # making sure both the numbers
    b = abs(b) # are positive

    for i in range(31,-1,-1): # starting our loop

        if b << i <= a  : # checking if b multiplied by 2**i is <= a 
            a -= b << i   # subtracting b << i from a
            ans += 1 << i # adding 2 power i to the answer

    # and finally checking if the output should be negative and returning it
    return ans if neg == 0 else -1 * ans

现在,让我们来分解一下这段代码。

我们得到两个数字ab 作为输入,然后我们启动一个变量ans 来存储我们的最终答案,即商。然后我们检查其中一个数字是否为负数,但我们为什么要这样做呢?因为,如果其中一个数字是负的,商也将是负的,但是,如果两个数字都是正的,或者都是负的,答案将是正的。

然后我们取两个数字的绝对值,使它们都是正数。然后我们开始我们的循环。我们从31 _最重要的位_开始,因为如前所述,一个整数的大小是32位,然后遍历到0 最小有效位。在每个迭代过程中,我们检查b << i ,即b乘以2 ,再乘以i ,是否小于或等于a ,即股息。如果是真的,我们从红利a 中减去b << i ,并更新它,然后将等于2 power i1 << i 加到答案变量ans 。我们这样做直到循环到达0

最后,我们使用变量neg 的值检查答案是负数还是正数,并以正确的形式返回。

让我们通过一个例子来了解一下,以获得更好的理解。

考虑a = 96b = 7

所以控制流程是这样的。

  1. ans = 0
  2. neg = 0 即为假(因为96或7都是正数)。
  3. 由于这两个数字都是正数,所以绝对值与数字相同。
  4. 那么循环就从i = 31
  5. 直到i = 4 ,因为7 << i 大于96 ,所以_if语句_没有被执行。
  6. i = 37 << i 的值是56 ,小于96 ,因此它进入了if语句。
    • a = 40 (a = a - b << i i.e.a = 96 - 56 )
    • ans = 8 (ans += 1 << i i.e.ans = 0 + 8 )
  7. 现在,i = 27 << i 的值是28 ,小于40 ,当前a
    • a = 12
    • ans = 12 i.e.8 + 4
  8. 现在,i = 1 再次7<<i (14) 大于a12 所以不执行。
  9. 然后i 变成0
    • a = 5
    • ans = 13 i.e.12 + 1
  10. 循环结束,我们在a 的提醒5 ,在ans 的商13 。因此,我们最后返回商ans

时间和空间复杂度

这个算法的时间复杂度将是O((log a)^2),其中a是红利。

这是因为每次左移操作都需要O(log a)的时间。简而言之,除法是基于乘法运算的,基数乘法算法的时间复杂度就是除法运算的时间复杂度。

该算法的空间复杂度为O(1)。

总结

位运算符是任何编程语言的重要组成部分之一。它们在密码学、散列函数、计算机图形等方面有很多应用。因此,拥有一个良好的位操作流程对你的编程技能来说总是一笔财富。

通过OpenGenus的这篇文章,你一定对使用位操作的除法有了完整的认识。请欣赏。