问题

求两个整型变量的和,不能使用四则运算,但可以使用位运算。

思路

以二进制形式,考虑两个整数相加:

a = 01101001b

b = 11100111b

s =  ????????

一个常见的结论是:

我们在进行二进制加法的时候(类比十进制加法),若无进位,则直接写上结果;若有进位,则写一个进位标志,作为累计,在高位计算时加入。

也就是说,结果是由截断二进制位和及其进位标志相加而得的

思考:两个数的和 == 截断二进制位和+进位和,这种转化分解是否可行?

证明:讨论两个1bit相加出现的全部情况,一共有四种。

  1. 0 + 0 = 0 + 00
  2. 0 + 1 = 1 + 00
  3. 1 + 0 = 1 + 00
  4. 1 + 1 = 0 + 10

等式左边为一位相加,右边为截断结果s和进位c相加,可以看出,等式成立,因此这种转化方式可行

将等式记为:

a + b = s + c

我们来寻求a、b和s、c的关系。

很容易证明:

  • s=a^b,^表示异或,即a、b不同时返回0,相同时返回1。
  • c=(a&b)<<1,&表示与,即a、b都为1时返回1,否则返回0;<<表示二进制左移,<<1即乘以2。

这样,转化关系式就得出了:

a + b = a ^ b + (a & b) << 1

但是,在转化关系求出后,还存在一个问题:既然存在转化关系,就有递归调用(迭代),那递归的出口是什么?

如果可以求出递归出口,那么这个方法就完美了。

这相当于问:

a 和 b 有没有终止条件(或者说极限)?

我们假设a和b都是n位二进制数(补码表示,这样a和b的正负性不影响结果),令c=a+b。

初始时,用二进制表示,a = a[n-1] a[n-2] … a[1] a[0],b = b[n-1] b[n-2] … b[1] b[0],c = c[n-1] c[n-2] … c[1] c[0]。

第一次迭代。a=a^b;b=(a&b)<<1。b必为偶数,这样b[0]就是0,又由于a+b=c,故a[0]=c[0]-b[0]=c[0]。

结果为:

a = a[n-1] a[n-2] … a[2] a[1] c[0]

b = b[n-1] b[n-2] … b[2] b[1] 0

第二次迭代。对于a和b最后一位,运算后结果保持不变,所以问题就归结为a[n-1]~a[1]和b[n-1]~b[1]间的迭代。

结果为:

a = a[n-1] a[n-2] … a[2] c[1] c[0]

b = b[n-1] b[n-2] … b[2] 0 0

……

第n次迭代。递推可得:

a = c[n-1] c[n-2] … c[1] c[0]

b = 0 0 … 0 0

所以,a=c,b=0,这就是收敛条件,即递归出口。证毕。

我们还可以得出其他结论:

  1. 迭代次数的上界是n,超过n,立即收敛。
  2. 假如b的二进制表示的前缀0有许多,那么收敛速度将大大增加。
  3. 在迭代过程中,a呈指数增加,b呈指数减少,比率近似于2。
  4. 假如a和b当中有负数,结果也是正确的,因为位运算是纯二进制运算(补码表示)。

实现

  1. function sum(a,b)
  2. {
  3. return b?sum(a^b,(a&b)<<1):a;
  4. }

