免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 1549 | 回复: 1
打印 上一主题 下一主题

[其他] 一种快速求幂的算法,大神们来议论两句吧! [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2013-12-08 22:54 |只看该作者 |倒序浏览
  正整数求幂,一般的写法是:
[code=c]pow_int:        ;pow_int(int base, int exp)
        mov ebx,[esp+4]
        mov ecx,[esp+8]
        mov eax,1
        .mult:
        mul ebx
        loop .mult
       
        ret[/code]
  我今天想到了一种加速的算法(当然,肯定早就有人想到了,只是我孤陋寡闻没学过):例如计算3的21次方,3exp(21)可以拆解成:
  3exp(16) + 3exp(4) +3exp(1)
  我们求3exp(16)时候,只需要4次乘法,3exp(4)只要2次乘法,而且在求3exp(16)的过程中,顺便就求出了3exp(4)。
  下面我粗糙的用汇编实现了,只能处理 1<指数<32的情形。
[code=c]
[section .text]
pow_int_fast:
        mov eax,[esp+4]
        mov ebx,[esp+8]
        mov edi,magic_val
        mov [edi],eax

        bsr ecx,ebx
       
        .fill_magic:
        mul dword [edi]
        add edi,4
        mov [edi],eax
        loop .fill_magic

        mov eax,1
        mov esi,magic_val
       
        .accumulate:
        bsf ecx,ebx
        jz .return
        btr ebx,ecx
        mul dword [esi+ecx*4]
        jmp .accumulate
       
        .return:
        ret

[section .data]
magic_val:
times 5 dd 0[/code]
这两个汇编模块运行结果都是正确的。我对比测试他俩的速度,几乎没有区别!
我的cpu是酷睿i5的,我怀疑后者虽有算法优势,但代码较长,对cache,流水和分支预测都不友好,因此将功折罪了。大神们能否指点指点?

下面是我的测试代码:
[code=c]#include<stdio.h>
#include<sys/time.h>
#define timelen(a,b) ((b.tv_sec - a.tv_sec)*1000 +(b.tv_usec - a.tv_usec)/1000)
int pow_int(int base, int exp);
int pow_int_fast(int base, int exp);
int main(void){
        int base;
        int repeat;
        int exp;
        struct timeval t0,t1,t2;
        while(printf("\nbase exp repeat\n"),scanf("%d%d%d",&base,&exp,&repeat)){
                int x,x_fast;
                repeat*=1000000;
                for(int k=0;k<5;k++){
                        gettimeofday(&t0,0);
                        for(int i=0;i<repeat;i++) x=pow_int(base,exp);
                        gettimeofday(&t1,0);
                        for(int i=0;i<repeat;i++) x_fast=pow_int_fast(base,exp);
                        gettimeofday(&t2,0);
                        printf("x:%10d%10d\ny:%10d%10dfast\n",x,timelen(t0,t1),x_fast,timelen(t1,t2));
                }
        }
        return 0;
}[/code]
测试如下:
第一组:
base exp repeat
2    20  10
x:   1048576       380
y:    1048576        126
x:   1048576       347
y:    1048576        127
x:   1048576       348
y:    1048576        126
x:   1048576       347
y:    1048576        128
x:   1048576       347
y:    1048576        126

第二组:
base exp repeat
3    10  10                                                                        
x:     59049       193
y:      59049        108
x:     59049       183
y:      59049        107
x:     59049       183
y:      59049        108
x:     59049       183
y:      59049        108
x:     59049       184
y:      59049        107

把测试代码里两行for(int i=...)交换位置
第一组:
2    20  10
x:   1048576       132
y:    1048576        345
x:   1048576       126
y:    1048576        347
x:   1048576       126
y:    1048576        346
x:   1048576       126
y:    1048576        346
x:   1048576       126
y:    1048576        344

第二组:
3    10  10
x:     59049       117
y:      59049        182
x:     59049       107
y:      59049        183
x:     59049       107
y:      59049        184
x:     59049       108
y:      59049        183
x:     59049       108
y:      59049        183

之所以交换位置,是发现交换位置后输出结果差别巨大,似乎先测试的模块总是运行的慢,后测试的模块总是运行的快。交换就像是高中时候做的对照实验。

论坛徽章:
59
2015年亚洲杯之约旦
日期:2015-01-27 21:27:392015年亚洲杯之日本
日期:2015-02-06 22:09:41拜羊年徽章
日期:2015-03-03 16:15:432015年辞旧岁徽章
日期:2015-03-03 16:54:152015年迎新春徽章
日期:2015-03-04 09:50:282015元宵节徽章
日期:2015-03-06 15:50:392015年亚洲杯之阿联酋
日期:2015-03-19 17:39:302015年亚洲杯之中国
日期:2015-03-23 18:52:23巳蛇
日期:2014-12-14 22:44:03双子座
日期:2014-12-10 21:39:16处女座
日期:2014-12-02 08:03:17天蝎座
日期:2014-07-21 19:08:47
2 [报告]
发表于 2013-12-08 23:15 |只看该作者
做大数运算时, 很自然不会想到这个方法, 不然没办法计算啊。
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP