位运算之美--用 +、- 和位运算实现正整数除法和取模(2)

限时加码!20+主流AI编程工具免费用 购周边加赠Coding Plan Lite,Claude Code、Cursor等即刻畅享,学习进阶更高效! 阅读详情

上一篇文章(下文中称为“文(一)”)中,我提出了一个具体的问题“只能用+,-和位运算实现整数除法(/)和取模(%)”,并整理了一些和位运算相关的题目和算法,本文将给出上述问题的一个完整的解答思路和实现。


首先思考最简单的除法实现,即循环减除数,减到不能再减为止,所减次数即所求的商,事实上这就是我们初学四则运算时对除法的定义,实现:

 1int integer_div_0(int dividend, int divisor)
 2{
 3    if(divisor == 0)
 4    {
 5        cout<<"非法参数,除零错"<<endl;
 6        exit(1);
 7    }

 8
 9    int res = 0;
10    while((dividend-=divisor)>=0)
11        ++res;
12
13    return res;
14}

很显然,这个简单的实现是非常低效的,求integer_div_0(a,b)的时间复杂度为O(a/b),当a很大b很小时,计算开销很大,有什么办法能提高效率吗?

考虑到循环减除数同时比较差是否大于等于0有点类似于在一个一个数组或是linked list中顺序搜索某个目标值,在较差的情况下近似于搜索整个问题空间,这样的开销必然很大,那么只要找到某个方法来减少需要搜索的问题空间就可以提高性能了。

于是,自然可以想到如下算法:

以除数为初始测试值,以2的指数为步长来搜索问题空间,当被除数与测试值的差小于除数时便结束搜索,若在这之前测试值大于被除数,则将被除数减去前一个测试值,并重复上述过程直到搜索结束。


举个例子,求1200/3


顺序搜索时,我们要与1200比较的数有:3,6,9,12,15,...,1998,2001,比较次数667次

以2的指数为步长搜索时,与1200比较3,6,12,24,48,96,192,384,768,1536,然后与1200-768=432再进行比较3,6,12,24,48,96,192,384,768,再取432-384=48比较3,6,12,24,48,搜索结束,比较次数共24次,比顺序搜索有很大的提高。

你可能会问,为什么要以2的指数为步长来搜索呢?答案是,这样我们就可以使用位操作来进一步提高计算效率了。下面是这个算法的实现:


 1int integer_div_1(unsigned int dividend, unsigned int divisor)
 2{
 3    if(divisor == 0)
 4    {
 5        cout<<"非法参数,除零错"<<endl;
 6        exit(1);
 7    }

 8
 9    if(dividend < divisor) return 0;
10    unsigned int k=0,c=divisor, res=0;
11    
12    for(;dividend>=c;c<<=1,k++)
13        if(dividend-c < divisor)
14            return 1<<k;
15
16    return integer_div_1(dividend-(c>>1), divisor)+(1<<(k-1));
17}


注意到最后一行的尾递归,再把代码优化为非递归如下:


 1//非递归整数除法
 2int integer_div_2(unsigned int dividend, unsigned int divisor)
 3{
 4    if(divisor == 0)
 5    {
 6        cout<<"非法参数,除零错"<<endl;
 7        exit(1);
 8    }

 9
10    if(dividend < divisor) return 0;
11    unsigned int k, c, res=0;
12
13    while(dividend > divisor)
14    {
15        for(k=0,c=divisor;dividend>=c;c<<=1,k++)
16        {
17            if(dividend-c < divisor)
18            {
19                res += 1<<k;
20                break;
21            }
        
22        }

23        if(dividend-c < divisor)
24            break;
25
26        res += 1<<(k-1);
27        dividend -= c>>1;
28    }

29
30    return res;
31}


最后,有了整数除法,取模运算就很简单了,从进行整数除法搜索商的最后一步立刻就能得到模除的余数,实现如下,为了方便起见,代码里使用C++ STL中的pair模板以同时返回商和余数:


 1//整数除法and取模,返回商和余数
 2pair<int,int> integer_div_3(unsigned int dividend, unsigned int divisor)
 3{
 4    if(divisor == 0)
 5    {
 6        cout<<"非法参数,除零错"<<endl;
 7        exit(1);
 8    }

 9
10    if(dividend < divisor) 
11        return make_pair(0, dividend);
12    unsigned int k, c, quotient=0, remainder;
13
14    while(dividend > divisor)
15    {
16        for(k=0,c=divisor;dividend>=c;c<<=1,k++)
17        {
18            if(dividend-c < divisor)
19            {
20                quotient += 1<<k;
21                remainder = dividend-c;
22                break;
23            }
        
24        }

25        if(dividend-c < divisor)
26            break;
27
28        quotient += 1<<(k-1);
29        dividend -= c>>1;
30    }

31
32    return make_pair(quotient, remainder);
33}

写到这里,算是把全文的任务都完成了,读者可能觉得文(一)和文(二)里讲到的一些题目的关系不大,但我写这两篇文章的目的就是想强调位运算的作用,或者说二进制的美,很多时候如果我们换个角度,用二进制来思考问题,也许会突然“Aha!Insight!”,从而得到一个优美的解答。


