如何实现java的四则运算

很多语言底层对四则运算都有内部封装, 我们还是要重复造下轮子,不为别的, 就是为了面试可以多装一分 b, 假设你已经了解了什么是二进制, 什么是异或, 什么是移位运算, 什么是与, 这些不懂就别硬上(先区了解下),小心走火入魔
加法运算
  • 加法可以说是所有运算的基础, 有了加法,其他的减,乘, 除都可以用加法为基础进行
  • 废话不说, 直接lu代码, 涉及思路都写在代码注释里
public class Addition {
    /**
     * 先sum, 后进位, 无进位,return sum
     * 使用递归进行加法计算
     *
     * 以13 + 9的8位二进举例
     *    00001101
     *  + 00001001
     *-----------------
     *    00000100 :只求和(a ^ b)的结果
     *    00010010 :只求进位(a & b << 1)的结果;
     *------------------
     *    00010110 :对上面结果继续求和
     *    00000000 : 此时没有了进位,所以结果就是上面的sum
     * @param a
     * @param b
     * @return
     */
    public int sumRecursive(int a, int b){
        if(b == 0){
            return a;
        }else{
            return sumRecursive(a ^ b, (a & b) << 1);
        }
    }

    /**
     * 循环实现加法计算
     * @param a
     * @param b
     * @return
     */
    public int sumLoop(int a, int b){
        int sum = a;
        int carry = b;
        while (carry != 0){
            int temsum = sum;
            sum = sum ^ carry;
            carry = (temsum & carry) << 1;
        }
        return sum;
    }
}
public class TestAddition {
    @Test
    public void testAddition(){
        Addition addition = new Addition();
        Assert.assertEquals(22, addition.sumLoop(13, 9));
        Assert.assertEquals(22, addition.sumLoop(9, 13));

        Assert.assertEquals(22, addition.sumRecursive(13, 9));
        Assert.assertEquals(22, addition.sumRecursive(9, 13));
    }
}
减法运算
  • 减法就是加法的逆运算
  • 一个正数的补码就等于它的相反数, 一个数求俩次补码还等于它自己
public class Subtraction {

    /**
     * 减法就是加法的逆运算,
     * 原码 补码关系按照转换后10进制数来看 |原码| == |补码|
     *
     * 一个字节用数字9举例:
     * 二进制          10进制
     * 原码:00001001   9
     * 反码:11110110
     * 补码:11110111   -9
     *
     * 数字 -9
     * 原码: 11110111
     * 反码: 00001000
     * 补码: 00001001 -9的补码正好是9的原码
     * @param a
     * @param b
     * @return
     */
    public int subtraction(int a, int b){
        if(b == 0){
            return a;
        }else {
            Addition addition = new Addition();
            return addition.sumRecursive(a, addition.sumRecursive(~b, 1));
        }
    }
}
public class TestSubtraction {
    @Test
    public void testSubtraction(){
        int a = 22;
        int b = 9;
        Subtraction subtraction = new Subtraction();
        Assert.assertEquals(13,subtraction.subtraction(a,b));
    }
}
乘法运算
  • 乘法运算这里写了俩种方式, 一种(v1版本)直男写的, 耿直性能低, 另一种烧脑, 时间短
  • 俩数做异或就可以确定运算符号 是 + 还是 -
  • 一个正数 &0x1 只能是0或者是1, 来确定当前二进位是0还是1
public class Multiplication {

    /**
     * 第一感觉, 乘法就是多次的加法, 将相同的数多次累计求和
     * 需要考虑运算符号, 通过俩数异或判断
     * @param a
     * @param b
     * @return
     */
    public int multiplicationV1(int a, int b){
        Addition addition = new Addition();

        //异或小于0, 则结果为负
        boolean flag = ((a ^ b) < 0);

        //对a, b求绝对值
        a = a > 0 ? a:addition.sumRecursive(~a, 1);
        b = b > 0 ? b:addition.sumRecursive(~b, 1);
        int sum = 0;
        for (int i = 0; i < b; i++) {
            //b 数量级很大时, 循环次数太多
            sum = addition.sumRecursive(addition.sumRecursive(0, a), sum);
        }

        if(flag){
            //通过判断符号位, 将绝对值转换为指定数
            return addition.sumRecursive(~sum, 1) ;
        }else{
            return sum;
        }
    }

    /**
     *  使用算法规则
     *   0100  4
     * x 1001  9
     * 100100  36
     *
     * 计算过程
     *    0100
     * x  1001
     * -----------
     *  判断 参数1 > 0: 参数1(偶数:4) & 0x1 == 0, 参数1右移动0010, 参数2左移(10010)
     *  判断 参数1 > 0: 参数1(偶数: 2) & 0x1 == 0, 参数1右移动0001, 参数2左移(100100)
     *  判断 参数1 > 0: 参数1(基数:1) & 0x1 == 1, 满足(if), sum累加当前参数2, sum(100100), 参数1右移动0000, 参数2左移(1001000)
     *  判断 参数1 !> 0, 跳出, 返回结果
     *
     *
     * 需要了解:
     * 偶数 & 0x1 == 0
     * 奇数 & 0x1 == 1
     * a^b < 0 一定符号相反
     *
     * 只要参数1不等于0就一直循环, 每次循环参数2左移一位并赋值给参数2
     * 如果参数1当前位置为1, 那么将当前参数2的值累加
     *
     * @param a
     * @param b
     * @return
     */
    public int multiplicationV2(int a, int b){
        Addition addition = new Addition();

        //异或小于0, 则结果为负
        boolean flag = ((a ^ b) < 0);

        //对a, b求绝对值
        a = a > 0 ? a:addition.sumRecursive(~a, 1);
        b = b > 0 ? b:addition.sumRecursive(~b, 1);

        int sum = 0;
        while(a > 0){
            if((a&0x1) != 0){
                //如果参数1,当前位置是1, 将目前的b累加sum
                sum = addition.sumRecursive(b,sum);
            }

            //每次循环参数2左移位
            b = b<<1;
            //第一个参数一直右移来判断当前是否为0
            a = a>>1;
        }

        if(flag){
            //通过判断符号位, 将绝对值转换为指定数
            return addition.sumRecursive(~sum, 1) ;
        }else{
            return sum;
        }
    }
}

