学习分治法+解决大数乘法问题

大整数乘法

分治法

将一个规模为N的问题,分解成K个规模较小的子问题,这些子问题相互独立且月原问题性质相同,求解出子问题的解,合并得到原问题的解

java中的BigInteger总结

(1)程序有时需要处理大整数,java.math包中的BigInteger类提供任意精度的整数运算,可以使用构造方法:

public BigInteger(String VAL)构造一个十进制的BigInteger对象,该构造方法可以发生NumberFormatException异常,也就是说,字符串参数VAL中如果含有非数字字符就会发生NumberFormatException异常。

(2)BigInteger类的常用方法:

public BigInteger add(BigInteger val) 返回当前大整数对象与参数指定的大整数对象的和

public BigInteger subtract(BigInteger val) 返回当前大整数对象与参数指定的大整数对象的差

public BigInteger multiply(BigInteger val) 返回当前大整数对象与参数指定的大整数对象的积

public BigInteger devide(BigInteger val) 返回当前大整数对象与参数指定的大整数对象的商

public BigInteger remainder(BigInteger val) 返回当前大整数对象与参数指定的大整数对象的余

public int compareTo(BigInteger val) 返回当前大整数对象与参数指定的大整数对象的比较结果,返回值是1、-1、0,分别表示当前大整数对象大于、小于或等于参数指定的大整数。

public BigInteger abs() 返回当前大整数对象的绝对值

public BigInteger pow(int exponent) 返回当前大整数对象的exponent次幂。

public String toString() 返回当前当前大整数对象十进制的字符串表示。

public String toString(int p) 返回当前大整数对象p进制的字符串表示。

但是这篇文章不用这个java内置的方法来实现大数乘法

 import java.util.*;
 import java.math.*;
  
 public class NumMul{
      public static void main(String args[]){
          Scanner cin = new Scanner(System.in);
          BigInteger a, b;
          while(cin.hasNext()){
              a = cin.nextBigInteger();
             b = cin.nextBigInteger();
            System.out.println(a.multiply(b));
         }
    }
 }

分治算法特征分析

分治法能解决的问题一般具有以下几个特征:

  1. 该问题的规模缩小到一定程度就可以容易的解决;

  2. 该问题可以分解为若干个规模较小的相同问题,即该问题具有最优子结构性质;

  3. 利用该问题分解出子问题的解,可以合并为该问题的解;

  4. 该问题所分解出的各个子问题是相互独立的,即子问题之间不包含公共的子子问题;

分治算法大多采用递归实现,第二条特征就反应了递归思想的引用。

如果满足了第一条特征和第二条特征,不满足第三条特征,可以考虑用贪心法或动态规划法。

如果不满足第四条特征,也可以用分治法,但是要做很多不必要的工作,重复的解公共的子问题,所以一般用动态规划法比较好。

大整数乘法:十进制

解法一(分治法)

将两个整数分别一分为二:

X = A*10^(n/2) + B

Y = C*10^(n/2) + D

X*Y =  (A*10^(n/2) + B)*( C*10^(n/2) + D) =  A*C*10^n + A*D*10^(n/2) + B*C*10^(n/2) + B*D

但是这样做并没有减少直接相乘的的时间复杂度,所以要继续减少乘法的次数

X*Y = (A*10^(n/2) + B)*( C*10^(n/2) + D)

    = A*C*10^n + A*D*10^(n/2) + B*C*10^(n/2) + B*D

    = A*C*10^n + ((A+B)(C+D) – A*C – B*D)*10*(n/2) + B*D

或者:= A*C*10^n + ((A-B)(D-C) + A*C + B*D)*10*(n/2) + B*D

然后利用上面的公式写递归就好了

代码如下:


public class BigDataRide {
 
	public static int sign(long a) {
 
		return a < 0 ? -1 : 1;
 
	}
 
	public static double bigdataride(long x,long y,int n) {
 
		x = Math.abs(x);
 
		y = Math.abs(y);
 
		if (n == 1) {
			
			return x * y;
 
		}
 
		else {
			if (n%2==1) {
				n = n - 1; //对奇数的操作
			}
			long a = x / Math.round(Math.pow( 10 , (n / 2)));
 
			long b = x - a * Math.round(Math.pow( 10 , (n / 2)));
 
			long c = y / Math.round(Math.pow( 10 , (n / 2)));
 
			long d = y - c * Math.round(Math.pow( 10 , (n / 2)));
 
			double ac = bigdataride(a,c,n/2);//递归计算a*c
 
			double bd = bigdataride(b,d,n/2);//计算b*d
 
			long aJb = a + b;
 
			long cJd = c + d;
 
			double abcd = bigdataride(aJb,cJd,n/2);
 
			return (ac*Math.pow(10,n) + (abcd - ac - bd)*Math.pow(10,n/2) +bd);
 
		}
 
	}
 
