JAVA实现高精度整数加减乘

首先,需要指出的是,java内的BigInteger类很好用,功能相当全面。像加减乘这样的基础操作都是可以胜任的。所以,在java中其实没有手动实现高精度的必要。

所以写下这篇博客的初衷并不是放在实际情况下使用,而是当做高精度算法的练习,也同时练习java编程能力。

封装类内容:

实现接口

克隆接口Cloneable,重写clone方法。便于复制

可比较接口Comparable,实现CompareTo方法,便于比较排序。
(可搭配各种数据结构使用)

成员变量

数组number,用于记录每一位,需要指出的是,该类仅支持十进制的运算。

数组长度len,用于表示数组中数字长度,便于统计和计算

符号变量sign,用于作为符号项参与运算。取值为1或-1.

成员方法:

构造方法

提供两种构造方法,用于初始化

传入整数,按位倒序转化进数组

传入字符串,检索符号,倒序存入数组

全局方法

打印方法void print,以带符号字符串的形式打印。

高精度加法void add,传入大整数,加到对象上

高精度减法void minus,传入大整数,计算减法

高精度乘法void multiply,传入大整数,计算乘法

克隆方法BigNumber clone,深克隆对象,返回克隆对象

比较方法int compareTo,与传入大整数进行比较,大1,小-1,相等0

私有方法

整理数组void sort,传入计算后的长度,计算符号并整理数组

代码

public class BigNumber implements Cloneable,Comparable<BigNumber>
{
	short[] number = new short[50000];
	int len = 0;
	int sign;
	BigNumber(int num)
	{
		if(num < 0)
		{
			num = -num;
			this.sign = -1;
		}
		else
		{
			this.sign = 1;
		}
		while(num != 0)
		{
			this.number[++len] = (short)(num % 10);
			num /= 10;
		}
	}
	
	BigNumber(String num)
	{
		this.len = num.length();
		this.sign = 1;
		for(int i = this.len - 1;i > 0;i--)
		{
			number[this.len - i] = (short)(num.charAt(i) - '0');
		}
		if(num.charAt(0) == '-')
		{
			this.len -= 1;
			this.sign = -1;
		}
		else
		{
			this.number[this.len] = (short)(num.charAt(0) - '0');
		}
	}
	
	public BigNumber clone()
	{
		BigNumber num = null;
		try {
			num = (BigNumber) super.clone();
		} catch (CloneNotSupportedException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		num.number = number.clone();
		return num;
	}
	
	public void print()
	{
		if(sign == -1)
		{
			System.out.print("-");
		}
		for(int i = len;i >= 1;i--)
		{
			System.out.print(this.number[i]);
		}
		System.out.println();
	}
	
	private void sort(int len)
	{
		while(this.number[len + 1] != 0)
		{
			len++;
		}
		
		
		if(this.number[len] < 0)
		{
			this.sign = -this.sign;
			for(int i = 1;i < len;i++)
			{
				if(this.number[i] == 0)
				{
					continue;
				}
				this.number[i] = (short)(10 - this.number[i]);
				this.number[i + 1]++;
			}
			this.number[len] *= -1;
		}
		while(len > 1 &&this.number[len] == 0)
		{
			len--;
		}
		this.len = len;
	}
	
	public void add(BigNumber num)
	{
		int len = Math.max(this.len, num.len);
		for(int i = 1;i <= len ;i++)
		{
			this.number[i] = (short)((this.number[i] * this.sign) + (num.number[i] * num.sign));
			this.number[i + 1] += this.number[i] / 10;
			this.number[i] %= 10;
			if(this.number[i] < 0)
			{
				this.number[i + 1] --;
				this.number[i] += 10;
			}
		}
		sort(len);
	}
	
	public void minus(BigNumber num)
	{
		int len = Math.max(this.len, num.len);
		for(int i = 1;i <= len ;i++)
		{
			this.number[i] = (short)((this.number[i] * this.sign) - (num.number[i] * num.sign));
			this.number[i + 1] += this.number[i] / 10;
			this.number[i] %= 10;
			if(this.number[i] < 0)
			{
				this.number[i + 1] --;
				this.number[i] += 10;
			}
		}
		sort(len);
	}
	
	public void multiply(BigNumber num)
	{
		BigNumber ans = new BigNumber(0);
		for(int i = 1;i <= this.len;i++)
		{
			for(int j = 1;j <= num.len;j++)
			{
				ans.number[i + j - 1] += (short)(this.number[i] * num.number[j] * this.sign * num.sign * ans.sign);
				ans.number[i + j] += ans.number[i + j - 1] / 10;
				ans.number[i + j - 1] %= 10;
				if(ans.number[i + j - 1] < 0)
				{
					ans.number[i + j]--;
					ans.number[i + j - 1] += 10;
				}
			}
			ans.sort(i + num.len);
		}
		number = ans.number.clone();
		sign = ans.sign;
		len = ans.len;
	}
	@Override
	public int compareTo(BigNumber o) {
		o.minus(this);
		if(o.len == 0)
		{
			return 0;
		}
		return o.sign == 1 ? -1 : 1;
	}
}

主要算法实现细节:

加法&减法

首先,为了处理负数的相加减。允许数组在计算时出现负数,并且边计算边整理的。

基本思路是尾对齐并按位直接相加减。由于在数组中数据逆序存放,所以尾对齐是默认设置。所以计算时仅需要从第一位开始至两者长度中较大者循环即可。

对每一位,取每一位的绝对值乘上该数符号作为真实值进行加减。算完后,进行整理,主要是进位和借位,保证每一位的值落在0-9上。

对于首位,允许出现负数,当首位为负数是,表示该数符号与当前符号相反,交于整理方法进行整理,传递数组长度为两者长度较长者。

整理方法

对计算完成的数进行整理,具体分为三步

第一步,从当前位开始找到最高位。方法就是从向后寻找第一个0.(当然这样的处理当当前位与最高位之间存在零的时候会出现bug,但其作为内部方法,通过在调用前对长度的准确把握,还是可以避开这个bug的。)

第二步,根据最高位整理数组并更新符号。 主要针对的,是当数组中的值为负的情况。由于在计算处理时,采用的是边算边进位。若出现负数,最终一定会出现一个负的最高位,其余位为正。当检测到最高位为负数时,翻转符号,最高位取相反数,从最低位至次高位开始翻转符号,并借位归正。例如 -1 2 9 符号为正, 整理后变为 7 1 ,符号为负。

第三步,从最高位起,计算真实长度。由于负数情况可能导致位数减少,该操作即为解决这种情况。实现方法为从最高位起,找到首个不为零的位。并处理答案为0的情况。并修正长度和符号成员变量。

乘法

乘法的高精度不能直接在原数组上进行,故定义答案变量ans。

模拟乘法竖式,每一次,取以为与另一因数相乘,取该位下标为偏移量,统计在答案数组中,并进行整理。

需要注意的是,在向答案数组中累加时,需要额外乘上答案变量的符号以确保符号统一。

你可能感兴趣的:(算法,算法,java)