public class TestMultiplication {

    private static final Multiplication multiplication = new Multiplication();
    @Test
    public void testMultiplicationV1(){
        Assert.assertEquals(-6, multiplication.multiplicationV1(-2, 3));
        Assert.assertEquals(-6, multiplication.multiplicationV1(-3, 2));
        Assert.assertEquals(6, multiplication.multiplicationV1(3, 2));
        Assert.assertEquals(6, multiplication.multiplicationV1(6, 1));

    }

    @Test
    public void testMultiplicationV2(){
        Assert.assertEquals(-6, multiplication.multiplicationV2(-2, 3));
        Assert.assertEquals(-6, multiplication.multiplicationV2(-3, 2));
        Assert.assertEquals(6, multiplication.multiplicationV2(3, 2));
        Assert.assertEquals(6, multiplication.multiplicationV2(6, 1));
    }

    @Test
    public void testPerformance(){
        long l = System.currentTimeMillis();
        System.out.println(multiplication.multiplicationV1(2, 999999999));
        System.out.println(System.currentTimeMillis() - l);//3764

        //第二种性能完虐第一种
        long l2 = System.currentTimeMillis();
        System.out.println(multiplication.multiplicationV2(2, 999999999));
        System.out.println(System.currentTimeMillis() - l2);//1
    }
}
除法运算
  • 同样除法可以看作是多次求减, 但是可能会有余数, 也有俩个版本, 直男版(v1)和动脑子版(v2)
public class Division {

    private static final Addition addition = new Addition();

    private static final Subtraction subtraction = new Subtraction();

    /**
     * 同样先来使用最脑残的方式来计算商
     * <p>
     * 商跟乘积相反, 就是用除数b不断的去扣减被除数a, 直到a < b, 这时循环次数就是商,   b-a的值就是余数
     *
     * @param a
     * @param b
     * @return
     */
    public int divisionV1(int a, int b) {

        //是否负数
        boolean islowthanzero = (a ^ b) < 0;

        //绝对值
        a = (a > 0 ? a : addition.sumRecursive(~a, 1));
        b = (b > 0 ? b : addition.sumRecursive(~b, 1));

        Subtraction subtraction = new Subtraction();
        int remainder = 0;
        int quotient = 0;
        int count = 0;
        while (a > b) {
            remainder = subtraction.subtraction(a, b);
            a = subtraction.subtraction(a, b);
            quotient++;
            count++;
        }
        System.out.println("次数:" + count);

        if (islowthanzero) {
            return addition.sumRecursive(~quotient, 1);
        } else {
            return quotient;
        }
    }

    /**
     * v1版本如果a极大, 而b极小, 会找成循环次数超多, 所以要从一个大值开始除, int类型最大值为2^32次, 如果能a除2^n次后还大于b, 那么商就是2^n次
     * 该方式性能完虐v1
     * @param a
     * @param b
     * @return
     */
    public int divisionV2(int a, int b) {
        //是否负数
        boolean islowthanzero = (a ^ b) < 0;

        //绝对值
        a = (a > 0 ? a : addition.sumRecursive(~a, 1));
        b = (b > 0 ? b : addition.sumRecursive(~b, 1));

        int remainder = 0;
        int quotient = 0;

        //int 类型数除 2^32次都等于原数字, 所以如果i == 32循环次数还是与b有关, 与v1 相同
        for (int i = 31; i >= 0; i--) {
            while (a >> i >= b) {
                remainder = subtraction.subtraction(a, b);
                a = subtraction.subtraction(a, b << i);
                quotient = addition.sumRecursive(1 << i, quotient);
            }
        }

        if (islowthanzero) {
            return addition.sumRecursive(~quotient, 1);
        } else {
            return quotient;
        }
    }
}

public class TestDivision {

    private static final Division division = new Division();
    @Test
    public void testDivisionV1(){
        Assert.assertEquals(2,division.divisionV1(5, 2));
    }

    @Test
    public void testDivisionV2(){
        Assert.assertEquals(-33,division.divisionV2(-100, 3));
    }

    @Test
    public void testDivisionPerformance(){
//        long l = System.currentTimeMillis();
//        System.out.println(division.divisionV1(999999999, 1));
//        System.out.println(System.currentTimeMillis() - l);//21428

        //第二种性能完虐第一种
        long l2 = System.currentTimeMillis();
        System.out.println(division.divisionV2(999999999, 1));
        System.out.println(System.currentTimeMillis() - l2);//1
    }
}
代码路径:

https://github.com/offline7LY/lintcoderoad/tree/master/src/main/java/com/lx/lintcoderoad/operator

参考:

http://www.cnblogs.com/kiven-code/archive/2012/09/15/2686922.html
https://www.jianshu.com/p/7bba031b11e7

水平有限,希望帮到大家

转载于:https://www.cnblogs.com/qq347061329/p/8721892.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值