严格来说,我并不知道何谓“水仙花数”,因为以前读书时根本没听过这种数,也不知道这种数有什么特征。后来从事编程之后反而听说了所谓的“水仙花数”。

如果通过网络查询,则发现水仙花数的定义也不统一,比如通过baidu百科查到如下定义:

水仙花数(Narcissistic number)也被称为超完全数字不变数(pluperfect digital invariant,PPDI)、自恋数、自幂数、阿姆斯壮数或阿姆斯特朗数(Armstrong number),水仙花数是指一个 3 位数,它的每个位上的数字的 3次幂之和等于它本身(例如:1^3 + 5^3+ 3^3 = 153)。

但也有资料将“水仙花数”等同于“自幂数”——只要该数的每个位上的数字的N次方(N等于该数的位数)的和等于该数即可。

不管怎样,这些不是我们关注的重点。程序员就是根据客户需求(业务规则)进行实现——规则你们来定,我们负责实现!

通过上面不难发现,判断一个数是否为水仙花数,首先要获得该数在个位、十位、百位……上的数字,然后计算这些数字的N次方,并将它们加起来即可。

先看真正“水仙花”数的简单计算方法:

基于循环来计算严格的“水仙花数”

 1# 方法一2start = 1013end = 9994for i in range(start, end + 1):5    # 计算百位上的数6    bai = i // 1007    # 计算十位、个位上的数8    shi, ge = (i - bai * 100) // 10, i % 109    # 判断是否为水仙花数
10    if ge ** len(str(i)) + shi ** len(str(i)) + bai ** len(str(i)) == i:
11        print(i)

上面算法只能计算三位的水仙花数,该算法只需要使用单层循环,并通过数学整除、求余来计算百位、十位、个位上的数,然后判断该数是否为水仙花数。

总结来说,这个算法简单、易懂、适合初学者上手学习,而且这个算法只需要单层循环;这个算法最大的问题是不适合计算多位的“自幂数”。

下面对这个方法略作改进。

基于循环来计算“自幂数”(非严格“水仙花数”)

下面方法就是可以计算任意范围(只要不超过Python整数的取值范围)的水仙花数(自幂数),下面这个算法将采用循环来计算各数位上的数值。

 1# 方法二2end = int(input('请输入最大范围:'))3for i in range(1, end + 1):4    # 计算数字i的长度5    length = len(str(i))6    sm = 07    temp = i8    for j in range(length):9        # 对于10求余等到个位上的数字,
10        # 然后计算length次方,并累加其总和
11        sm += (temp % 10) ** length
12        # 将目标数缩小10倍,下一步将会获取十位上的数字
13        # 依次类推,下一次获取百位、千位上的数
14        temp //= 10
15    # 判断是否为水仙花数
16    if sm == i:
17        print(i)

这个算法与前面算法基本相似,区别只是这个算法需要通过依次求余来获取个位、十位、百位、千位……上的数,由于程序并不知道要判断的数到底有几位,因此程序使用了循环依次求余来获取个位、十位、百位、千位……上的数。

这个算法是前一个算法的稍作改进。

此外,我们知道Python的字符串也是可迭代对象,当程序迭代字符串时,程序就可以依次获取字符串中的每个字符,因此程序可同构这种方式来获取一个数在在个位、十位、百位……上的数字。

通过遍历字符串来计算“自幂数”(非严格“水仙花数”)

下面程序只是对前一个程序的改变,本程序不再使用数学的求余、整除算法来计算个位、十位、百位……上的数字,而是通过遍历字符串来获取个位、十位、百位……上的数字。

 1# 方法三2end = int(input('请输入最大范围:'))3for i in range(1, end + 1):4    # 计算数字i的长度5    length = len(str(i))6    sm = 07    # 将i转成字符串,然后通过遍历字符串来依次获取每位数字8    for j in str(i):9        sm += (ord(j) - 48) ** length
10    # 判断是否为水仙花数
11    if sm == i:
12        print(i)

这个算法与前一个算法的区别在于计算计算个位、十位、百位……上的数字的方法不同,本程序采用的是遍历字符串的方式进行计算。

此外,Python还提供了一个sum()函数来计算列表的总和,因此程序可以将各数位上的值的N次方收集成一个列表,然后利用sum()函数来计算该列表的总和,这样就可判断该数是否为水仙花数了。

