大整数相乘问题

先把好的博客贴上 代码明天再说。。。

http://blog.youkuaiyun.com/chhuach2005/article/details/21168179

#include<iostream> 
#include<sstream> 
#include<string> 
using namespace std; 
//string类型转换成int类型
long string_to_num(string str) 
{ 
   int result = 0;
   for(int i = 0; i < str.length();i++)
   result = result * 10 + str[i] - '0';
   return result;
} 
 
 //整形数转换为string类型
string num_to_string(int intValue)
{
	//用输入流的方法 
    string result;
    stringstream stream;
    stream << intValue;
    stream >> result;
    return result;
}
 
//在字符串str前添加s个零
string stringBeforeZero(string str,int s)
{
    for(int i=0;i<s;i++)
        str.insert(0,"0");
     return str;
}
 //在字符串str后跟随s个零
string stringFollowZero(string str,int s)
{
    for(int i=0;i<s;i++)
        str.insert(str.size(),"0");
     
    return str;
}
 
//两个大整数字符串相加
string stringAddstring(string str1,string str2)
{
    //假定str1和str2是相等的长度,不相等时在前面自动补零,使两个字符串长度相等
    if (str1.size() > str2.size()) 
        str2 = stringBeforeZero(str2,str1.size() - str2.size());
    else if (str1.size() < str2.size()) 
        str1 = stringBeforeZero(str1,str2.size() - str1.size());
     string result;
    int flag = 0;//前一进位是否有标志,0代表无进位,1代表有进位
    for(int i=str1.size()-1;i>=0;i--)
    {
        int c = (str1[i] - '0') + (str2[i] - '0') + flag;//利用ASCII码对字符进行运算,这里加上flag代表的是:当前一位有进位时加1,无进位时加0
        flag = c/10;//c大于10时,flag置为1,否则为0
        c %= 10;//c大于10时取模,否则为其本身
        result.insert(0,num_to_string(c));//在result字符串最前端插入新生成的单个字符
    }
    if (0 != flag) //最后一为(最高位)判断,如果有进位则再添一位
        result.insert(0,num_to_string(flag));
     return result;
}
 
//两个大整数字符串相减,
string stringSubtractstring(string str1,string str2)
{
    //使两个字符串的长度相同 
    while ('0' == str1[0]&&str1.size()>1)
        str1=str1.substr(1,str1.size()-1);
     
    while ('0' == str2[0]&&str2.size()>1)
        str2=str2.substr(1,str2.size()-1);
     
 
    //使两个字符串长度相等
    if (str1.size() > str2.size()) 
        str2 = stringBeforeZero(str2,str1.size() - str2.size());
     
 
    string result;
    for(int i = str1.size()-1;i >= 0;i--)
    {
        int c = (str1[i] - '0') - (str2[i] - '0');//利用ASCII码进行各位减法运算
        if (c < 0) //当不够减时向前一位借位,前一位也不够位时再向前一位借位,依次如下
        {
            c +=10;
            int prePos = i-1; 
            char preChar = str1[prePos];
            while ('0' == preChar) 
            {
                str1[prePos]='9';
                prePos -= 1;
                preChar = str1[prePos];
            }
 
            str1[prePos]-=1;
        }
 
        result.insert(0,num_to_string(c));//在result字符串最前端插入新生成的单个字符
    }
    return result;
}
 
