如何实现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