【密码学课程设计】大整数运算包的设计与实现

目录

    • 前置工作
    • 一、大数加法
    • 二、大数减法
    • 三、大数乘法
    • 四、大数除法(取模)

前置工作

因为需要多次比较字符串的长度所以我们写成函数

compare是string中常用的函数,功能是和str按字典序比较大小,若小于str返回小于0的值,若等于str返回0,若大于str返回大于0的值

int compare(string str1, string str2) {
    if (str1.length() > str2.length()) return 1;
    else if(str1.length() < str2.length()) return -1;
    else return str1.compare(str2);
} 

一、大数加法

问题分析:
分为四种情况进行设计
负 + 负 = -(正+正)
负 + 正 = 正 - 负
正 + 负 = 正 - 负
正 + 正

所以可以看出大数加法主要是研究两个大整数的加法运算,因为整数过长超出无符号长整形范围所以我采用字符串了将两个整数相加转化为两个字符串按位相加,短的字符串前补零凑位数相同,按位相加超过9则向前进一位,当前位值模10后落下来。

这个问题也可以使用开一个大的数组进行存储,将两个字符串反向后低位在前高位在后,按位相加输出逆序。

string Addition(string num1, string num2)
{
    int sign = 1; //符号位
    string sum;
    if (num1[0]=='-') {
        if (num2[0]=='-') { //都是负数
            sign = -1;
            sum = Addition(num1.erase(0,1),num2.erase(0,1));
        } else { //第一个是负数,第二个是正数,转化为减法
            sum = Subtraction(num2, num1.erase(0,1));
        }
    }
    else { //第一个是正数
        if (num2[0] == '-') { //第二个是负数
            sum = Subtraction(num1, num2.erase(0,1));
        } else { //都为正数
            __SIZE_TYPE__ L1, L2;
            int i;
            L1 = num1.length();
            L2 = num2.length();
            if(L1 < L2) { //第一个数短补0
                for (i = 1; i <= L2 - L1; i++)
                    num1 = "0" + num1;
            } else if(L1 > L2) { //第二个数短补0
                for (i = 1; i <= L1 - L2; i++)
                    num2 = "0" + num2;
            }
            int carry = 0; //进位
            int fall = 0; //落下来的数字
            for (i = num1.length() - 1; i >= 0; i--) {
                fall = ((num1[i] - '0') + (num2[i] - '0') + carry)%10;
                carry = ((num1[i] - '0') + (num2[i] - '0') + carry)/10;
                sum = to_string(fall) + sum;
            }
            if (carry) //最高位进位,相当于+1
                sum = to_string(carry) + sum;
        }
    }
    // 符号位
    if((sign == -1) && (sum[0] != '0'))
        sum = "-" + sum;
    return sum;
}

二、大数减法

问题分析:
分为四种情况设计
负 - 正 = -(正 + 正)
正 - 负 = 正 + 正
负 - 负 = 正 - 正
正 - 正

同理大数减法主要是研究两个正数的减法,这里我使用了将两个字符串反置相减,进位变成借位,当最短的字符串运算完毕后将长字符串剩余部分落下来。

string Subtraction(string num1, string num2) {
    int sign = 1; //符号位
    string margin;
    int i, j;
    
    if (num2[0] == '-') { //第二个数为负数,相当于加上绝对值
        if(num1[0] == '-') { //负负
            sign = -1;
            margin = Addition(num1.erase(0,1), num2.erase(0,1));
        }
        else margin = Addition(num1, num2.erase(0,1)); //正负
    } else { //*正
        if(num1[0] == '-') { //负正
            sign = -1;
            margin = Addition(num1.erase(0,1),num2);
        } else {
        int res = compare(num1, num2);
        if(res == 0) return "0";
        if (res < 0) {
            sign = -1;
            string temp = num1;
            num1 = num2;
            num2 = temp;
        }
        int down = 0;
        int tempc = 0;
        for(i = num2.length()-1,j = num1.length()-1;i >= 0; i--,j--) {
            down = (num1[j] - '0') - (num2[i] - '0');
            if(down < 0) {
                down += 10;
                num1[j-1] = char(int(num1[j-1]) - 1);
            }
            margin = char(down + '0') + margin;
        }
        if(down) margin = num1.substr(0, num1.length() - num2.length()) + margin;
        }
    }
    margin.erase(0,margin.find_first_not_of('0'));
    if (margin.empty()) margin = "0";
    if((sign == -1) && (margin[0]!='0'))
    margin = "-" + margin;
    return margin;
}