原文出处:http://www.cppblog.com/xiaoyisnail/archive/2009/09/21/96883.html

运算 ,变小 1.加法 (A+B)%P=(A%P+B%P)%P; 2.乘法 (AB)%P=(A%PB%P)%P; 以上是最简单的;;; ~减法: (A-B)%P=(A%P-B%P+P)%P; 要加上*P; ~整除: 费马小定理:(A\B\P) (A/B)%P=(A%P * B^(P-2)%P)%P: ... 阅读详情

相关推荐

位运算——用+,-位运算实现正整数除法()

<br />作者:翼帆@cppblog  <br />原文地址:http://www.cppblog.com/xiaoyisnail/archive/2009/09/21/96883.html<br />本文版权归作者cppblog共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。 <br /><br />终于有时间写本文的第二部分了。在上一篇文章(下文中称为“文())中,我提出了一个具体的问题“只能用+,-位运算实现正整数除法(/)

博学之,审问之,慎思之,明辨之,笃行之 642

用位操作实现整数的加减乘除

用位操作实现整数的加减乘除 相关位操作: 由于使用位操作,所以需要进行二进制位的相关操作: 1. 按位异或 (^) 可用于二进制数的无进位加法。 同时,异或运算还支持结合律交换律: 即 a^b^c == a^(b^c) a^b^c == c^b^a 2. 按位或 (|) 通常用于将某一位置一,也可以将某位的数出 比如 a |= 0x10 就可以将第4位置一,同时出0~3位的数值。 3. 按位与 (&) 通常用于将某一位置零或

Tai_Monster的博客 707

位运算——用+,-位运算实现正整数除法()

<br />作者:翼帆@cppblog  <br />原文地址:http://www.cppblog.com/xiaoyisnail/archive/2009/09/19/96707.html<br />本文版权归作者cppblog共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。 <br /><br />    今天看了一位师兄去年的笔经总结,其中有一题是“不许用%/来实现求任意数除以3的余数”,我想考官的目的应该是想考察学生对位运算的熟悉程

博学之,审问之,慎思之,明辨之,笃行之 639

只用位运算实现整数的加减乘除四则运算 (转载,还没有来得及整理!!!!)

听同学百度二面中,不准用四则运算操作符来实现四则运算。一想就想到了计算机组成原理上学过的。位运算的思想可以应用到很多地方,这里简单的总结一下用位运算实现整数的四则运算。 首先回忆计算机组成原理学过的内容,数字在机器ALU运算逻辑单元内部是以补码形式进行运算的,因为补码有两个优势: 1、能做到符号位数值部分一起运算,这样无需单独考虑符号。 2、能把减法运算转化为加法运算来处理。

L未若的博客 1185

位运算——用+,-位运算实现整数除法()

位运算——用+,-位运算实现整数除法()  今天看了一位师兄去年的笔经总结,其中有一题是“不许用%/来实现求任意数除以3的余数”,我想考官的目的应该是想考察学生对位运算的熟悉程度吧,于是我把题目扩展成“只能用+,-位运算实现整数除法(/)(%)”,注意:这里不能使用其它的库例程来辅助计算,如log,log10等。在思考这道题目的过程中,我又涉及到了许多二进制相关的题

zht_304的专栏 4228

位运算--用 + 、- 位运算实现正整数除法(1)

今天看了一位师兄去年的笔经总结,其中有一题是“不许用%/来实现求任意数除以3的余数”,我想考官的目的应该是想考察学生对位运算的熟悉程度吧,于是我把题目扩展成“只能用+,-位运算实现正整数除法(/)(%)”,注意:这里不能使用其它的库例程来辅助计算,如log,log10等。在思考这道题目的过程中,我又涉及到了许多二进制相关的题目,如:     判断给定的整数是不是2的整数次幂    

tkp2014的专栏 591

关于位运算

9月21日,对本文从格式到部分内容上都进行了修改 另外,鉴于某些转载没有注明出处,考虑到版权问题,特声明如下: 作者:翼帆@cppblog 原文地址:http://www.cppblog.com/xiaoyisnail/archive/2009/09/19/96707.html 本文版权归作者cppblog共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则

L影罗 479

【算法100天 | 2位运算实现加减乘除(含:LeetCode 29题 两数相除)

1、打印整数的二进制数据 2位运算求相反数 3、位运算实现加法 4、位运算实现减法 5、位运算实现乘法 6、位运算实现除法

健身变秃,coding变强 1090

详解c语言操作符(上篇)

c语言操作符上篇:操作符的分类、算数操作符、移位操作符(在这里讲了一些关于整型数据在内存中的存储)。

something for nothing的博客 1284
上一篇: Device Memory Spaces
下一篇: 位运算之美--用 + 、- 和位运算实现正整数除法和取模(1)
tkp2014
博客等级 码龄12年 137粉丝 241原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值