水仙花数的改进算法

什么是水仙花数?

请参见:http://baike.baidu.com/view/152077.htm

 

在传统算法中,需要花费大量的时间进行计算,一个是进行幂运算,一个是进行数字的拆分判断。

这个算法在两个地方上进行了改进:

1。 幂运算用一个数组缓冲,这样,只有位数变化的时候,才需要刷新一次缓冲,而且是乘法,不是幂运算,

    有了这个缓冲,幂运算就变成取数组元素的代码。

    这个改进的前提是,我们只需要0~9的n次幂,所以个数是固定的

 

2。判断是否水仙花数,用差值比较,而不是直接判断

    原理是这样的:

   假设一个数A=a(N)a(N-1)...a(0) 是一个水仙花数

   如果另一个数B=b(M)b(M-1)....b(0)也是一个水仙花数  其中 a(x)表示A在第x位上的数字,b(x)也是

   那么,因为 A,B的特性,可以得到

  A-B=[a(N)^N+...a(0)^N ] - [b(M)^M+...b(0)^M]                 (式1)

   为什么要搞成这个样子呢? 因为,如果N=M,A与B的大多数位的数值相同,那么我们只需要计算A、B数字不相同的位就可以判断是否水仙花数。

 

   因为 A-Z=A-B+B-C+C....+Y-Y-Z = (A-B)+(B-C)+(C.....-Y)+(Y-Z)

   所以,计算差值的时候,可以把每次的差值累计起来计算。

 

   因此,我们来看,假设循环每次递增为1,那么,90%的情况下,只有1位数值发生了变化。 判断这个数是不是水仙花数的时候,不需要把所有位上的数字的N次方的值相加再和数字本身比较,而是仅仅计算发生了变化的这一位数字的N次方,和上一个水仙花数的对应位的M次方的差,如果(式1)成立,那么这个数就是一个新的水仙花数。而因为只有一位发生了变化,所以(式1)可以简化成

  A-B=a(x)^N-b(x)^M                                                            (式2)

N是A的位数,M是B的位数。

   这样一来,几乎90%的计算只需要做1位的加减法。前面也说过了,“如果N=M,A与B...”,所以在N!=M的情况下,需要从新调整差值。

    因此,编写代码如下,主要的思想是利用每次判断某个数是否水仙花数的时候的计算结果,减轻下一次计算的负担。

使用这个程序计算10位以下的水仙花数,结果如下:

153
370
371
407
1634
8208
9474
54748
92727
93084
548834
1741725
4210818
9800817
9926315
24678050
24678051
88593477
146511208
472335975
534494836
912985153
Used 32.929 Second

 

而使用简单的每次判断,则运算时间稍长:

153
370
371
407
1634
8208
9474
54748
92727
93084
548834
1741725
4210818
9800817
9926315
24678050
24678051
88593477
146511208
472335975
534494836
912985153
Used 66.258 Second

 

这个优化的效果在9位以下时,效果不明显,以下是计算时间比较:

计算位数简单算法时间(秒)优化算法时间(秒)循环花费时间(秒)
3 0 0 0
4 0 0 0
5 0.016 0 0
6 0.047 0.031 0.016
7 0.546 0.328 0.219
8 6.032 3.313 2.500
9 66.258 32.929 28.926
10 714.458 330.422 330.167
11 。。。 。。。 ...

上表中的“循环花费时间”是指不做判断,只是对数字作++运算。

例如,位数=9,这一行,简单算法共用时66.258秒,其中循环花费时间28.926秒,那么,用于判断的时间为:66.258-28.926=37.328秒;

而同时,优化算法共用时32.929秒,那么判断的时间为32.929-28.926=4.003秒。

 

所以从这里看,数位越大,循环本身花费的时间越成为性能瓶颈。 如果能够提高循环的效率,比如,每次不是增加1,而是增加10以上的一个数,那么效率还可以提高10倍。

我想了一天,也没想到怎么确定递增的步长。 希望高手们继续研究啊

==========================================

 

2010-10-12

----------------------------------------------------

有研究了一天,终于得到一个增加递增步长的办法。

原理是根据差值计算方法判断一个数是不是水仙花数的时候,从个位数为0开始,如果在10个数范围内有一个水仙花数的话,此时计算的sum[a(x)^n-b(x)^m]与A-B的差值一定是固定的。 因为,在这10个数内,sum[a(x)^n-b(x)^m]只会有x=0的变化,以个位数=4时变成水仙花数为例,假设个数+4是水仙花数,那么新增的差值就是 4^n+4. 所以,如果个位数为0时,

    sum[a(x)^n-b(x)^m]与A-B的差值刚好等于 4^n+4, 也就是  sum[a(x)^n-b(x)^m] - (A-B) = 4^n+4

那么,在个位数变成4后, sum[a(x)^n-b(x)^m]的变化就是

    sum[a(x)^n-b(x)^m] + a(0)^n , 其中a(0)=4,

所以上式变成

    (A-B)+4^n+4+4^n = 4+(A-B)

而同时,A-B的值,B一直没有变化,A从一个个位数=0的数,变成个位数=4的数,那么A-B也就变化了4,也是(A-B)+4

所以新的sum[a(x)^n-b(x)^m]=新的A-B,

所以,可以根据个位数=0的时候计算得到的 差值,直接判断出这10个数内有没有水仙花数。

 

据此修改代码的步长计算方法,运算时间比较如下:

计算位数简单算法时间(秒)优化算法时间(秒)步长优化后
算法时间(秒)

循环花费时间 (秒,步长=1)

3 0 0 0 0
4 0 0 0 0
5 0.016 0 0 0
6 0.047 0.031 0.016 0.016
7 0.546 0.328 0.094 0.219
8 6.032 3.313 0.875 2.500
9 66.258 32.929 8.323 28.926
10 714.458 330.42284.955 330.167
11 。。。 。。。 ...... ...

 

 

附代码如下

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值