首页 > 其他分享 >分治法处理大整数相乘问题

分治法处理大整数相乘问题

时间:2023-07-17 17:57:41浏览次数:30  
标签:10 int 分治 整数 相乘 B1

分治法解决大整数相乘问题

1. 题目描述

大数乘法法运算跟一般的减法运算是不同的,在面对基本数据类型容量有限而导致无法存储特大数字的情况下,本文采用分治策略的方法来解决大数减运算问题。

输入:两个代表整数的字符串a和b,规定a>=b,a,b>0。

输出:返回表示结果整数的字符串。

2.解决思路——分治策略

2.1题目情景代入

假如现在要求两个大整数相乘的乘积,如1234 * 1234(这里为了了分析简便,所以不举形如1234567891234567这样的大整数,不必要在此纠结),那么按照我们小学学的乘法,就是用乘数的每一项去和1234相乘,这样很明显,算法的时间复杂度是O(n^2),效率很低下,那么有没有一种更好的方式?我们可以使用分治法来解决。

2.1.1什么是分治法

分治法的意思就是,分而治之,也就是把一个问题,拆分成几个小问题,最后再汇总解决的方法

image-20230717001014881

2.1.2通过大整数相乘理解分治的策略

​ 我们首先将两个数字A和B各自分成前后两段,当然这种时候可能存在两边数字长度不一样,而且比较长的一边不是偶数导致无法整除的方法,所以我们采用前方填充0的办法把两个数字填充成长度大于最长部分的前方填充0的偶长字符串。

​ 然后,可以把$A$分成$A1,A2$,同时将$B$分成$B1,B2$,然后原本的计算公式就变成了$A1 * B1 * 10^N + (A1 * B2 + A2 * B1) * 10^{N/2} + A2 * B2$,这样一来,递归每一步的时间复杂度就变成了$O(n)$,主要是加法,分治则包括四个子问题,每个子问题的规模是原本的1 / 2 , 也就是$T ( n ) = 4 T ( n / 2 ) + O ( n ) $, 一层层递归就是$n+ 4 ∗ n / 2 + . . . + 4 l o g 2 n ∗ 1$, 然后后面我们用一个换底公式把最后一项改一下,结果就是$n{log_24}$也就是$n^2 $, 我们这里可以直接用最大项,当然其实就不那么看,我们也可以大致得出,这个复杂度如果算上一些小的项目可能已经比原本的竖式算法还要大了,为此,我们必须要想办法去把复杂度降下来。降低时间复杂度的方法就是改一下我们的公式,$(A1∗B2+A2∗B1)=(A1−A2)∗(B2−B1)+A1∗B1+A2∗B2$,观察这个式子,其中的$A1 * B1$和$A2 * B2$都被复用了,也就是说相较于前面式子这个只需要计算三次了,很明显,时间复杂度降到了$O(n{log3.5/2})$,这时候我们就能基本证明复杂度降低了。就算是我们单步骤中增加了减法,我们的系数变大,但在n变得足够大的时候也是会比$O(n2)$ )小的。

​ 但是从算法中可以看到,计算过程中可能出现负数,在计算的过程中,需要考虑到负数,所以对此做一些分类讨论,首先就是要对同号相乘和异号相乘进行分类,然后在计算减法的时候也要进行分类讨论,比较两者的绝对值,被减数的绝对值比减数大的情况和比减数小的都要进行分类,计算过程中,我们同样都是采取从后往前计算的方式,采用借位的方式。

3.算法实现

3.1如何处理相乘的正负数问题

​ 本文中是使用的宏定义一个SIGN函数,对给出数据的值进行一个判断给出相应的正负标识,真正处理结果的时候,只需要对此拿出来进行正负判定就行了。

3.2怎么按照分治策略的思想来写递归函数呢?

​ 首先,在前面的分析当中,我们只是考虑的当前这个大整数,但是,大多时候真正计算的时候,他经过一次的分治后的数仍然还是大整数,也就是我们从一个大的问题(求最后结果)走到了一个小的问题(大问题衍生出来的分支也是和大问题一个性质的问题),这样会一直迭代下去,直到衍生出的小问题不会出现大问题当中的大整数,此时就会按照着先前走的分支,往回走,这就是递归的实现、

​ 现在我们理清了思路,对于递归函数,我们首先要写出终止递归条件,然后按照分治,使得问题衍生,依次进入到相应下的函数。最后返回正确的答案。

3.3代码实现

#include<cstdio>
#include<cmath>

using namespace std;
typedef long long ll;
#define SIGN(A) ((A > 0) ? 1 : -1) //定义的符号函数

int divideConquer(int X, int Y, int n){
    int sign = SIGN(X) * SIGN(Y);
    //不管三七二十一,先都变成正数,后续再处理正负号
    int x = abs(X);
    int y = abs(Y);
    if(x == 0 || y == 0)//递归结束标志
        return 0;
    else if(n == 1)
        return sign * x * y;//此时一定不再是大整数问题
    else
    {
        //处理大整数分治时的数据,前面的公式推导
        int A = (int) x / pow(10, (int)(n / 2));
        int B = x - A * pow(10, n / 2);
        int C = (int) y / pow(10, (int)(n / 2));
        int D = y - C * pow(10, n / 2);
        int AC = divideConquer(A, C, n / 2);
        int BD = divideConquer(B, D, n / 2);
        int ABDC = divideConquer((A - B), (D - C), n / 2) + AC + BD;
        return sign * (AC * pow(10 , n) + ABDC * pow(10, (int)(n / 2)) + BD); 
    }
}