//分治法大整数乘法实现函数
string IntMult(string x,string y)//递归函数
{ 
    //对传进来的第一个数进行修剪,如果前面几位有0则先去掉,便于统一处理
    while ('0' == x[0]&&x.size()>1)
        x=x.substr(1,x.size()-1);
 
    //对传进来的第二个数进行修剪,如果前面几位有0则先去掉,便于统一处理
    while ('0' == y[0]&&y.size()>1)
        y=y.substr(1,y.size()-1);
 
    /*这里的f变量代表在两个数据字符串长度不想等或者不是2的指数倍的情况下,所要统一的长度,这样做是为了让数据长度为2的n次方的情况下便于利用分治法处理*/
 
    int temp = 4;
    /*当两字符串中有任意一个字符串长度大于2时都要通过与上面定义的f值进行比较,使其达到数据长度为2的n次方,实现方式是在前面补0,这样可以保证数据值大小不变*/
    if (x.size()>2 || y.size()>2) 
    {
        if (x.size() >= y.size()) //第一个数长度大于等于第二个数长度的情况
        {
            while (x.size()>temp) //判断f值
                temp*=2;
            if (x.size() != temp) 
            {
                x = stringBeforeZero(x,temp-x.size());
                y = stringBeforeZero(y,temp-y.size());
            }
        }else//第二个数长度大于第一个数长度的情况
        {
            while (y.size()>temp) //判断f值
                temp*=2;
             
            if (y.size() != temp) 
            {
                x = stringBeforeZero(x,temp-x.size());
                y = stringBeforeZero(y,temp-y.size());
            }
        }
    }
 
    if ( x.size() == 1) //数据长度为1时,在前面补一个0(这里之所以会出现长度为1的数据是因为前面对数据修剪过)
        x=stringBeforeZero(x,1);
         
    if (y.size() == 1) //数据长度为1时,在前面补一个0(这里之所以会出现长度为1的数据是因为前面对数据修剪过)
        y=stringBeforeZero(y,1);
     
    if (x.size() > y.size()) //最后一次对数据校正,确保两个数据长度统一
        y = stringBeforeZero(y,x.size()-y.size());
     
    if (x.size() < y.size()) //最后一次对数据校正,确保两个数据长度统一
        x = stringBeforeZero(x,y.size()-x.size());
          
    int s = x.size(); 
    string a1,a0,b1,b0; 
    if( s > 1) 
    { 
        a1 = x.substr(0,s/2); 
        a0 = x.substr(s/2,s-1); 
        b1 = y.substr(0,s/2);      
        b0 = y.substr(s/2,s-1); 
    } 
    string result;
    if( s == 2) //长度为2时代表着递归的结束条件
    { 
        int na = string_to_num(a1); 
        int nb = string_to_num(a0);  
        int nc = string_to_num(b1); 
        int nd = string_to_num(b0);
        result = num_to_string((na*10+nb) * (nc*10+nd)); 
    } 
    else{ //用分治法进行分割 
        string c2 = IntMult(a1,b1);// (a1 * b1)
        string c0 = IntMult(a0,b0);// (a0 * b0)
        string c1_1 = stringAddstring(a1,a0);// (a1 + a0)
        string c1_2 = stringAddstring(b1,b0);// (b1 + b0)
        string c1_3 = IntMult(c1_1,c1_2);// (a1 + a0)*(b1 + b0)
        string c1_4 = stringAddstring(c2,c0);// (c2 + c0)
        string c1=stringSubtractstring(c1_3,c1_4);// (a1 + a0)*(b1 + b0)-(c2 + c0)
        string s1=stringFollowZero(c1,s/2);// c1*(10^(n/2))
        string s2=stringFollowZero(c2,s);// c2*(10^n)
        result = stringAddstring(stringAddstring(s2,s1),c0);// c2*(10^n) + c1*(10^(n/2)) + c0
    } 
 
    return result; 
} 
 
int main() 
{ 
    string num1,num2; 
    string r;
   cout<<"输入两个大整数:";
    cin>>num1>>num2;
   r=IntMult(num1,num2); 
 //对求得的结果进行修剪,去掉最前面的几个0
    while (r[0] == '0'&&r.size()>1)
    {
        r=r.substr(1,r.size()-1);
    }
    cout<<"两数相乘结果为:"<<r;     
return 0;
}


