分治法实验之大整数乘法(算法设计分析)


01. 问题描述

编写一个能计算两个n(16<=n<=256)位整数的乘法。


02. 输入格式

输入两行数据,每行表示一个大整数


03. 输出格式

输出运算后大整数的数据值


04. 输入样例

1111111111111111
1111111111111111


05. 输出样例

结果:1234567901234567654320987654321


06. 问题分析

  1. 大整数的位数n≥16,可以是十进制或者二进制数。
  2. 两个大整数相乘如果按照传统的方法,需要耗费n2次数量相乘,利用分治算法,可降低其时间复杂度。
  3. 分治实现思想:将第一个整数分成AB两部分,第二个整数分成CD两部分,当n=1时,直接利用普通乘法相乘,当n>1时,根据公式分治解乘法AC、(B-A)(C-D)、BD,然后根据公式将AC、(B-A)(C-D)、BD相加并移动n/2位,将AC结果移动n位,最后合并求解结果。
  4. 这样,原整数乘法化为3次n/2规模整数乘法运算和6次加减运算(Θ(n))

公式一:AC*10^2^ + ( B-A)(C-D) + AC + BD )*10^n/2^ + BD
公式二:AC*10^n^ + ( A+B)(C+D) - AC - BD )*10^n/2^ + BD

  1. 以下实现采用第一个公式实现

07. 算法设计

算法设计:
算法:MULTIFY
input:存储数组x,y;整数的位数n
output:相乘结果数组result
过程:Multify(x,y,result,n)
1.	if n=1 直接相乘计算结果
2.	else
3.	    x_div2[1…n/2] ← x[1…n/2]
4.	    x_div1[1…n/2] ← x[n/2+1…n]
5.	    y_div2[1…n/2] ← y[1…n/2]
6.	    y_div1[1…n/2] ← y[n/2+1…n]
7.	    Multify(x_div1,y_div2,ac,n/2)
8.	    Multify(x_div2,y_div2,bd,n/2)
9.	    subx[1…n/2] ← x_div2[1…n/2] – x_div1[1…n/2]
10.	    suby[1…n/2] ← y_div1[1…n/2] – y_div2[1…n/2]
11.	    Multify(subx,suby,middleresult_1,n/2)
12.	    addxy_1 ← ac + bd;
13.	    middleresult_2 ← addxy_1 + middleresult_1
14.	    move middleresult_2  n/215.	    move ac  n位
16.	    addxy_2 ← ac + bd;
17.	    result ← addxy_2 + middleresult_2
18.	endif


08. 代码实现


位数对齐函数:

/*位数对齐函数
 *将输入的字符串转换为数字数组(数字数组倒序存储,即高位放在后面,并在数组[0]位置放置数字位的位数)
 *位数对齐:为了方便计算,直接对齐到一个大于两数最大位数的一个2的幂次
 */
int align(char* ch_x,char* ch_y,int& ch_x_length,int& ch_y_length){

    int k = 0;
    int n = pow(2,k);
    while(1){
        if(n>=max(ch_x_length,ch_y_length))
            break;
        ++k;
        n = pow(2,k);
    }

    int i = 0,j = 0;
    if(ch_x_length < n){
        char ch_x_tmp[n];
        for(;i<n;++i){
            if(i < n - ch_x_length)
                ch_x_tmp[i] = '0';
            else
                ch_x_tmp[i] = ch_x[j++];
        }
        strncpy(ch_x,ch_x_tmp,n);
        ch_x_length = n;
    }
    i = 0;
    j = 0;
    if(ch_y_length < n){
        char ch_y_tmp[n];
        for(;i<n;++i){
            if(i < n - ch_y_length)
                ch_y_tmp[i] = '0';
            else
                ch_y_tmp[i] = ch_y[j++];
        }
        strncpy(ch_y,ch_y_tmp,n);
        ch_y_length = n;
    }
    return n;
}

大整数加法函数:

/*大整数加法函数
 *考虑进位
 *并将两数相加和直接补位到指定new_bits位数,存于addxy数组中
 */
void Add(int* x,int* y,int* addxy,int new_bits){//这里应当保证位数的对齐
    for(int i = 1;x[0]+i <= new_bits;++i)
        x[x[0]+i] = 0;
    for(int i = 1;y[0]+i <= new_bits;++i)
        y[y[0]+i] = 0;
    x[0] = new_bits;
    y[0] = new_bits;

    int carry = 0;//进位位

    for(int i = 1;i<=new_bits;++i){
        addxy[i] = (x[i]+y[i]+carry)%10;
        if(x[i]+y[i]+carry>9)
            carry = 1;
        else
            carry = 0;
    }

    if(carry == 1){
        addxy[0] = new_bits+1;//结果长度
        addxy[addxy[0]] = 1;//进位为1
    }
    else
        addxy[0] = new_bits;//结果长度
}

