Java大数相乘(不使用BigInteger)
作者:黑衣侠客
一.前言
此次Java大数相乘,完全用算法实现,不使用Java大数计算的关键字方法,算法开始很难理解,不过彻彻底底的把每一步过一遍,感觉算法还是很容易的。
二.算法
我们以123乘以456为例:
在这里我们将123,和456以字符串的形式存储在String中,然后通过再将String类型转为StringBuffer类型,通过StringBuffer的reverse方法,将123,456倒置,然后存储在char[]数组中,这样就会成为654,123,然后我们知道这两个三位数相乘,得到数的长度一定小于两数长度之和,这也就是我每行画了6个格子的含义,同时我在每个乘数的下端都标记了他们在char[]数组中的存储位置。接下来看最右侧红框里的数18,他是在0位置的数字3和在另一个char数组中0位置的数字6相乘得来的,同样,靠着18的15是下标为0的数字3和下标为1的数字5相乘的来的,以此类推…
从以上叙述中,我们可以根据每个结果的下标得出一个规律,可以看出,红框的每一列的下标和都是相等的,因此我们可以得出这段代码:
for(int i =0;i<len1;i++){
for(int j =0;j<len2;j++){
result[i+j]+=(int)(num1[i]-'0')*(int)(num2[j]-'0');
//其中result[]是用来存储红框内数据每一列的和,详情见下图
}
}
result[]是用来存储18,27,28,13,4(按result的存储顺序),其中后面空的一位默认为0。
现在我们处理进位的问题:
由于我们现在采用的是倒置的,所以18其实是个位,因此,个位保留8将10进位到27里,27变为28,而28为十位,保留8,将20进位到28,28变为30,30为百位,保留0,将30进位到13,13变为16,16在保留6,再次进位,4变为5。代码如下:
int leng = result.length;
//开始取余
for(int i=0;i<leng;i++){
if(result[i]>=10){
result[i+1]+=result[i]/10;
result[i]%=10;
}
}
目前,大致思路已经差不多了,接下来,该将逆序变为正序了,通过StringBuffer的append方法,将字符串变为正序,但是,仔细看会发现,存进去的是 ‘0’,‘5’,‘6’,‘0’,‘8’,‘8’ ,首位的0,是因为我们开始定义result数组长度为6,但只使用了前5个,第六个位置默认值为0,所以在正序之后自然跑到前面来了,因此在将逆序转成正序传入StringBuffer中时,需要加一个判断,代码如下:
//开始倒置,将result中的数组倒置存入StringBuffer
StringBuffer sb = new StringBuffer();
boolean bool = true;
for(int i=leng-1;i>=0;i--){
if(result[i]==0&&bool){
continue;
}
sb.append(result[i]);
bool=false;
}
至此,大数相乘的算法思路,已经讲完了,还有一些判断正负号的方法,在具体代码中写的很清楚,虽然代码不长,但是整体思路理解起来还是挺困难的,不过现在理解了,总比开始时不知从哪下手的感觉要好得多,至少心态不崩了。接下来我会将具体代码写到下面。
三.具体代码
import java.util.Scanner;
public class question_test {
public static void main(String[] args){
Scanner sc = new Scanner(System.in);
String num1 = sc.next();
String num2 = sc.next();
String num = bigNumMultiplication(num1,num2);
System.out.println(num);
}
public static String bigNumMultiplication(String f,String s){
//获取首字母,判断是否是符号位
char signA = f.charAt(0);
char signB = s.charAt(0);
char sign = '+';
if(signA=='+'||signA=='-'){
sign = signA;
f=f.substring(1);//使f返回一个子字符串,该字符串从原始字符串的第二位开始,一直到结尾结束(substring(索引))
}
if(signB=='+'||signB=='-'){
if(signB==sign){
sign ='+';
}else{
sign ='-';
}
s=s.substring(1);
}
//将大数翻转并转换成字符数组
char[] num1 = new StringBuffer(f).reverse().toString().toCharArray();
//字符串内容较长的时候,特别是这个字符串是动态拼接的时候,用String可能发生内存不够的错误,这种情况必须用stringbuffer
char[] num2 = new StringBuffer(s).reverse().toString().toCharArray();
//StringBuffer()的reverse方法作用是:使StringBuffer中的内容进行反转
int len1 = num1.length;
int len2 = num2.length;
int [] result = new int[len1+len2];
//计算最终最大长度
//开始做乘法
for(int i =0;i<len1;i++){
for(int j =0;j<len2;j++){
result[i+j]+=(int)(num1[i]-'0')*(int)(num2[j]-'0');
//用a[i]-'0'的作用是:如果直接转a[i]为int型那么只会显示a[i]所对应的ASCLL码值,减去字符0的ASCLL码值,正好为改数字,然后再转int型
}
}
int leng = result.length;
//开始取余
for(int i=0;i<leng;i++){
if(result[i]>=10){
result[i+1]+=result[i]/10;
result[i]%=10;
}
}
//开始倒置,将result中的数组倒置存入StringBuffer
StringBuffer sb = new StringBuffer();
boolean bool = true;//该字段用于标识是否有前置0,如果是0就不需要打印或者存储下来
for(int i=leng-1;i>=0;i--){
if(result[i]==0&&bool){
continue;
}
sb.append(result[i]);
bool=false;
}
if(!sb.toString().equals("")){
if(sign=='-'){
sb.insert(0,sign);
}
}else{
sb.append(0);
}
return sb.toString();
}
}
注:大家有什么更好的思路或是想法的,欢迎在下方留言。