Processing math: 100%
那 些 回 忆 丶 挥 之 不 去 Cnblogs Pandaman Dashboard

3 分钟内找到全部的 21 位水仙花数

一、问题描述

一个 N 位的十进制正整数,如果它的每个位上的数字的 N 次方的和等于这个数本身,则称其为水仙花数。

例如:当 N=3 时,153 就满足条件,因为 13+53+33=153;当 N=4 时,1634 满足条件,因为 14+64+34+44=1634;当 N=5 时,92727 满足条件。实际上,对 N 的每个取值,可能有多个数字满足条件。

程序的任务是:求 N=21 时,所有满足条件的花朵数。

注意:这个整数有 21 位,它的各个位数字的 21 次方之和正好等于这个数本身。如果满足条件的数字不只有一个,请从小到大输出所有符合条件的数字,每个数字占一行。因为这个数字很大,请注意解法时间上的可行性。要求程序在 3 分钟内运行完毕。

二、难点与思路

这个问题用 C 语言实现主要面临 2 个难点:

  1. 一个数的 21 次幂极为庞大,即使 unsigned long 也显得无能为力;
  2. 即使是 C 语言这样运行效率极高的语言,穷举判断也很有可能超出 3 分钟的时限。

1、借助数组逐位运算

为了进一步减少程序运行时间,我们可以预存 0 到 9 的 21 次幂的结果,之后采用查表的方法求各位数的 N 次幂之和。

展开(隐藏)

展开(隐藏)

2、搜索水仙花数的方法

x0=9×1020 开始,通过 yV 之间的关系判定所属情况(如下表所列),然后搜寻下一个 x,具体步骤为:

  • 从高位开始依次寻找 x 中首个为 0 的位 bk:若为情况 2,则将高一位赋值于它 bk=bk+1;若为情况 3,则将高一位减 1,即 bk+1=bk+11
  • 如果 x 不存在 0,则从高位开始依次寻找 x 中首个为 1 的位 bk,将高一位减 1,即 bk+1=bk+11,同时清零 bi,i<=k
  • 如果 x 不存在 0 和 1,则将 x 的末位 b0 减小 1。

显然,按上述方法操作 x 低位的数总不能大于高位的数,考虑下表情况 1 可知:程序终止条件为 b20<8

情况 x=20i=0bi×10i y=f(x)=20i=0b21i V=(1020,1021) 说明
1 bi<8 maxy=21×721 yV,y<1020 x 不含数字 8 或 9,则 y 一定不足 21 位
2 9×1020 109418989131512359209 yV x,y 只是数字的排列顺序不同,则 y 为水仙花数
3 (10101)×1010 1094189891315123592090 yV,y>1021 y 超出 V 的上界,则改变 x 使 y 减小

再详细解释下为何这样搜索 x(以 3 位数为例):

  • 譬如 x=900 开始,y 也是三位数,但不是水仙花数,就应该找下一个 x=990,即尽可能保证改变一个数位后 y 增加最大,这样可以保证覆盖到所有可能的数;
  • 由于 f(990) 超出了 V 的范围,因此缩小它到 x=980,继续,直到 x=960 又回到 V 的范围;
  • 因为 990,980,970 都在 960 之前判断过,因此改为判断 x=966,继续,直到 x=961,还在 V 的范围内;
  • 因为 960 也已经判断过,所以接下来该判断 x=950,还在 V 的范围内,继续,直到 x=740,求得 y=407
  • 显然 xy 只是数字的排列顺序不同,因此 y 是水仙花数,继续,直至 x 的最高位为 3;
  • 由于 x 低位的数不会大于高位,因此 y 的最大值也不超过三位数,判断结束。

三、C 程序源代码及其运行结果

展开(隐藏)

展开(隐藏)

四、Python 实现方案及其运行结果

展开(隐藏)

展开(隐藏)

比 C 语言多耗费了 5.7 倍的执行时间,不过好在勉强完成了任务。

人生苦短,我用 Python。Pythoner 都以这句话引以为傲,因为 Python 的开发效率非常高,而且强制的缩进,使得不管是写代码的人还是看代码的人都非常清楚。人生只有短短几十年,开发效率低无疑浪费生命。如果不是对运行效率有更为严苛的要求,试着让自己轻松一些吧!

点击右上角即可分享
微信分享提示

目录