大整数减法函数:

/*大整数减法函数
 *减法位数不会改变
 *考虑大减小和小减大两种情况
 *用数组[0]位的符号表示结果的正负,绝对值为其位数
 */
void Sub(int* x,int* y,int* subxy,int bits){  //x-y  减位 位数不变
    int flag = 0;
    for(int i = bits;i>=1;--i){
        if((x[i]!=0||y[i]!=0)&&(x[i]!=y[i])){
            if(x[i]<y[i])
                flag = 1;
            break;
        }
    }

    if(flag == 0){ //大减小
        int borrow = 0;//借位
        for(int i = 1;i <= bits;++i){
            if(x[i]-y[i]<0){
               x[i+1]--;
                borrow = 1;
            }
            else
                borrow = 0;
            subxy[i] = (10*(borrow) + x[i] - y[i]);
        }
        subxy[0] = bits;

    }
    else{   //小减大
        int borrow = 0;//借位
        for(int i = 1;i <= bits;++i){
            if(y[i]-x[i]<0){
               y[i+1]--;
                borrow = 1;
            }
            else
                borrow = 0;
            subxy[i] = (10*(borrow) + y[i] - x[i]);
        }
        subxy[0] = -bits;
    }

}

位数移动函数:

/*位数移动函数
 *即扩大10的n次幂
 *这里考虑的是倒序存储的情况
 */
void Move(int* arr,int n){   //正序左移n位  //这里先扩大位数再移位
    for(int i=arr[0];i>=1;--i)
        arr[i+n]= arr[i];
    for(int i = 1;i<=n;++i)
        arr[i] = 0;
    arr[0] += n;
}

分治函数:

/*分治函数
 *将一个大整数分成左右两部分
 */
void Divide(int* arr_1,int* arr_2,int s,int t){ //由arr_1分到arr_2
    int i;
    for(i = 1;i <= t - s;++i)
        arr_2[i] = arr_1[s+i-1];
    arr_2[0] = t -s;
}

大整数乘法函数:

/*大整数乘法实现函数
 */
void Multify(int* x,int* y,int* result,const int& n){ //大整数乘法
	//申请空间存放数据四部分
    int* x_div1 = (int *)malloc((n*8+1)*sizeof(int));
    int* x_div2 = (int *)malloc((n*8+1)*sizeof(int));
    int* y_div1 = (int *)malloc((n*8+1)*sizeof(int));
    int* y_div2 = (int *)malloc((n*8+1)*sizeof(int));
    //递归出口
    if(n==1){
        result[1]=x[1]*y[1];
        if(result[1]<=9)
            result[0] = 1;
        else{//存在进位
            result[2]=result[1]/10;
            result[1]%=10;
            result[0] = 2;
        }
        if(x[0]*y[0]<0){ //判断正负
            result[0] = - abs(result[0]);
        }
    }
    else{
    	//分成四部分
        Divide(x,x_div2,1,(n>>1)+1);
        Divide(x,x_div1,n/2+1,n+1);
        Divide(y,y_div2,1,(n/2)+1);
        Divide(y,y_div1,n/2+1,n+1);
        
        int* ac = (int *)malloc((n*32+1)*sizeof(int));//分治结果
        int* bd = (int *)malloc((n*32+1)*sizeof(int));//分治结果
        int* addxy_1 = (int *)malloc((n*32+1)*sizeof(int));//存放加法和
        int* addxy_2 = (int *)malloc((n*32+1)*sizeof(int));//存放加法和
        int* subx = (int *)malloc((n*32+1)*sizeof(int));//存放减法差
        int* suby = (int *)malloc((n*32+1)*sizeof(int));//存放减法差
        int* middle_result_1 = (int *)malloc((n*32+1)*sizeof(int));//中间结果
        int* middle_result_2 = (int *)malloc((n*32+1)*sizeof(int));//中间结果

        Multify(x_div1,y_div1,ac,n/2);//分治
        Multify(x_div2,y_div2,bd,n/2);//分治
        Sub(x_div2,x_div1,subx,n/2);    //B-A
        Sub(y_div1,y_div2,suby,n/2);    //C-D
        Multify(subx,suby,middle_result_1,n/2);//分治
        Add(ac,bd,addxy_1,n*2);
        if(middle_result_1[0] < 0){ //根据正负号 判断接下来的某种操作
            for(int i = 1;abs(middle_result_1[0])+i <= addxy_1[0];++i)
                middle_result_1[middle_result_1[0]+i] = 0;
            Sub(addxy_1,middle_result_1,middle_result_2,n*2);
        }
        else
            Add(addxy_1,middle_result_1,middle_result_2,n*4);

		//下面将所用到的进行退位,排除没用的0空位 因为当前的值的每个位数可能差距太大

        for(int i = abs(middle_result_2[0]);i>=1;--i){
            if(middle_result_2[i]==0)
                continue;
            else{
                middle_result_2[0] = i;
                break;
            }
        }
        for(int i = abs(ac[0]);i>=1;--i){
            if(ac[i]==0)
                continue;
            else{
                ac[0] = i;
                break;
            }
        }
        for(int i = abs(bd[0]);i>=1;--i){
            if(bd[i]==0)
                continue;
            else{
                bd[0] = i;
                break;
            }
        }

        //合并
        Move(middle_result_2,n/2);
        Move(ac,n);
        Add(ac,bd,addxy_2,n*2);
        Add(addxy_2,middle_result_2,result,n*2);

        if(x[0]*y[0]<0) //判断符号
            result[0] = -abs(result[0]);
        else
            result[0] = abs(result[0]);
    }
}

