递归算法(三)——不借助四则运算实现加法

问题

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

思路

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

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当中有负数,结果也是正确的,因为位运算是纯二进制运算(补码表示)。

实现

function sum(a,b)
{
    return b?sum(a^b,(a&b)<<1):a;
}
时间: 2024-10-16 22:05:34

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

随机产生三十道四则运算题程序

#include<stdio.h>#include<stdlib.h>#include<time.h>int main(){ int x,y,z,t,i; srand(time(NULL)); printf("三十道四则运算题\n"); for(i=0;i<30;i++) { x=rand()%100; y=rand()%100; z=rand()%4; switch(z) { case 0: printf("%d+%d=\n&quo

第三次作业---四则运算的进一步完善

这次作业的要求是让对上次作业的四则运算进一步改善,不能在减法中出现负数,不能出现除不尽的情况. 就像这种情况,不知道怎么规定一个数值必须是正整数,所以我们就采用最笨的方法来做,定义一个f,其值为e,d的倍数. 而d做为除数,f做为被除数,所以无论如何f都是d的e倍,而我们定义的e的取值范围又为(1,10)之间的正整数,所以不管怎么随机,f都是d的正整数倍数,不会存在有余数的情况. using System;using System.Collections.Generic;using System

第三次程序-四则运算(结对开发)

接着第二周的程序,这次程序要求结对开发,并要求在第二次的基础上添加用户输入结果判断对错的功能. 结对开发伙伴: 博客名:斗破2 姓名:王文奇 博客链接:http://www.cnblogs.com/qwer111/ 功能要求: 1.乘除可控 2.随机添加括号 3.输入结果判断正误 4.统计正确数量 5.正负,余数可控 6.去除连除误区 程序思想: 1.无乘除可用0,1表示,有乘除可再加2,3,共四种选择 2.用随机函数选择左括号放在哪个操作数前面,然后再用随机函数添加右括号,在使用循环判断去除左

随机给出三十道四则运算题目

这是课上练习,应用了随机函数,涉及是三个部分第一操作数.运算符.第二操作数,这三个部分都是随机产生的:第一.第二操作数可以应用随机函数产生符合条件的数值,运算符的产生可以在0-3,之间产生随机整数,分别代表一种运算符,即可完成要求. #include<iostream>using namespace std;void main(){ for(int x=0;x<30;x++) { int a=rand()%100; int b=rand()%100; int c=rand()%3; sw

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

/* 声明两个字符数组,用于存储大数,声明两个整数型数组便于计算,将字符数组中的元素转换为对应整数存于整数数组中,将低位放在整数数组低位,便于对齐计算 判断是否有进位,计算结果高位先输出,从数组后往前找,找到第一个不为0的数,可以设置一个标志位.*/ #include<stdio.h>#include<string.h> int main(){ int num1[1000],num2[1000],i,j,max; char ch1[1000],ch2[1000]; int flag

关于三十道四则运算题的修改(修改减法,使其被减数大于减数)

#include "stdafx.h"#include<stdio.h>#include<stdlib.h>int main(int argc, char* argv[]){ int i,j,k,sum; for(int t = 0;t<30;t++) { i = rand()%50; j = rand()%50; k = rand()%4; switch(k) { case 0: sum = i+j; printf("第%d道题:",

一个可以自动生成三十道四则运算的程序

#include<stdio.h>int main() { int i,j,k,a; for(i=0;i<13;i++){ j=rand()%(99-10+1)+10; k=rand()%(99-10+1)+10; a=rand()%(4-1+1)+1;switch(a){case 1:printf("%d+%d=\n",j,k);case 2:printf("%d*%d=\n",j,k); case 3:printf("%d-%d=\n

小学二年级三十道四则运算题目。

代码: #include <stdio.h>#include <string.h>#include <stdlib.h>#include <time.h> int main(int argc, char **argv){    int a[30],b[30];     int i,c,d;        srand((unsigned int)time(NULL));    for (i = 0; i < 30; i++)    {          

小学二年级三十道四则运算题目-扩展运算,添加指定题目数量和支持真分数运算

代码: #include<stdio.h>  #include<Windows.h>  #include<time.h>  void main()  {      int a, b, c, d,i,m,n;      float p, q;      srand(unsigned( time(NULL)));      for (i = 0; i < 30; i++)      {         a = rand() % 100;         b = ran