zoukankan      html  css  js  c++  java
  • 学习分治法+解决大数乘法问题

    大整数乘法

    分治法

    将一个规模为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("
    ");
        }
        return 0;
    
    }
    
  • 相关阅读:
    关于EasyUI datagrid 无法在dialog中显示的问题分析及解决方案!
    WPF 矩形框8个控制点伸缩及拖拽
    Socket异步通信及心跳包同时响应逻辑分析(最后附Demo)。
    C#断点续传下载。
    C# 全屏坐标及区域坐标获取。自定义光标及系统光标描边捕捉显示。
    解决项目无法添加VBIDE问题
    python爬虫-入门-了解爬虫
    字符串输入数字
    面试题3--数组中的重复数字(new数组的新写法)
    等号操作符重载为什么不能用友元函数大揭秘,以及函数没有等到重载的时候赋值会出现什么现象(盲点)
  • 原文地址:https://www.cnblogs.com/ycoder/p/13755166.html
Copyright © 2011-2022 走看看