主函数:

/*main函数
 */

#include <iostream>
using namespace std;
#include <bits/stdc++.h>
#include <math.h>
#include <cstdlib>
#include <stdio.h>
#include <cstring>

int main(){
    const int maxbits = 1024;
    char ch_x[maxbits];
    char ch_y[maxbits];
    scanf("%s",&ch_x);
    scanf("%s",&ch_y);
    int ch_x_length = strlen(ch_x);
    int ch_y_length = strlen(ch_y);
    int bits = align(ch_x,ch_y,ch_x_length,ch_y_length);//补位
    int* x = (int *)malloc((maxbits*2+1)*sizeof(int));
    int* y = (int *)malloc((maxbits*2+1)*sizeof(int));
    int* addxy = (int *)malloc((maxbits*4+1)*sizeof(int));
    int* subxy = (int *)malloc((maxbits*4+1)*sizeof(int));
    int* result = (int *)malloc((maxbits*8+1)*sizeof(int));
    x[0] = bits;//默认输入的都是正整数
    y[0] = bits;//默认输入的都是正整数
    for(int i = 0;i<bits;++i){ //逆序存储 方便进位
        x[bits-i] = ch_x[i] - '0';
        y[bits-i] = ch_y[i] - '0';
    }
    Multify(x,y,result,bits); //调用函数
    //打印实现
    cout<<"结果:";
    int flag = 0;
    for(int i = abs(result[0]);i>=1;--i){
        if(result[i]!=0){
            flag = i;
            break;
        }
    }
    for(int i = flag;i>=1;--i)
        cout<<result[i];
    return 0;
}


09. 测试结果

1234567890123
1234567890123
结果:1524157875322755800955129
Process returned 0 (0x0)   execution time : 52.699 s
Press any key to continue.

10. 复杂度分析

在这里插入图片描述

在这里插入图片描述


——————END-2022-04-26——————

  • 5
    点赞
  • 60
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
分治法是一种算法设计策略,通过将问题分解成更小的子问题,再通过将解合并起来来解决原始问题。在大整数乘法问题中,可以使用分治法来提高算法效率。 下面是一个使用C语言编写的分治法大整数乘法的代码示例: #include <stdio.h> #include <math.h> long long int power(int a, int b) { if (b == 0) { return 1; } else { return a * power(a, b - 1); } } long long int multiply(long long int x, long long int y) { int n; if (x < 10 || y < 10) { return x * y; } n = fmax(log10(x) + 1, log10(y) + 1); int m = ceil(n / 2.0); long long int a = x / power(10, m); long long int b = x % power(10, m); long long int c = y / power(10, m); long long int d = y % power(10, m); long long int ac = multiply(a, c); long long int bd = multiply(b, d); long long int ad_bc = multiply(a + b, c + d) - ac - bd; return ac * power(10, 2 * m) + ad_bc * power(10, m) + bd; } int main() { long long int x, y; printf("请输入两个整数:"); scanf("%lld %lld", &x, &y); long long int result = multiply(x, y); printf("两数乘积为:%lld\n", result); return 0; } 在这个代码示例中,使用了递归来实现分治法的思想。首先判断输入的两个数是否小于10,如果是,则直接返回乘积。如果不是,则计算出这两个数的位数n,然后取n的一半向上取整得到m。分别将两个数的高位和低位等分为a、b、c、d四部分。 接着,使用递归调用multiply函数来计算ac、bd和ad_bc三个结果,最后将这三个结果按照指定的规则相加得到最终的乘积。 通过使用分治法,可以显著提高大整数乘法的效率。因为将问题分解成更小的子问题,每个子问题处理的数据规模更小,计算量相对减少,从而提高了算法的效率。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

苡荏

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值