大整数算法[07] 绝对值减法

★ 引子

最近两个星期一直在折腾,主机从 Windows 换到了 Linux,工作环境从实体机转移到虚拟机中。当然目的只有一个,那就是充分发挥 Linux 和虚拟机的优势来提高自己的工作效率。俗话说得好:磨刀不误砍柴工,花费一些时间来折腾升级还是有必要的,有空的话可以聊聊相关经验,如果你想急于知道的话,推荐编程随想的博客(博客在墙外,请自行搜索梯子)。

★ 计算原理

好了,废话不多说。上一篇文章讲了绝对值加法的实现,这次来讲讲绝对值减法该如何做。绝对值减法的做法仍然是笔算算法,从低位开始减,不够的向高位借位,直到所有的数位都处理完毕。为了方便日后的有符号数加减的实现,这里规定,算法计算 z = x - y,并且 x 的绝对值大于或等于 y,否则算法返回负数错误。

★ 实现

因为原理比较简单,所以我就先把代码贴出来,然后在介绍他的工作方式。

int bn_sub_abs(bignum *z, const bignum *x, const bignum *y)
{
    int ret;
    bn_digit *px, *py, *pz;
    size_t i, min, max, olduse, t1, t2, c;

    max = x->used;
    min = y->used;

    if(bn_cmp_abs(x, y) < 0)
        return BN_NEGATIVE_VALUE_ERROR;

    olduse = z->used;
    z->used = max;
    BN_CHECK(bn_grow(z, z->used));

    c = 0;
    px = x->dp;
    py = y->dp;
    pz = z->dp;

    for(i = 0; i < min; i++)
    {
        t1 = *px++;
        t2 = *py++;
        *pz++ = t1 - t2 - c;
        if(t1 != t2) c = (t1 < t2);
    }

    for(; i < max; i++)
    {
        t1 = *px++;
        *pz++ = t1 - c;
        if(c != 0 && t1 != 0) c = 0;
    }

    for(i = max; i < olduse; i++)
        *pz++ = 0;

    z->sign = 1;
    bn_clamp(z);

clean:

    return ret;
}

绝对值减法中,对输入进行排序并不重要,因为前面已经规定 |x| >= |y|,所以直接把 x->used 给 max, y->used 给 min;t1 和 t2 是临时变量,c 是借位。

在进行计算之前,先检查 x 和 y 的绝对值大小,如果不满足上面约定的条件,返回负数错误。

如果 x 和 y 的绝对值大小检查没问题,那么计算就可以正常进行,首先把借位的值设为 0,然后设定指针别名来提高内存访问效率。

第一个循环:对位相减。分别把 x 和 y 的每一个数位赋值给临时变量 t1 和 t2,计算 t1 - t2 - c 的值,然后存放到 z 的对应数位当中,如果 c = 0,表示低位没有向高位借位。
相减完毕后,判断本次相减是否需要向高位借位,如果原来 x 中的某一数位的值小于 y 中对应数位的值,则比较的结果为 1,c = 1。注意所有的计算都是 mod 2^n。

第二个循环:退位和赋值。如果 max > min,表明 x 的数位要比 y 多,所以还需进行退位计算。如果 c = 0,则不会有退位了,直接把 x 的剩余数位赋值给 z 的对应数位即可。如果 c = 1,则还有来自低位的借位,在完成一次退位计算后,判断下一位是否需要退位,由于 c 的值只可能是 0 或 1,如果本次退位计算前,该数位的值大于 0,则以后的数位都不需要进行退位,故将 c 的值置为 0,否则保持退位值 1。完成退位计算后,将 x 剩余的数位给 z ,完成减法计算。

第三个循环:高位清零。如果减法计算完毕后,高位还有不为 0 的数位,必须清空,否则结果会出错。

所有循环结束后,把符号为设为 1,因为绝对值减法的最终结果仍然是个非负整数;最后压缩多余位完成计算。

★ 总结

             减法操作相对于加法来讲要简单些,主要是不需要考虑单双精度的问题,只要你直到笔算算法以及理解计算机下二进制的补码运算,就不难写出。下一篇文章将根据前面建立的比较算法,绝对值加减算法构造有符号数的加减计算算法。

回到本系列目录

版权声明
原创博文,转载必须包含本声明,保持本文完整,并以超链接形式注明作者Starrybird和本文原始地址:http://www.cnblogs.com/starrybird/p/4399652.html

时间: 2024-11-03 00:10:27

大整数算法[07] 绝对值减法的相关文章

大整数算法[00] 概述

★ 为啥要做这个 早在大一的时候,我便对密码学产生兴趣.那时在计算机导论后面看到RSA加密的计算原理,觉得十分有趣,于是就很想自己实现一个RSA加密,不过我很快就放弃了,因为实在搞不定那超长的整数计算.C里面最长的整数类型也就64位,对于动辄就1024位的RSA密钥,这连个零头都没有.为了完成这个目标,我便开始琢磨着弄一个用来计算大整数的库.原本我也打算使用别人已经写好的大数库,不过最终还是决定自己搞一个,因为凡是效率高速度快的大数库 (OpenSSL, Crypto++, GMP),要么使用的