利用列表推导式来计算“自幂数”(非严格“水仙花数”)

下面采用一个嵌套的列表推导式来计算水仙花数(自幂数)。

1# 方法四
2end = int(input('请输入最大范围:'))
3lt =[j for j in range(1, end + 1) if sum([(ord(i) - 48) ** len(str(j)) for i in str(j)]) == j]
4print(lt)

从上面代码可以看到,该程序只要一行就可以计算所有水仙花数(自幂数),该程序的本质是一个嵌套循环——只不过它是嵌套的列表推导式。

首先列表推导式的语法是:

for表达式用于利用其他区间、元组、列表等可迭代对象创建新的列表,for表达式语法格式如下:

[表达式 for 循环计数器 in 可迭代对象]

由于上面列表推导式存在嵌套,因此我们先看一层,如果将上面推倒使式写成如下形式:

lt =[j for j in range(1, end + 1) if j % 2 == 0]

此时该列表内将会收集从1~end的所有偶数(根据if j % 2 == 0),此时该列表推导式只有一层,并没有嵌套。

但我们并不是要简单地收集偶数,而是要收集水仙花数(自幂数),因此程序还得搞一个列表,该列表的元素是个位、十位、百位……上数字的N次方。

如何获取一个数的个位、十位、百位……上数字的N次方呢?前面已经介绍了,使用循环来遍历字符串即可。假如目标数字是j,那下面代码即可获取数值j在个位、十位、百位……上数字的N次方。

[(ord(i) - 48) ** len(str(j)) for i in str(j)]

再回头看到前面的列表推导式,它的完整格式其实就是:

[j for j in range(1, end + 1) if sum(xxx) == j]

只不过它的xxx就是[(ord(i) - 48) ** len(str(j)) for i in str(j)]。

这个算法比较简洁,只要一行代码即可计算使用列表获取指定范围的所有水仙花数,但有些初学者会反应这个列表推导式不容易看懂,这可能也是这个算法的一个问题:一般来说,我们并不建议使用多层嵌套的列表推导式,因此这样会降低程序的可读性。毕竟,对于实际企业开发来说,程序可读性才是第一位的。

上面这些算法来计算10的5次方以内的“自幂数”时,能拥有较高的效率,但一旦要计算10的8次方、甚至10的20次方以内的“自幂数”时,程序效率会变得非常低——这是由于程序本身采用是循环来判断每个数字,这种循环本身有性能开销,因此效率较低。

高效计算 “自幂数”(非严格“水仙花数”)

下面介绍一种较为高效的算法,这个算法利用了列表来减少计算,程序将“存放数字0-9的num次方的N倍(代表出现次数)的值”使用列表保存下来,这样可避免每次都要重新计算数字0-9的num次方。

此外,该算法还利用了一种预检查的方法来快速排除不符合条件的目标数,这样能更快地加速自幂数的查找效率。

