算法题02:递归:大整数乘法的变式、二分搜索

一、给定 2 2 2 个大整数 u u u v v v,它们分别有 m m m 位和 n n n 位数字,且 m ≤ n m\le n mn。 当 m m m n n n 小很多时,设计一个算法用 O ( n m log ⁡ ( 3 / 2 ) ) O(nm^{\log(3/2)}) O(nmlog(3/2))时间求出 u v uv uv 的值。说明分析思路,写出伪代码,并写出算法复杂度分析过程。

​ 考虑到 m m m n n n小很多,可以把m位乘n位的问题拆解为多个m位相乘的问题,然后利用已经得出的m位相乘的优化算法进行计算。

​ 将 v v v 分解为 n / m n/m n/m 段,使得每段为 m m m位,那么类似大整数乘法的计算方法,这里计算 u v uv uv就相当于计算 n / m n/m n/m m m m位的乘法运算,然后在进行 n / m − 1 n/m-1 n/m1次加法。利用已经知道的分治法求 m m m位乘积复杂度为 O ( m l o g 3 ) O(m^{log^{3}}) O(mlog3),所以求 u v uv uv的时间复杂度为 O ( ( n / m ) m l o g 3 ) + O ( n / m ) = O ( n m l o g ( 3 / 2 ) ) O((n/m)m^{log^{3}}) + O(n/m) = O(nm^{log(3/2)}) O((n/m)mlog3)+O(n/m)=O(nmlog(3/2)).

伪代码

/**
 *计算位数为m,n,且m<n的两十进制数乘积,使得时间复杂度为O(nm^log(3/2))
 */

//十进制的同位大整数乘法,且位数为n.
public static int bigMul(int x,int y,int n){
    if(n == 1){
        return x*y;	
    }
    int k = n/2;
    int b = x % pow(10,n/2);
    int a = x / pow(10,n/2);
    int d = y % pow(10,n/2);
    int c = y / pow(10,n/2);
    return bigMul(a,c,k)*pow(2,2*k)+((a-b)*(d-c)+bigMul(a,c,k)+bigMul(b,d,k))*pow(2,k)+bigMul(b,d,k);
}
public static int main(){
    Scenner(int u,int v,int m,int n);	//输入u,v,m,n
    int res=0;							//结果
    int divide_V[n/m]; 		//定义存放将v分为n/m个长为m的数的数组
    //将v划分并存入数组
    for i=0 to i=n/m-1:
    	divide_V[i] = v%pow(10,m);
    	v /= pow(10,m);
    //将数组内的数与m使用上面定义的函数分别相乘,再分别乘以对相应段的位数,如2343->23 43,23参与乘法后还要乘10的2次方,最后全部相加
    for i=0 to i=n/m-1:
    	res += bigMul(u,divide_V[i],m) * i*pow(10,m);//第i段的应该补的位数恰为i*10的m次方
    
    return res;
}

二、 设 n n n不同的整数(可能为正整数、负整数或者0)排序后存于数组 T [ 0 : n − 1 ] T[0:n-1] T[0:n1]中。 设计一个算法,判断数组中是否存在不动点(若元素 T [ i ] T[i] T[i] 等于 i i i, 即 T [ i ] = i T[i]=i T[i]=i,则此元素为不动点)。说明分析思路,写出伪代码,并写出算法复杂度分析过程。

解:

​ 因为每个数都是整数,并且数组是升序的,假设其中某个位置的A[i] = i,那么可以肯定的值,之前的A[x] > x,之后的A[x] < x,那么这个问题除了判断相等的条件不同,其他和课上讲的二分搜索几乎一样,所以可以和二分搜索一样使用分治法和递归进行计算。

​ 因为每次都是执行二分操作,每次递归中间的操作复杂度都为 O ( 1 ) O(1) O(1),所以,总的复杂度的递归表达式为

T(n) = T(n/2) + O(1)

​ 由主方法得时间复杂度为: O ( l o g n ) O(logn) O(logn)

伪代码:

public static int hasPoint(int start, int end,int T[]){
    int mid = (end - start) / 2 + start;
    if(start > end):
        return false;
    
    if(T[mid] == mid):		
    	return true;
    else if(T[mid]<mid):
    	start = mid+1;			//此时值小于下标,应该在右边
    	return hasPoint(start,end,T);
    else if(T(mid)>mid)						//此时值大于下标,那么可能的值就在左边
    	end = mid-1;
        return hasPoint(start,end T);
    return false;
}
  • 11
    点赞
  • 38
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值