int main(){
    
    ll x, y, n;//给出的大整数有可能会爆int(就是超出int类型)
    scanf("%d%d%d", &x, &y, &n);
    
    printf("x 和 y的乘积为:%d", divideConquer(x, y, n));
}

标签:10,int,分治,整数,相乘,B1
From: https://www.cnblogs.com/zyq811/p/17560769.html

相关文章

  • 线段树分治结构
    目录线段树分治结构基本知识:例题1: 模板(基础题)例题2: dp(背包)(会了就掌握题)线段树分治结构基本知识:应用点: 当有些东西一会出现,一会又不出现时,可以使用线段树分治方式: 维护某一个东西出现的时间段,并在线段树上打上标记,并dfs遇到标记后,就相当于加入了这个物品。当dfs到叶子节点后,就......
  • P8708 [蓝桥杯 2020 省 A1] 整数小拼接 题解
    前言传送门blog思路这种选出两个数拼接在一起的题,一看就可以使用two-point,我们使用$l$和$r$分别从最大的和最小的开始搜索,进行两次。以$l$为头,$r$为尾。以$r$为头,$l$为尾。如何比较大小呢?我们可以先去做宇宙总统这道题。首先排序的$cmp$:boolcmp(strin......
  • java验证小数整数位和小数位的正则
    Java验证小数整数位和小数位的正则正则表达式是一种强大的工具,用于匹配和操作字符串。在Java中,我们可以使用正则表达式来验证小数的整数位和小数位。验证小数整数位和小数位的规则在验证小数的整数位和小数位之前,我们需要了解一下这两个部分的规则。整数位:小数点之前的数字部......
  • 代码随想录算法训练营第三十二天| 343. 整数拆分 96.不同的二叉搜索树
     343.整数拆分要求:将一个正数拆分成N个正整数,使得这N个正整数的乘机是最大的思路:DP数组:dp[n]N的时候,它的乘机最大值注意:不是i*dp[n-i]就是最大值,因为如果用dp就证明要开始拆分了,如果我不拆分,就是用的这两个数的话,那么就是单纯的i*(n-i)代码:1//要求:将N拆分成K......
  • 树分治
    点分治回想一下在序列上的二分治:每次选择序列中点,递归处理两个子序列,处理跨过两个序列的信息。前两步保证了复杂度,第三步往往是解决问题的关键,那么这个思路能不能搬到树上呢?答案是肯定的,树分治思路和序列分治类似,我们每次递归处理子树,统计子树间的答案..,初步建立起模型后还有一......
  • 2023-07-15:给你一个 非递减 的正整数数组 nums 和整数 K, 判断该数组是否可以被分成一
    2023-07-15:给你一个非递减的正整数数组nums和整数K,判断该数组是否可以被分成一个或几个长度至少为K的不相交的递增子序列。输入:nums=[1,2,2,3,3,4,4],K=3。输出:true。答案2023-07-15:大体步骤如下:1.初始化计数变量cnt和最大计数变量maxCnt,初始值都为1。2.从索引......
  • 2023-07-15:给你一个 非递减 的正整数数组 nums 和整数 K, 判断该数组是否可以被分成一
    2023-07-15:给你一个非递减的正整数数组nums和整数K,判断该数组是否可以被分成一个或几个长度至少为K的不相交的递增子序列。输入:nums=[1,2,2,3,3,4,4],K=3。输出:true。答案2023-07-15:大体步骤如下:1.初始化计数变量cnt和最大计数变量maxCnt,初始值都为1。2......
  • [oeasy]python0072_整数类型_int_integer_整型变量
    帮助手册回忆上次内容 上次了解的是字符串字符串就是字符的串字符串长度可以用len函数字符可以用下标索引[] 可以用str将整型数字转化为字符串 字符的长度本身有长有短ascii字符集包括各种转义字符都对应1个字节......
  • 正整数正则表达式
    正整数正则表达式正数的正则表达式(包括0,小数保留两位):^((0{1}.\d{1,2})|([1-9]\d.{1}\d{1,2})|([1-9]+\d)|0)$正数的正则表达式(不包括0,小数保留两位):^((0{1}.\d{1,2})|([1-9]\d.{1}\d{1,2})|([1-9]+\d))$正整数的正则表达式(包括0):^[+]{0,1}(\d+)$正整数的正则表达式(不包括0......
  • 剑指 Offer 16. 数值的整数次方
    剑指Offer16.数值的整数次方这是在面试时候,无准备折腾除了的递归写法。classSolution{publicdoublemyPow(doublex,intn){//if(x==0)return0;//longb=if(n==0)return1.0;if(n==1)returnx;//把n为负......