该程序代码如下。

  1# 方法五(高效)2def narcissus_num(num):3    # results 存放找到的自幂数4    results = [] 5    # 定义一个长度为10的列表6    selected = [0] * 107    # power_of_10列表依次保存[0, 10, 100, 1000, 10000, ...]8    # 该列表中的元素都是10的N次方9    power_of_10 = [10 ** i for i in range(num + 1)]10    # pre_table1 存放数字0-9的num次方的N倍(代表出现次数)的值11    # 例如num为6,pre_table1的元素依次为12    # [[0**6 * 0, 0**6 * 1, ...0**6 * 6], [1**6 * 0, 1**6 * 1, 1**6 * 2, ...]]13    pre_table1 = [[i ** num * j for j in range(num + 1)] for i in range(10)]14    # pre_table2是一个长度为10的列表,每个列表元素又是一个长度为num+1、元素为0的列表15    pre_table2 = [[0] * (num + 1) for i in range(10)]16    # num位的自幂数应该在power_of_10[num - 1](10**num-1)~power_of_10[num](10**num)之间17    min_num = power_of_10[num - 1]18    max_num = power_of_10[num]19    # 对pre_table2进行初始化,让它存放pre_table1中各个值除首位外的位数20    for i in range(10):21        for j in range(num + 1):22            for k in range(num, 0, -1):23                if power_of_10[k] < pre_table1[i][j]:24                    pre_table2[i][j] = k25                    break2627    # 检查value是否为自幂数28    def check_narcissus(value):29        bit_result = bit_count(value)30        for i in range(10):31            if bit_result[i] != selected[i]:32                return False33        return True3435    # 统计value中数字的个数,返回形如[0, 2, 2, 0, 0, 0, 0, 0, 0, 0]的列表,36    # 列表中每个元素依次代表value中0、1、2、3、4...的出现次数37    def bit_count(value):38        # 定义一个长度为10的列表39        bit_result = [0] * 1040        # 依次遍历每个位上的数,并用bit_result列表保存每个数位上的数字的出现次数。41        for i in str(value):42            bit_result[int(i)] += 143        # 假如最后返回[0, 2, 2, 0, 0, 0, 0, 0, 0, 0]44        # 那代表该数中1出现2次、2出现2次45        return bit_result4647    def pre_check(cur_index, sum, remain_num):48        cur_big = pre_table1[cur_index][remain_num]49        # 如果sum比当前数剩余最大可能数小,说明还有可能找到50        if sum < cur_big:51            return True52        max = sum + cur_big53        # 去掉cur_big的位数54        max = max // power_of_10[pre_table2[cur_index][remain_num]]55        sum = sum // power_of_10[pre_table2[cur_index][remain_num]]56        # 去掉max,sum不同的尾部。57        while not max == sum:58            max = max // 1059            sum = sum // 1060        # max,sum头部没有相同部分。61        if max == 0:62            return True63        bit_result = bit_count(max)64        # 判断大于cur_index 的所有已确定数是否在正常范围。65        for i in range(9, cur_index, -1):66            if bit_result[i] > selected[i]:67                return False68        # 判断bit_result中小于cur_index的数(从9到0还没有判断的数)的数量是否大于remain_num69        for i in range(cur_index + 1):70            remain_num -= bit_result[i]71         # 小于remain_num,属正常,返回True。72        return remain_num >= 07374    def search_num(cur_index, sum, remain_num):75        # 如果sum已经大于max_num最大值,说明已经不可能了,直接返回76        if sum > max_num:77            return78        # 如果sum加上cur_index的num次方remain_num倍,依然小于min_num最小值79        # 说明已经不可能了,直接返回80        if (sum + pre_table1[cur_index][remain_num]) < min_num:81            return8283        # 如果不符合预检查,直接跳过84        if not pre_check(cur_index, sum, remain_num):85            return8687        if remain_num == 0:88            # 如果检查sum符合自幂数特征,将该数添加到results列表中89            if sum > min_num and check_narcissus(sum):90                results.append(sum)91            return9293        if cur_index == 0:94            selected[0] = remain_num95            # 递归调用search_num96            search_num(-1, sum, 0)97        else:98            for i in range(remain_num + 1):99                selected[cur_index] = i
100                search_num(cur_index - 1, sum + pre_table1[cur_index][i], remain_num - i)
101        # 对selected[cur_index]进行复位
102        selected[cur_index] = 0
103
104    # 设定初始值调用search_num,然后返回结果。
105    search_num(9, 0, num)
106    return results
107
108num = int(input('请输入要计算几位的自幂数:'))
109print('%d位的自幂数有:%s' % (num, narcissus_num(num)))

另外本人还开设了个人公众号:JiandaoStudio ,会在公众号内定期发布行业信息,以及各类免费代码、书籍、大师课程资源。

扫码关注本人微信公众号,有惊喜奥!公众号每天定时发送精致文章!回复关键词可获得海量各类编程开发学习资料!

例如:想获得Python入门至精通学习资料,请回复关键词Python即可。