递归算法(三)——不借助四则运算实现加法的更多相关文章

  1. 剑指Offer(书):不用四则运算做加法

    题目:写一个函数,求两个整数之和,不得使用四则运算位运算. package com.gjjun.jzoffer; /** * 写一个函数,求两个整数之和,不得使用四则运算 * * @author gj ...

  2. Opengl_入门学习分享和记录_03_渲染管线(三)借助顶点数组对象VAO提高绑定属性效率

    目前我们已经知道了,如果想要顶点着色器解释理解我们的输入数据,就必须要按照以下繁琐的步骤:第一步:将输入的数据复制一份到缓冲区,供OpenGL使用.而这块新出现的区域由VBO管理和表示.(若有多个输入 ...

  3. 大数四则运算之加法运算--------C语言版(未考虑负数)

    /* 声明两个字符数组,用于存储大数,声明两个整数型数组便于计算,将字符数组中的元素转换为对应整数存于整数数组中,将低位放在整数数组低位,便于对齐计算 判断是否有进位,计算结果高位先输出,从数组后往前 ...

  4. JavaScript中交换两个变量的值得三种做法(代码实现)

    javascript在编程时经常会涉及到如何交换两个变量的值,例如常见的冒泡排序,快速排序等:下面我讲根据自己近期所学总结几种常见的交换两个变量值的方法: 方法一:借助第三方变量交换两个变量的值 va ...

  5. [剑指Offer]65-不用加减乘除做加法

    题目 写一个函数,求两个整数之和,要求在函数体内不得使用+.-.*./四则运算符号. 题解 用位运算模拟加法的三步: 无进位加法:异或运算. 进位:与运算再左移一位. 直到进位为0结束. 代码 pub ...

  6. Sql三种分页方法

    --分页三种方法--第一种 ROW_NUMBER() OVER( ORDER BY OrgID) AS indexs 大于pagesize*pageindex,少于等于pagesize*(pagein ...

  7. spring学习总结——装配Bean学习三(xml装配bean)

    通过XML装配bean Spring现在有了强大的自动化配置和基于Java的配置,XML不应该再是你的第一选择了.不过,鉴于已经存在那么多基于XML的Spring配置,所以理解如何在Spring中使用 ...

  8. Java实现小学四则运算练习系统(UI)

    github项目地址 :https://github.com/feser-xuan/Arithmetic_test3_UI 小伙伴的博客链接:http://www.cnblogs.com/fukang ...

  9. github下载源码的三种方式

      从github上下载源码的三种方式 CreationTime--2018年6月7日15点21分 Author:Marydon 1.情景展示 2.实现方式 方式一:直接点击"Downloa ...

随机推荐

  1. tcpip

    netstat -anp | grep 8099 kill -9 8099 服务端端口状态 1.LISTENING状态 FTP服务启动后首先处于侦听(LISTENING)状态. 2.ESTABLISH ...

  2. Lua JSONRPC学习笔记

    JSON RPC JSON RPC 为利用json数据格式来执行远程调用方式, 作用同xmlrpc,不过与xmlrpc相比, jsonrpc更加轻量,json更加节省数据量,更加可读性高. 官网网站: ...

  3. vue.js慢速入门(2)

    4.组件使用基础 什么是组件?组件可以理解为可重用的自定义HTML. 可以使用一堆组件来构造大型应用,任意类型的应用界面都可以抽象为一个组件树: 可以把组件代码按照template.style.scr ...

  4. [原创]java WEB学习笔记101:Spring学习---Spring Bean配置:IOC容器中bean的声明周期,Bean 后置处理器

    本博客的目的:①总结自己的学习过程,相当于学习笔记 ②将自己的经验分享给大家,相互学习,互相交流,不可商用 内容难免出现问题,欢迎指正,交流,探讨,可以留言,也可以通过以下方式联系. 本人互联网技术爱 ...

  5. 原生js与css3结合的电风扇

    最近学习了css3,就琢磨做些东西练练手,下面是自己写的一个电风扇,使用了原生js中的定时器和css3的一些属性 <!doctype html> <html lang="e ...

  6. C#.Net 中的 new 的几个用法

    之前面试的时候,有人问过我这个问题,当时自己只记得两种.后来上msdn看了下,发现有三种,第三种用法基本没怎么用过 这里先贴出来: 三种用法如下: 在 C# 中,new 关键字可用作运算符.修饰符或约 ...

  7. zTree插件之多选下拉菜单代码

    zTree插件之多选下拉菜单代码 css和js <!--ztree树结构--> <link rel="stylesheet" type="text/cs ...

  8. SharpZipLib 文件/文件夹压缩

    一.ZipFile ZipFile类用于选择文件或文件夹进行压缩生成压缩包. 常用属性: 属性 说明 Count 文件数目(注意是在ComitUpdat之后才有) Password 压缩包密码 Siz ...

  9. RF内置库-----内置库的学习过程总结

    前段时间充忙的学习RF,系统学习完之后就开始动手做各种接口的测试,虽然各类的接口测试基本能跑通了,但是重复造车的问题存在太明显.RF本身内置库就已经比较丰富,比如不需要import直接就加载到内存的B ...

  10. 将java项目转换成Web项目

    http://www.cnblogs.com/kaige123/p/5866446.html 在项目上点击右键,进入Properties配置,点击Project Facets,再点击Convert t ...