大整数算法[02] 基本的操作(维护算法)

上一篇博文简单介绍了大整数的表示方法,这次开始介绍一些基本的算法.       ★ 初始化和清除 编写大整数函数的出发点是bignum结构的初始化和清除,在其他大部分算法当中,这两个算法都会用到. 对于给定的bignum结构,初始化有两种情况:一是仅仅把bignum结构的dp指向NULL,二是初始化的时候顺便分配一定的动态内存,并让dp指针指向这块内存.其实我本来打算只用第二种方式进行初始化,不过考虑到初始内存可能分配过多导致内存浪费,于是决定两种方式一起使用.第一种方式的优点是在后面编程中你不

大整数算法[01] 大整数的表示和相关定义

★ 相关的数据类型定义 在干正事之前,先定义好各种数据类型还是很有必要的,避免在以后的编码中引起混乱. uintX   X位无符号整形,如uint32表示32位无符号整形 intX    X位有符号整形,如int32表示32位有符号整形 基本数据类型定义: #ifdef _MSC_VER            typedef __int8              int8;            typedef __int16             int16;            typ

大整数算法[08] Comba乘法(原理)

★ 引子          原本打算一篇文章讲完,后来发现篇幅会很大,所以拆成两部分,先讲原理,再讲实现.实现的话相对复杂,要用到内联汇编,要考虑不同平台等等. 在大整数计算中,乘法是非常重要的,因为在公钥密码学中模幂运算要频繁使用乘法,所以乘法的性能会直接影响到模幂运算的效率.下面将会介绍两种乘法:基线乘法和 Comba 乘法,尽管他们的原理和计算看起来十分类似,而且算法的时间复杂度都是 O(n^2),但是他们的效率差别是很大的. ★ 基线乘法 (Baseline Multiplication

大整数算法[08] 有符号加法和减法

★ 引子 前面几篇文章介绍了比较操作,绝对值加法和绝对值减法,现在就可以利用这几个算法构建有符号数的加减算法. ★ 有符号数加法            有符号数的加法分成两种情况:同号和异号. 1.  如果两个数同号,则执行绝对值加法,如果两个数为非负数,则结果为非负数:如果两个数都是负数,则结果也为负数. 2.  如果两个数异号,则要执行绝对值减法,用绝对值较大的数去减绝对值较小的数.最终结果 z 的符号由 x 和 y 的绝对值大小决定:如果 x 的绝对值大于或等于 y,则 z 的符号与 x

大整数算法[11] Karatsuba乘法

★ 引子         前面两篇介绍了 Comba 乘法,最后提到当输入的规模很大时,所需的计算时间会急剧增长,因为 Comba 乘法的时间复杂度仍然是 O(n^2).想要打破乘法中 O(n^2) 的限制,需要从一个完全不同的角度来看待乘法.在下面的乘法算法中,需要使用 x 和 y 这两个大整数的多项式基表达式 f(x) 和 g(x) 来表示. 令 f(x) = a * x + b,g(x) = c * x + d,h(x) = f(x) * g(x).这里的 x 相当于一个基,比如十进制下,

[转]大整数算法[11] Karatsuba乘法

★ 引子         前面两篇介绍了 Comba 乘法,最后提到当输入的规模很大时,所需的计算时间会急剧增长,因为 Comba 乘法的时间复杂度仍然是 O(n^2).想要打破乘法中 O(n^2) 的限制,需要从一个完全不同的角度来看待乘法.在下面的乘法算法中,需要使用 x 和 y 这两个大整数的多项式基表达式 f(x) 和 g(x) 来表示. 令 f(x) = a * x + b,g(x) = c * x + d,h(x) = f(x) * g(x).这里的 x 相当于一个基,比如十进制下,

大整数算法[13] 单数位乘法

★ 引子 最近在折腾 wxWidgets,同时拖延症又犯了,所以中断了好久.这次来讲讲单数位乘法,前面讲到 Comba 和 Karatsuba 乘法,这两个算法适合用来处理比较大的整数,但是对于一个大整数和一个单精度数相乘,其效果反而会不好,因为计算量过多.实际上单数位乘法只是基线乘法的一个特例,不存在嵌套循环进位,因此可以通过优化减少计算量.另外与完整的乘法不同的是,单数位乘法不需要什么临时变量存储和内存分配(目标精度增加除外). ★ 算法思路         单数位乘法类似于计算 12345

大整数算法

本文主要整理了几个常用的大整数的算法:大整数加法大整数乘法大整数阶乘大整数幂其实大体的思路都差不多,都是用数组来存储大整数.以下的代码仅仅实现功能,并没有充分详细的参数判断,在实际运用中,肯定是需要考虑的. 大整数相加 1 #include <stdio.h> 2 #include <string.h> 3 #define N 1000 4 void get_num(int *array) 5 { 6 char str[N] = {'\0'}; 7 int loop = 0; 8