只是呼吸 发表于 2016-1-11 21:55:42

大整数的平方代码

gxqcn曾经说过,大整数的平方有快速算法。经本人实践后,果然如此。虽然我写的程序显得幼稚,但平方算法确实比调用乘法要快大约一倍的时间。
以下是本人的习作。

      编译环境:Windows xp ,vc++6.0,debug模式,32位环境。
      硬件设备:AMD Athlon(tm) II X2250          3.0GHz   2.00G的内存
      运行时间:一个14700位(十进制)数的平方,用时<2.7毫秒。
      函数原形: void _axa_( UINT32 *result, UINT32 * left,int LL)
             其中:*result 平方后的结果。 * left 待平方的数字。LL, *left的长度。
             进制:采用10000 0000(10^8)进制。十进制数高位对应数组较大的下标,十进制数的低位对应数组较小的下标。
      特别声明:1)*result 要用到的存贮空间,是待平方数* left 的实际空间的4倍。
                        2)待平方数* left 的长度不能超过14700位十进制数。


   //   特别声明:1)*result 要用到的存贮空间,是待平方数* left 的实际空间的4倍。
   //          2)待平方数* left 的长度不能超过14700位十进制数。
   //          3)如果采用VC++6.0以外的编译器,请确保每个存贮单元为32位。
   //          4)*result的空间在使用之前要进行清零

_declspec(naked)
void _axa_( unsigned int *result, unsigned int *left,int LL)
{
#undef LEFT_REG
#undef RIGHT_REG
#undef RESULT_REG
#define LEFT_REG      esi
#define RIGHT_REG      edi
#define RESULT_REG      ebp
       
        __asm
        {
        push      esi
      push      edi
      push      ebx
      push      ebp
///------------------------------------------------------------------------////               

      mov      RIGHT_REG, dword ptr   ;right
      mov      ebx,dword ptr   ; //LL
      sub         ebx,1   ///控制外层循环。
        je            kkkk            

loop01:
       mov      eax,dword ptr////每次读取新的LL。eax用于控制
       mov      ecx,ebx                   ;;///赋值给ecx控制内层循环
       sub         eax,ebx            

      mov       LEFT_REG,dword ptr    ; //left每次进入内循环时取新值。
      lea          LEFT_REG,    ////这个eax控制LEFT_REG的位置

      shl          eax,1;;///乘以2
        sub      eax,1 ;;

      mov      RESULT_REG, dword ptr   ;
      lea         RESULT_REG,///这个eax控制RESULT_REG的位置
loop00:
      mov      eax,dword ptr
      mul       dword ptr             

      add       dword ptr ,eax
      adc       dword ptr ,edx      

      lea      LEFT_REG,
      lea      RESULT_REG,
      dec       ecx
      jnz      loop00;;///内层循环

      lea      RIGHT_REG,
       dec       ebx
      jnz       loop01/////外层循环
/*//---------------------------------------------------------------///
   以上loop00和loop01循环,用来求非平方项的乘积,
   结果保存在两个相邻的RESULT和RESULT中,
   不考虑最终的模10^8进位

///---------------------------------------------------------------*///   


        mov         RESULT_REG, dword ptr
        mov         ecx,dword ptr///每次读取新的LL
        shl            ecx,1;;///乘以2
        sub          ecx,3
loop02:
        mov      eax,dword ptr
        shld         dword ptr,eax,1
        shl         dword ptr,1

        lea          RESULT_REG,
        dec         ecx
        jnz          loop02
/*///-----------------------------------------------------------------------------------------////
    以上的loop02用来将非平方项的结果乘以2, 不考虑最终的模10^8进位

          
////-----------------------------------------------------------------------------------------*////
kkkk:
        mov      ecx,dword ptr
        mov      RESULT_REG, dword ptr
        mov      LEFT_REG, dword ptr   ;right
loop03:
        mov      eax,dword ptr
        mul          dword ptr

        add      dword ptr ,eax
        adc      dword ptr ,edx

        lea      RESULT_REG,
        lea      LEFT_REG,
        dec      ecx
        jnz      loop03
/*///-------------------------------------------------------------------------------////
          以上的loop03用来把平方项加到结果中,不考虑进位。

////-------------------------------------------------------------------------------*////

      xor      edx,edx
      mov      ebx,100000000
      mov      ecx,dword ptr
      shl         ecx,1
      mov      RESULT_REG, dword ptr   ; result
loop04:
        mov      eax,dword ptr ///取本位的高32位做除法
        div         ebx

        add      dword ptr ,eax///商加在下一个数的高32位
        mov       eax, dword ptr ///取本位数的低32位做除法
        div          ebx

        mov       dword ptr ,edx///本位的余数,就是结果
        xor         edx,edx
        add      dword ptr ,eax ///本位的商,加在下一位的低32位上。
        adc      dword ptr ,edx;;

        lea      RESULT_REG,
        sub       ecx,1
        jnz      loop04
/*///---------------------------------------------------------------------------////
    以上的loop04语句用来将RESULT和RESULT组成的64位中间结果进位到
    RESULT[(i+1)*8+4]和RESULT[(i+1)*8]并获得正确结果
////----------------------------------------------------------------------------*////
      mov      ecx,dword ptr
        shl         ecx,1

      mov      edx,1
        xor          ebx,ebx
        mov      RESULT_REG, dword ptr   ; result
loop05:
        mov       eax, dword ptr
        mov       dword ptr ,eax

        mov       dword ptr,ebx
        mov       dword ptr,ebx

        add      edx,1
        sub      ecx,1
        jnz         loop05

/*//--------------------------------------------------------------------------/////
        以上的loop05语句,用来把正确结果移动到正常位置,
        并将多余的数清零。
//*-------------------------------------------------------------------------*////


        pop      ebp
      pop      ebx
      pop      edi
      pop      esi

      ret
        }
}/////*///




只是呼吸 发表于 2016-1-11 23:13:05

这里给出一楼函数的一个调用示例:



#include <stdio.h>
unsigned int bignum={0}, result={0};
//----------------------------------------//
//把一楼的代码粘贴在这里
//----------------------------------------//
void main()
{          
        int i,s,Len;
//ch[]=30000000000321456987100000009999999999999999993214
//将ch[]按10^8为进位的基,存入数组bignum[]中。
                bignum=99993214;
                bignum=99999999;
                bignum=999999;
                bignum=87100000;
                bignum=3214569;
                bignum=0;
                bignum=30;
//大整数的长度是:
       Len=6+1;
//调用一楼的函数,注意函数的接口条件:
_axa_(result,bignum,Len);
      i=100;
               while((result==0)&&(i>0))//前导零清除。
                         {
                                 i--;
                         }      
                printf("%d",result);//输出语句。把乘积的结果输出。
                for(s=i-1;s>=0;s--)//输出语句。把乘积的结果输出。
                     printf("%08d",result);

//这里是实际的平方结果:
//result[]=bignum[]^2=9000000000 1928741922 6103335194 5554095728 3913933484
//                  0000095637 1857710787 9986428000 0000000000 046049796
// 共99位数。实际输出数字是全部连在一起的,为了方便我把它写成了10位一组,                     

                printf("\n");   
return 0;
}

无心人 发表于 2016-1-13 16:30:24

有更好的算法哦~~~

只是呼吸 发表于 2016-1-13 20:21:16

本帖最后由 只是呼吸 于 2016-1-13 20:43 编辑

有更好的算法哦~~~

咳咳……。我不会哦,我只会最原始的啊。这个算法就是模拟手算的平方算法,比调用乘法快。
页: [1]
查看完整版本: 大整数的平方代码