Python经典练习题——求水仙花数相关推荐

  1. C语言经典题目 水仙花数,C语言经典练习题:水仙花数

    废话不说直接进入正题:传说中的水仙花数就是一个三位数的各个位数的三次方之和就是这个数本身例如153=1*1*1+5*5*5+3*3*3. 好,开始解题:首先,题中已经说了水仙花数是一个三位数,也就是说 ...

  2. python中如何求水仙花数_python如何求水仙花数?

    水仙花数(Narcissistic number)也被称为超完全数字不变数(pluperfect digital invariant, PPDI).自恋数.自幂数.阿姆斯壮数或阿姆斯特朗数(Armst ...

  3. python基础练习题(按条件对指定序列求和,打印99乘法表、求斐波那契数列、百马百担、求水仙花数、求n以内的所有质数(素数)和)、集合的讲解、一些公关方法

    1.求100(含100)以内所有偶数的和 range(start,end,step)这个序列生成器,和那个切片的语法一样,含头不含尾,step是步长,这里就不需要在对j进行判断了,对于这些简单求奇数和 ...

  4. Python基础练习题(按条件对指定序列求和,打印99乘法表、求斐波那契数列、百马百担、求水仙花数、求n以内的所有质数(素数)和)

    1.求100(含100)以内所有偶数的和 range(start,end,step)这个序列生成器,和那个切片的语法一样,含头不含尾,step是步长,这里就不需要在对j进行判断了,对于这些简单求奇数和 ...

  5. C++中如何读取一个数的位数_C语言编写程序求水仙花数

    C语言编写程序求水仙花数 水仙花数是一个数学问题,其实质是一个三位数,个位数的立方加十位数的立方加百位数的立方之和等于这个三位数本身.例如153=1*1*1+5*5*5+3*3*3,即153=1+12 ...

  6. 水仙花数c语言程序解析,C语言求水仙花数代码解析

    原标题:C语言求水仙花数代码解析 水仙花数 "水仙花数"又称为"阿姆斯特朗数".如果一个n(n≥3)位数的各位数字的n次幂之和等于该数本身,则该数称为" ...

  7. C语言编程:求水仙花数。输入一个正整数n,计算n位水仙花数。

    /*求水仙花数.输入一个正整数n,计算n位水仙花数.*/ #include<stdio.h> //编译预处理命令int mypow (int x, int n); //声明自己的幂函数in ...

  8. C语言求水仙花数(自幂数)

    C语言求水仙花数 什么是水仙花数 水仙花数是指一个 3 位数,它的每个位上的数字的 3次幂之和等于它本身.例如:1^3 + 5^3+ 3^3 = 153 函数实现 代码如下: #define _CRT ...

  9. dowhile实现求水仙花数

    水仙花数:每位数字的三次方相加等于该数字,需求得三位数的水仙花数 #include<iostream> using namespace std; int main() {//主函数返回值为 ...

最新文章

  1. 2021全国高校计算机能力挑战赛(初赛)C语言
  2. 简单描述Java模板引擎 FreeMarker相关知识
  3. JS 判断js是加载完成!
  4. C#编程语言之MD5加密的两种方式
  5. Windows四大傻X功能——那些拖慢系统性能的罪魁祸首
  6. python的类与模块_Python类与模块属性
  7. [AH/HNOI2017]大佬
  8. 域添加另一台机器_巨杉Tech | SequoiaDB数据域概念解读与实践
  9. STM32之内部FLASH例程
  10. yii2 shi用modal弹窗 select2搜索框无法使用
  11. EJB的beans们
  12. 2.Linux环境下配置Solr4.10.3
  13. 模糊PID算法及其MATLAB仿真(2)
  14. 少儿Python编程教程
  15. ppt背景图片php,ppt背景图片怎么设置 ppt幻灯片制作视频
  16. Word中公式输入的快捷键
  17. 用智能硬件“折叠时间”,外卖商家正在触摸的数字化未来
  18. win7硬盘分区软件_误删数据恢复软件,你应该拥有它!
  19. 两步解决XMind发生了错误,请参阅日志文件
  20. 计算机屏幕变红色,win7系统电脑屏幕变成红色的设置技巧

热门文章

  1. 英语语法篇 - 各类词性和句子成分的作用
  2. 百度网盘真实地址解析(告别下载百度网盘)
  3. D. Flood Fill
  4. MySQL索引数据结构及算法原理
  5. 微信公众号发送小程序卡片_如何在公众号文章中添加小程序卡片
  6. H5调用手机的前后摄像头,canvas显示,自带截图,兼容ios和android
  7. 机器学习并没有那么深奥,它很有趣(2)
  8. Kubectl(完整)基本操作命令
  9. 环境实验耐高温、耐低温、温度变化、恒定湿热、湿热循环
  10. 当涉及某个项目需要大量使用到tensorflow时,最后不要使用tensorflow的GPU版本,这会出很多毛病,最好使用CPU版本