超长整数的基础运算 算法实现自平方篇

在《乘、除篇》中提到过一类特殊的乘法--自平方。之所以特殊是因为两个相乘的数是相同的,在进行每一位遍历的时候其实知道的是两个数的信息。

其实乘法之所以需要O(n^2)的复杂度就是因为需要内嵌两层循环来得出两数的全部信息,所以如果仅需遍历一次即可获取信息的话那么在处理自平方的时候即可达到O(n^2/4)级别。

虽然在级别上没有质的飞跃,但是对n较大的时候,计算量的线性减少而提升的性能还是可观的,毕竟不积硅步无已成千里。

接下来我们先讨论实现思路:

1、将超长整数An (n表示整数长度)划分成两段L1,L2,则L1=L2=n/2(非整除时选择L1=L2+1或者L2=L1+1任取)(例如:L1:0...n/2
;L2:n/2...n)

2、分别 An 中每一位的各自的平方和,每一位得出的结果存入Li‘ = L(i+i),其中 i‘ = i+i

3、执行一次 L1 和 L2 中每一位乘法运算,将每次运算的结果扩大2倍即可,存入对应结果位置Lk = Li + Lj,其中 k = i+j

Code View:

/*
自平方运算
*/
int mulHBInt(HBigInt *product, HBigInt *biA){
    HBigInt biT;        //计算结果先保存在临时变量中
    register un_short *pWordA = biA->pBigInt;
    register un_short *pPrd = NULL;
    long result = 0, i = 0, j = 0, index=0; 

    //初始化临时大整数变量
    if((result = initHBInt(&biT,biA->length<<1)) != RETURN_OK_BINT)
        return result;  

    biT.length = biA->length << 1;
    biT.sign = 1;
    pPrd = biT.pBigInt;

    index = biA->length >> 1;
    for(i=0; i<biT.length; ++i) {
        pPrd[i+i] = pWordA[i]*pWordA[i];
    }

    for(i=0; i<index; ++i) {
        for(j=index; j<biA->length; ++j) {
            pPrd[i+j] += ((pWordA[i] * pWordA[j]) << 1);
            // 如果超出单位长度能表示最大的值(本例中是2<<16),则进行一次格式化处理
            if(pPrd[i+j] >> BIT_PRE_WORD) formatHBInt(&biT);
        }
    } 

    trimHBInt(&biT);//去除高位无效的0
    extendHBInt(product,biT.length);
    assignHBInt(product,&biT);
    deleteHBInt(&biT); //清除临时变量  

    return RETURN_OK_BINT;
} 

展望:

在程序中其实复杂度为O(n+n^2/4),相较于纯平方级的复杂度可谓提升有限,但是为并行算法的实现提供了一个思路,因为可以做数据分解,分解的粒度可以根据处理机的配置而定制。后续笔者会对所有的算法(超长整数部分)进行并行改造,一方面提升自己的能力,同时也请各位指正。毕竟大家好,才是真的好!

时间: 2024-08-07 14:02:13

超长整数的基础运算 算法实现自平方篇的相关文章

超长整数的基础运算 算法实现之进制转换篇

十进制转二进制 由于单个"位"采用的是216-1作为理论最大值,因此在本次大整数的表示过程中每个类似"十进制"位可采用16位的二进制来表示,符号位单独表示. "十"进制转换成二进制,实际上是经过中间状态(即大整数的逻辑存储表示)转化.在转换过程中大整数的每个"位"无耦合,不存在依赖关系,因此实现方式较为单一,即采用十进制数进行不断除2得余数的方式组成二进制的结果.需要特别注意的是二进制字符串不足16位的需要在高位用"

超长整数的基础运算 算法实现之乘、除篇

笔算乘法: 对于m位和n位的输入.传统的乘法须要m*n次主要的乘法,也即算法复杂度为O().我们用纸和笔做乘法运算时,用乘数的每一位乘以被乘数的每一位并加上上一列的进位而产生一行适当移位的中间结果.然后再将各行中间结果相加即得到乘法的终于结果.比如10进制下计算189*34的步骤例如以下表: 笔算乘法的运算过程 本算法依照上述过程进行计算.但在计算机上最好是把内部的乘法和加法并行的运行.即计算每一行中间结果的同一时候将该行加到终于结果上.这样既可以省去不少步骤,也避免了中间结果的空间开销和内存管