	public static void main(String[] args) {
 
		// 大整数相乘
 
		long x = 12234L;
 
		long y = 45243L;
 
		String sx = String.valueOf(x);
 
		int n = sx.length();
 
		long sig = sign(x)*sign(y);
 
		double s = bigdataride(x,y,n);
 
		System.out.println("大数相乘的计算结果为:"+s*sig);
 
 
	}
 
}

解法二

模拟乘法

将两个数分别存入两个数组,然后根据乘法规则两层for循环分别让数字相乘,并存入一个新的数组,大致为这样:result[a+b] += num1[a]*num2[b];,现在这个result里存储的就是一个非个位数的临时结果,只需要做后将这个临时结果分别进位便可得到最终结果

代码如下:

import java.util.Scanner;

//创建类largenumberOperationMultiply
public class largenumberOperationMultiply {

    //定义方法multiply的功能
    public String multiply(String str1,String str2){
        int[] num1 = new int[str1.length()];
        int[] num2 = new int[str2.length()];
        int[] result = new int[str1.length() + str2.length()];

        //将两个字符串转成整型数组,顺序转换,数组下标越小,数字对应的位数越高
        for (int i = 0;i < str1.length(); i++){
            num1[i] = Integer.parseInt(str1.substring(i,i+1));
        }
        for (int i = 0;i < str2.length(); i++){
            num2[i] = Integer.parseInt(str2.substring(i,i+1));
        }

        //两大数相乘
        for (int a = 0;a < str1.length(); a++){
            for (int b = 0;b < str2.length(); b++){
                result[a+b] += num1[a]*num2[b];
            }
        }

        ////判断是否需要进位,满10进1,因为存储顺序与位数高低相反,所以采用逆序进位
        int temp;
        for (int k = result.length-1; k > 0; k--){
                temp=result[k]/10;  //数组下标大的向数组下标小的进位
                result[k-1] += temp;
                result[k] = result[k]%10;
            }

        //将结果数组逆序转化为字符串
        String resultstr = "";
        for (int i = 0; i < result.length-1; i++){
            resultstr += "" + result[i];
        }

        return resultstr;
    }

    public static void main(String[] args){
        Scanner sc = new Scanner(System.in);
        System.out.println("请输入第一个数:");
        String str1 = sc.next();
        System.out.println("请输入第二个数:");
        String str2 = sc.next();
        largenumberOperationMultiply bn = new largenumberOperationMultiply();
        //创建类largenumberOperationMultiply的对象bn
        String output = bn.multiply(str1,str2);
        //bn对象调用multiply方法对str1和str2进行操作
        System.out.println(str1+"与"+str2+"的积为:"+output);
    }
}

来个c版本的数组实现大数乘法

 
#include<stdio.h>
#include<string.h>
#include<math.h>
#define N 1005
char a[N],b[N];
int s1[N],s2[N],s3[N*N];
int main()
{
    int len1,len2,max,i,j;
    while(scanf("%s%s",a,b)!=EOF)
    {
        memset(s1,0,sizeof(s1));
        memset(s2,0,sizeof(s2));
        memset(s3,0,sizeof(s3));
        len1=strlen(a);
        len2=strlen(b);
        max=0;
        max=len1+len2;
        for(i=0,j=len1-1;i<len1;i++,j--)
            s1[i]=a[j]-'0';
        for(i=0,j=len2-1;i<len2;i++,j--)
            s2[i]=b[j]-'0';
        for(i=0;i<len1;i++)
            for(j=0;j<len2;j++)
                s3[i+j]+=s1[i]*s2[j];
        for(i=0;i<max;i++)
        {
            if(s3[i]>=10)
            {
                s3[i+1]+=s3[i]/10;
                s3[i]%=10;
            }
        }
        while(s3[max-1]==0)
        {
            if(s3[max-1]==0)
                max--;
        }
        for(i=max-1;i>=0;i--)
            printf("%d",s3[i]);
            printf("\n");
    }
    return 0;

}
posted @ 2020-09-30 16:23  跌打的小脆骨  阅读(763)  评论(1编辑  收藏  举报