### 分治法实现大整数相乘 分治法是一种经典的算法设计策略,通过将复杂问题分解成更小的子问题来简化计算过程。对于大整数相乘问题,Karatsuba 算法是一个典型的例子,它利用分治思想显著减少了传统逐位相乘所需的计算量。 以下是基于 Karatsuba 算法的大整数相乘 C 语言实现: #### 原理概述 假设两个 n 位大整数 \( A \) 和 \( B \),可以表示为: \[ A = a_1 \cdot 10^{n/2} + a_0 \] \[ B = b_1 \cdot 10^{n/2} + b_0 \] 其中 \( a_1, a_0, b_1, b_0 \) 是较小的部分。传统的逐位相乘需要 \( O(n^2) \) 的时间复杂度,而 Karatsuba 算法则仅需三次递归调用即可完成计算,从而降低到 \( O(n^{\log_2{3}}) \approx O(n^{1.58}) \)[^1]。 具体步骤如下: 1. 将输入分成两半:高位和低位。 2. 计算三个中间项: - \( z_0 = a_0 \cdot b_0 \) - \( z_1 = (a_0 + a_1) \cdot (b_0 + b_1) \) - \( z_2 = a_1 \cdot b_1 \) 3. 合并结果: \[ AB = z_2 \cdot 10^n + (z_1 - z_2 - z_0) \cdot 10^{n/2} + z_0 \] #### C 语言实现代码示例 以下是一个简单的 C 语言实现,用于演示如何使用分治法进行大整数相乘: ```c #include <stdio.h> #include <string.h> // 辅助函数:字符串加法 char* add(char *num1, char *num2) { int i = strlen(num1) - 1; int j = strlen(num2) - 1; int carry = 0; int sum = 0; static char result[1000]; int k = 0; while (i >= 0 || j >= 0 || carry > 0) { int digit1 = (i >= 0 ? num1[i--] - '0' : 0); int digit2 = (j >= 0 ? num2[j--] - '0' : 0); sum = digit1 + digit2 + carry; carry = sum / 10; result[k++] = (sum % 10) + '0'; } result[k] = '\0'; // 反转结果 for (int l = 0, r = k - 1; l < r; l++, r--) { char temp = result[l]; result[l] = result[r]; result[r] = temp; } return result; } // 辅助函数:字符串减法 char* subtract(char *num1, char *num2) { int i = strlen(num1) - 1; int j = strlen(num2) - 1; int borrow = 0; int diff = 0; static char result[1000]; int k = 0; while (i >= 0 || j >= 0) { int digit1 = (i >= 0 ? num1[i--] - '0' : 0); int digit2 = (j >= 0 ? num2[j--] - '0' : 0); diff = digit1 - digit2 - borrow; if (diff < 0) { diff += 10; borrow = 1; } else { borrow = 0; } result[k++] = (diff % 10) + '0'; } result[k] = '\0'; // 移除前导零 int m = 0; while (result[m] == '0') m++; if (m == k) return "0"; // 反转结果 for (int l = m, r = k - 1; l < r; l++, r--) { char temp = result[l]; result[l] = result[r]; result[r] = temp; } return &result[m]; } // 主函数:分治法大整数相乘 void multiply(char *A, char *B, char **res) { int lenA = strlen(A), lenB = strlen(B); // 如果其中一个数字长度小于等于1,则直接返回简单乘积 if (lenA == 1 && lenB == 1) { int product = (A[0] - '0') * (B[0] - '0'); sprintf(*res, "%d", product); return; } // 找到较大的长度作为分割依据 int maxLength = (lenA > lenB ? lenA : lenB); int halfLength = maxLength / 2; // 初始化变量存储高、低部分 char highA[halfLength], lowA[halfLength]; char highB[halfLength], lowB[halfLength]; memset(highA, '0', sizeof(highA)); memset(lowA, '0', sizeof(lowA)); memset(highB, '0', sizeof(highB)); memset(lowB, '0', sizeof(lowB)); strncpy(highA, A, lenA - halfLength); // 高位部分 strncpy(lowA, A + lenA - halfLength, halfLength); // 低位部分 strncpy(highB, B, lenB - halfLength); // 高位部分 strncpy(lowB, B + lenB - halfLength, halfLength); // 低位部分 // 递归计算三部分 char *z0, *z1, *z2; multiply(lowA, lowB, &z0); // Z0 = A_low * B_low multiply(add(highA, lowA), add(highB, lowB), &z1); // Z1 = (A_high+A_low)*(B_high+B_low) multiply(highA, highB, &z2); // Z2 = A_high * B_high // 计算最终结果 char *temp1, *temp2; temp1 = subtract(subtract(z1, z2), z0); // 中间差值 strcat(temp1, strrep('0', halfLength)); // 补充适当数量的零 strcat(z2, strrep('0', 2 * halfLength)); // 补充适当数量的零 *res = add(add(z2, temp1), z0); // 结果拼接 } ``` 此代码实现了基本的分治逻辑,并支持任意大小的整数相乘。 --- ### 关于乘法逆元与模运算优化 当涉及模数操作时,通常会采用 Montgomery 模幂技术或其他快速模运算方法。如果目标是减少因整数截断而导致的数据丢失,可以通过扩展数据类型(如 `uint64_t` 或更高精度库)或预处理方式规避这一问题[^2]。 --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值