文章目录
大整数乘法
思路一:大整数乘法
想了一会,没想到数学的解法,于是打算用大整数乘法做。
java是支持大整数乘法的,但是直接用就没意思了,起不到练习的效果,打算手动实现一下。
UML类图与流程图
UML类图
流程图
代码
SeqAddLayer 类
public class SeqAddLayer {
private String res = null;
public SeqAddLayer() {
}
public String getRes() {
return res;
}
public void forward(String sy) {
if (this.res == null) {
this.res = sy;
return;
}
char[] x = new StringBuilder(this.res).reverse().toString().toCharArray();
char[] y = new StringBuilder(sy).reverse().toString().toCharArray();
int i = 0, j = 0, step = 0;
StringBuilder res = new StringBuilder();
while (i < x.length || j < y.length) {
int ix=0, iy=0, tmp=0;
if (i < x.length) {
ix = x[i] - '0';
i++;
}
if (j < y.length) {
iy = y[j] - '0';
j++;
}
tmp = ix + iy + step;
step = tmp / 10;
res.append(tmp % 10);
}
if (step != 0)
res.append(step);
this.res = res.reverse().toString();
}
public void reset(){
this.res = null;
}
}
CharMulSeqLayer 类
public class CharMulSeqLayer {
public char[] y;
public CharMulSeqLayer(String y) {
//翻转并转为字符数组
this.y = new StringBuilder(y).reverse().toString().toCharArray();
}
public CharMulSeqLayer() {
}
public void setY(String y) {
this.y = new StringBuilder(y).reverse().toString().toCharArray();
}
public String forward(char x) {
int step = 0;
StringBuilder res = new StringBuilder();
for (char val : y) {
int tmp = multiply(x, val, step);
step = tmp / 10;
res.append(tmp % 10);
}
if (step != 0)
res.append(step);
return res.reverse().toString();
}
public int multiply(char cx, char cy, int step) {
int ix = cx - '0';
int iy = cy - '0';
return ix * iy + step;
}
}
BigIntegerMul 类
public class BigIntegerMul {
private final CharMulSeqLayer mulLayer = new CharMulSeqLayer();
private final SeqAddLayer addLayer = new SeqAddLayer();
private char[] x;
public BigIntegerMul(String x, String y) {
this.x = new StringBuilder(x).reverse().toString().toCharArray();
mulLayer.setY(y);
};
public BigIntegerMul() {};
public void setY(String y) {
mulLayer.setY(y);
}
public void setX(String x) {
this.x = new StringBuilder(x).reverse().toString().toCharArray();
}
public String forward() {
addLayer.reset();
for (int i = 0; i < x.length; i++) {
String tmp = mulLayer.forward(x[i]);
addLayer.forward(tmp + "0".repeat(i));//左移相加
}
return addLayer.getRes();
}
}
Test 类
import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
public class Test {
public static void main(String[] args) {
//接收数据
Scanner scan = new Scanner(System.in);
List<String> nums = new ArrayList<String>();
for (int i = 0; i < 100; i++) {
nums.add(scan.next());
}
//计算大整数乘法
BigIntegerMul bigIntegerMul = new BigIntegerMul();
bigIntegerMul.setX("1");
String res = "";
for (String num : nums) {
bigIntegerMul.setY(num);
res = bigIntegerMul.forward();
bigIntegerMul.setX(res);
System.out.printf("num is %s, tmp is %s\n", num, res);
}
//统计0的个数
int count = 0;
for (int i = res.length() - 1; i >= 0; i--) {
if (res.charAt(i) == '0')
count++;
else
break;
}
System.out.println(count);
scan.close();
}
}
优化思路
接收数据进行连乘可以用分治优化一下,但是懒得写了,之后想写再写吧(大概是没以后了)。
时间复杂度分析
长度未m和n的两个字符串相乘,时间复杂度为:
T
(
n
)
=
O
(
m
n
)
+
O
(
m
)
=
O
(
m
n
)
T(n)=O(mn)+O(m)=O(mn)
T(n)=O(mn)+O(m)=O(mn)
如果说给出k个数连乘,直接遍历的时间花费是:
T
(
k
)
=
O
(
k
m
n
)
T(k)=O(kmn)
T(k)=O(kmn)。
如果使用分治对字符串连乘,那么k个数可以形成一棵深度为logk的树,总时间花费是
T
(
k
)
=
O
(
l
o
g
k
∗
m
n
)
T(k)=O(logk * mn)
T(k)=O(logk∗mn)
对比两种方法,分治的m增长速度远小于直接连乘的m,所以分治优化的速度还是很大的。
思路二:相乘后统计尾0
两个数直接相乘,统计尾0后删除尾0,可以有效避免数据的增长。
但如果数据相乘后没有0,那就不能通过了。
可以使用分治优化相乘的过程,代码暂略。
总结
以前写过c++的大整数乘法,印象中很简单,这次写的好繁琐。
java写的一塌糊涂,写的是一次性代码,拓展性和维护性极差,有空再改改吧。
补充:43. 字符串相乘
补充一个以前写的c++代码
class Solution {
public:
string multiply(string num1, string num2) {
if (num1 == "0" || num2 == "0") return "0";
int step = 0; //控制移位
string res;
for (int i = num2.size() - 1; i >= 0; i--) //模拟字符串乘法
{
string temp;
int tag = 0, num = num2[i] - '0'; //控制进位
for (int j = 0; j < step; j++) //追加0
temp += '0';
for (int j = num1.size() - 1; j >= 0; j--)
{
int n = (num1[j] - '0') * num + tag;
temp += to_string(n % 10);
tag = n / 10;
}
if (tag) temp += to_string(tag);
reverse(temp.begin(), temp.end());
res = addStrings(res, temp);
step++;
}
return res;
}
string addStrings(string num1, string num2) { //模拟字符串加法
int tag = 0, i = num1.size() - 1, j = num2.size() - 1;
string res;
while (i >= 0 || j >= 0)
{
int ans = i >= 0 ? num1[i--] - '0' : 0;
ans += j >= 0? num2[j--] - '0' : 0;
res += to_string((ans + tag) % 10);
tag = (ans + tag) / 10;
}
if (tag)
res += to_string(tag);
reverse(res.begin(), res.end());
return res;
}
};
后记
翻到了别人的代码,自己写的好蠢。贴过来有空再看
public String multiply(String num1, String num2) {
if("0".equals(num1) || "0".equals(num2)){
return "0";
}
int len1=num1.length();
int len2=num2.length();
int[] ans=new int[len1+len2];
for(int i=len1-1;i>=0;i--){
int value1=num1.charAt(i)-'0';
for(int j=len2-1;j>=0;j--){
int value2=num2.charAt(j)-'0';
int sum=ans[i+j+1]+value1*value2;
ans[i+j+1]=sum%10;
ans[i+j]+=sum/10;
}
}
StringBuilder sb=new StringBuilder();
for(int i=0;i<ans.length;i++){
if(i==0 && ans[i]==0){
continue;
}
sb.append(ans[i]);
}
return sb.toString();
}