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 个难点:
- 一个数的 21 次幂极为庞大,即使 unsigned long 也显得无能为力;
- 即使是 C 语言这样运行效率极高的语言,穷举判断也很有可能超出 3 分钟的时限。
1、借助数组逐位运算
为了进一步减少程序运行时间,我们可以预存 0 到 9 的 21 次幂的结果,之后采用查表的方法求各位数的 N 次幂之和。
展开(隐藏)
展开(隐藏)
2、搜索水仙花数的方法
从 x0=9×1020 开始,通过 y⋅V 之间的关系判定所属情况(如下表所列),然后搜寻下一个 x,具体步骤为:
- 从高位开始依次寻找 x 中首个为 0 的位 bk:若为情况 2,则将高一位赋值于它 bk=bk+1;若为情况 3,则将高一位减 1,即 bk+1=bk+1−1;
- 如果 x 不存在 0,则从高位开始依次寻找 x 中首个为 1 的位 bk,将高一位减 1,即 bk+1=bk+1−1,同时清零 bi,i<=k;
- 如果 x 不存在 0 和 1,则将 x 的末位 b0 减小 1。
显然,按上述方法操作 x 低位的数总不能大于高位的数,考虑下表情况 1 可知:程序终止条件为 b20<8。
情况 | x=20∑i=0bi×10i | y=f(x)=20∑i=0b21i | V=(1020,1021) | 说明 |
---|---|---|---|---|
1 | ∀bi<8 | maxy=21×721 | y∉V,y<1020 | 若 x 不含数字 8 或 9,则 y 一定不足 21 位 |
2 | 9×1020 | 109418989131512359209 | y∈V | 若 x,y 只是数字的排列顺序不同,则 y 为水仙花数 |
3 | (1010−1)×1010 | 1094189891315123592090 | y∉V,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;
- 显然 x 与 y 只是数字的排列顺序不同,因此 y 是水仙花数,继续,直至 x 的最高位为 3;
- 由于 x 低位的数不会大于高位,因此 y 的最大值也不超过三位数,判断结束。
三、C 程序源代码及其运行结果
展开(隐藏)
展开(隐藏)
四、Python 实现方案及其运行结果
展开(隐藏)
展开(隐藏)
比 C 语言多耗费了 5.7 倍的执行时间,不过好在勉强完成了任务。
人生苦短,我用 Python。Pythoner 都以这句话引以为傲,因为 Python 的开发效率非常高,而且强制的缩进,使得不管是写代码的人还是看代码的人都非常清楚。人生只有短短几十年,开发效率低无疑浪费生命。如果不是对运行效率有更为严苛的要求,试着让自己轻松一些吧!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步