三、大数乘法

问题分析:
符号位比较好确定,负负得正就行了。
乘法模拟手工运算麻烦的是占位,所以坑点在运算前确定占了多少位之后补0,最后乘法运算就转化为重复的加法运算了

string Multiply (string num1, string num2) {
    int sign = 1;
    string product;
    if (num1[0] == '-') {
        sign *= -1;
        num1 = num1.erase(0, 1);
    }
    if (num2[0] == '-') {
        sign *= -1;
        num2 = num2.erase(0, 1);
    }
    int i, j;
    __SIZE_TYPE__ L1, L2;
    L1 = num1.length();
    L2 = num2.length();
    for(i = L2 - 1; i >= 0; i--) { //模拟手工乘法竖式
        string tempnum;
        int int1 = 0, int2 = 0, int3 = int(num2[i]) - '0';
        if (int3) {
            for (j = 1; j <= (int)(L2-1-i); j++)
                tempnum = "0" + tempnum;
            for (j = L1 -1; j >= 0; j--) {
                int1 = (int3 * (int(num1[j]) - '0') + int2) % 10;
                int2 = (int3 * (int(num1[j]) - '0') + int2) / 10;
                tempnum = char(int1 + '0') + tempnum;
            }
            if (int2) tempnum = char(int2 + '0') + tempnum;
        }
        product = Addition(product, tempnum);
    }
    product.erase(0,product.find_first_not_of('0'));
    if (product.empty()) product = "0";
    if ((sign==-1) && (product[0]!='0')) product = "-" + product;
    return product;
}

四、大数除法(取模)

问题分析:
除法模拟手工运算,先从被除数中取出与除数同样长度的字符串a,之后用乘法试出来商的高位b,a - b除数后接被除数落下的一位成为新的除法。
余 = 最后一次运算被除数 - b
除数

//除法,flag为1时,返回商;flag为0时返回余数
string Divide(string num1, string num2, int flag) {
    string quotient,residue; //商和余数
    int sign1 = 1, sign2 = 1;
    //除数为0
    if (num2 == "0") {
        quotient = "ERROR!";
        residue = "ERROR!";
        if(flag == 1) return quotient;
        else return residue;
    }
    //被除数为0
    if(num1 == "0") {
        quotient = "0";
        residue = "0";
    }
    if(num1[0]=='-') {
        num1 = num1.erase(0,1);
        sign1 *= -1;
        sign2 *= -1;
    }
    if(num2[0]=='-') {
        num2 = num2.erase(0,1);
        sign1 *= -1;
    }
    int res = compare(num1, num2);
    if(res < 0) {
        quotient = "0";
        residue = num1;
    } else if(res == 0) {
        quotient = "1";
        residue = "0";
    } else {
        __SIZE_TYPE__ L1, L2;
        L1 = num1.length();
        L2 = num2.length();
        string tempnum;
        tempnum.append(num1, 0, L2);
        for(int i = L2; i <= L1; i++) {
            tempnum.erase(0, tempnum.find_first_not_of('0'));
            if(tempnum.empty()) tempnum = "0";
            for(char ch = '9'; ch >= '0'; ch--) {
                string num;
                num = num + ch;
                if(compare(Multiply(num2,num),tempnum)<=0) {
                    quotient = quotient + ch;
                    tempnum = Subtraction(tempnum, Multiply(num2, num));
                    break;
                }
            }
            tempnum = tempnum + num1[i];
        }
        residue = tempnum;
    }
    quotient.erase(0,quotient.find_first_not_of('0'));
    if (quotient.empty()) quotient = "0";
    if ((sign1 == -1) && (quotient[0] != '0'))
        quotient = "-" + quotient;
    if ((sign2 == -1) && (residue[0]!= '0'))
        residue = "-" + residue;
    if (flag == 1)
        return quotient;
    else
        return residue;
}

string DIVIDE(string num1, string num2) {
    return Divide(num1, num2, 1);
}

string MOD(string num1, string num2) {
    return Divide(num1, num2, 0);
}

参考连接:
https://www.cnblogs.com/wuqianling/p/5387099.html
https://bbs.csdn.net/topics/390485178

你可能感兴趣的:(密码学课设)