超长整数的基础运算 之小结

首先将之前使用的内部函数一一说明实现 输入.输出: /* 大整数扩大radix倍 大整数的每一位都要乘以radix */ int hbi_mul_radix(HBigInt *a, int radix){ <span style="white-space:pre"> </span>long i; <span style="white-space:pre"> </span>unsigned int carry=0,res

华为测试 超长整数相加

请设计一个算法完成两个超长正整数的加法. 输入两个字符串数字 输出相加后的结果,string型 样例输入:99999999999999999999999999999999999999999999999999 1 样例输出:100000000000000000000000000000000000000000000000000 #include<iostream> #include<string> using namespace std; string add(string num1,

十大基础实用算法之归并排序和二分查找

归并排序 归并排序是建立在归并操作上的一种有效的排序算法.该算法是采用分治法(Divide and Conquer)的一个非常典型的应用. 算法步骤: 1. 申请空间,使其大小为两个已经排序序列之和,该空间用来存放合并后的序列 2. 设定两个指针,最初位置分别为两个已经排序序列的起始位置 3. 比较两个指针所指向的元素,选择相对小的元素放入到合并空间,并移动指针到下一位置 4. 重复步骤3直到某一指针达到序列尾 5. 将另一序列剩下的所有元素直接复制到合并序列尾 用分治策略解决问题分为三步:分解

『基础多项式算法总结』

在教练的要求下开始学习多项式算法了,不过因为不太会积分和求导先把多项式牛顿迭代,多项式指数函数,多项式幂函数,多项式快速幂等内容咕掉了,于是这一篇博客就是其他基础多项式内容的总结. Fast Fourier Transform \(FFT\),快速傅里叶变换,可以在\(O(n\log_2n)\)的时间内计算多项式乘法. 先回忆一下\(FFT\)的思路,首先是多项式的系数表达法,如果直接计算的话,时间复杂度是\(O(n^2)\)的,可以用分治算法优化到\(O(n^{log_23})\),通常没有其

0基础学算法 第二弹 排序

大家好啊,这是0算法基础学算法系列第二篇,上次我在第一弹里讲了关于流程图的内容,我寻思着,这次讲些什么好呢,于是我决定,教大家一个很基础的算法,那就是排序,排序有很多方法,如果你有更多方法请在评论区里留言哦. 排序在程序中特别实用,常用的有快速排序,桶排序,冒泡排序,插入排序等等,在这里我不建议使用冒泡排序或者插入排序,建议桶排序和快速排序,这两个排序非常实用,时间复杂度低,理解起来也很容易,首先,你先思考一下,怎么用程序进行排序,然后你再来看看你的思路合理不合理,最后试着用程序实现它,实现后你

程序员必须知道的10大基础实用算法及其讲解

程序员必须知道的10大基础实用算法及其讲解 原文出处: cricode 算法一:快速排序算法 快速排序是由东尼·霍尔所发展的一种排序算法.在平均状况下,排序 n 个项目要Ο(n log n)次比较.在最坏状况下则需要Ο(n2)次比 较,但这种状况并不常见.事实上,快速排序通常明显比其他Ο(n log n) 算法更快,因为它的内部循环(inner loop)可以在大部分的架构 上很有效率地被实现出来. 快速排序使用分治法(Divide and conquer)策略来把一个串行(list)分为两个子

程序员必知的10大基础实用算法

    算法一:快速排序算法 快速排序是由东尼·霍尔所发展的一种排序算法.在平均状况下,排序 n 个项目要Ο(n log n)次比较.在最坏状况下则需要Ο(n2) 次比较,但这种状况并不常见.事实上,快速排序通常明显比其他Ο(n log n) 算法更快,因为它的内部循环(inner loop)可以在大部分的 架构上很有效率地被实现出来. 快速排序使用分治法(Divide and conquer)策略来把一个串行(list)分为两个子串行(sub-lists). 算法步骤: 1 从数列中挑出一个元