Python经典练习题——求水仙花数

2023-11-17

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

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

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

 

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

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

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

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

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

 1# 方法一
 2start = 101
 3end = 999
 4for i in range(start, end + 1):
 5    # 计算百位上的数
 6    bai = i // 100
 7    # 计算十位、个位上的数
 8    shi, ge = (i - bai * 100) // 10, i % 10
 9    # 判断是否为水仙花数
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 = 0
 7    temp = i
 8    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 = 0
 7    # 将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] * 10
  7    # 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] = k
 25                    break
 26
 27    # 检查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 False
 33        return True
 34
 35    # 统计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] * 10
 40        # 依次遍历每个位上的数,并用bit_result列表保存每个数位上的数字的出现次数。
 41        for i in str(value):
 42            bit_result[int(i)] += 1
 43        # 假如最后返回[0, 2, 2, 0, 0, 0, 0, 0, 0, 0]
 44        # 那代表该数中1出现2次、2出现2次
 45        return bit_result
 46
 47    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 True
 52        max = sum + cur_big
 53        # 去掉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 // 10
 59            sum = sum // 10
 60        # max,sum头部没有相同部分。
 61        if max == 0:
 62            return True
 63        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 False
 68        # 判断bit_result中小于cur_index的数(从9到0还没有判断的数)的数量是否大于remain_num
 69        for i in range(cur_index + 1):
 70            remain_num -= bit_result[i]
 71         # 小于remain_num,属正常,返回True。
 72        return remain_num >= 0
 73
 74    def search_num(cur_index, sum, remain_num):
 75        # 如果sum已经大于max_num最大值,说明已经不可能了,直接返回
 76        if sum > max_num:
 77            return
 78        # 如果sum加上cur_index的num次方remain_num倍,依然小于min_num最小值
 79        # 说明已经不可能了,直接返回
 80        if (sum + pre_table1[cur_index][remain_num]) < min_num:
 81            return
 82
 83        # 如果不符合预检查,直接跳过
 84        if not pre_check(cur_index, sum, remain_num):
 85            return
 86
 87        if remain_num == 0:
 88            # 如果检查sum符合自幂数特征,将该数添加到results列表中
 89            if sum > min_num and check_narcissus(sum):
 90                results.append(sum)
 91            return
 92
 93        if cur_index == 0:
 94            selected[0] = remain_num
 95            # 递归调用search_num
 96            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即可。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Python经典练习题——求水仙花数 的相关文章

  • 【论文笔记_目标检测_2022】Cross Domain Object Detection by Target-Perceived Dual Branch Distillation

    基于目标感知双分支提取的跨域目标检测 摘要 在野外 跨领域目标检测是一项现实而具有挑战性的任务 由于数据分布的巨大变化和目标域中缺乏实例级注释 它的性能会下降 现有的方法主要关注这两个困难中的任何一个 即使它们在跨域对象检测中紧密耦合 为了
  • arcgis不闭合线转面_ArcGIS不闭合线转面

    ArcGIS不闭合线转面 1 打开ArcMap用Add Data加载shp Polyline线文件 2 选Editor编辑 Start Editing开始编辑 3 选Editor编辑 More Editing Tools Topology拓
  • java:hashMap: get(null)引发的对其数据结构具体形态的思考

    ref 原文 https blog csdn net fenglongmiao article details 79656198 note 我们知道HashMap集合是允许存放null值的 hashMap是根据key的hashCode来寻找
  • 软工导论知识框架(五)面向对象方法学

    传统软件工程方法学适用于中小型软件产品开发 面向对象软件工程方法学适用于大型软件产品开发 一 四要素 对象 类 继承 传递消息实现通信 二 概念 1 对象 具有相同状态的一组操作的集合 对状态和操作的封装 2 类 对具有相同状态和相同操作的
  • 带你手写基于 Spring 的可插拔式 RPC 框架(三)通信协议模块

    在写代码之前我们先要想清楚几个问题 我们的框架到底要实现什么功能 我们要实现一个远程调用的 RPC 协议 最终实现效果是什么样的 我们能像调用本地服务一样调用远程的服务 怎样实现上面的效果 前面几章已经给大家说了 使用动态代理 在客户端生成
  • CentOS7中安装mysql

    1 确保本机的mysql已经卸载干净 需要将mariadb和mysql全部卸载 rpm qa grep i mariadb rpm qa grep i mysql 使用rpm ev nodeps 命令将查询出来的文件逐一卸载 sudo rp
  • Docker Compose、Docker Swarm (docker进阶 狂神)

    文章目录 Docker Compose 安装 开源项目 博客 实战 自己编写微服务上线 Docker Swarm 四台机器安装docker环境 Swarm集群搭建 Raft协议 体会 灰度发布 金丝雀发布 其他命令学习方式 Docker C
  • notepad++以16进制查看文件

    1 Notepad 可以编辑PE文件 二进制文件即HEX码 2进制 16进制都可以 通过附加的组件HexEditor即可实现 另外一款Notepad 自带插件TextFX也有这个功能 但实现效果不如Hex Editor 下载地址 https
  • CH1-绪论

    文章目录 算法时间复杂度的计算 一 冒泡排序简介 从小到大排序 算法时间复杂度的计算 我们一般只关心随着问题规模n趋于无穷时 函数中对函数结果影响最大的项 比如说 T n 3n 3 当n非常大的时候 常数3和n的系数3对函数结果的影响就很小
  • vue--综合组件间的通信

    二 综合组件之间的通信 实现一个ToDoList 完成所有的组件的创建和使用 add点击add按钮时候 将用户输入的内容 todoinput 显示在 todolist 核心代码 兄弟组件间通信 步骤1 var bus new Vue 步骤2
  • QT210烧写UBOOT到SD卡原理以及UBOOT启动

    原文地址 http blog csdn net shushi0123 article details 8018998 世界早已进入cortex a8了 我也得跟进一下所以买了QT210的开发板 长话短说开始搞SD卡烧写UBOOT 从SD启动
  • TCP选项之SO_LINGER

    SO LINGER这个选项在我以前带队改造haproxy的时候引出过一个reset RST 客户端连接的bug SO LINGER作用 设置函数close 关闭TCP连接时的行为 缺省close 的行为是 如果有数据残留在socket发送缓
  • 手动实现 call、apply、bind

    手动实现 call apply bind 改变 this的指向 就是将函数fn放入传入的context中 然后执行context fn 此时的fn中的this就变成了context 在函数执行完毕之后 需删除context中的fn call
  • 腾讯云2核4G服务器性能如何?能安装几个网站?

    腾讯云2核4G服务器能安装多少个网站 2核4g配置能承载多少个网站 一台2核4G服务器可以安装多少个网站 阿腾云2核4G5M带宽服务器目前安装了14个网站 从技术角度是没有限制的 只要云服务器性能够用 想安装几个网站就安装几个网站 但是从公
  • Windows系统下安装Ubuntu子系统

    总共分三步 1 网上是有Windows 10版本的安装教程 链接如下 14条消息 Windows系统中安装ubutu子系统 惜洛 Jankin的博客 CSDN博客 2 补充Windows 11版本的安装 大同小异 3 如果出现报错 14条消

随机推荐

  • linux非阻塞socket教程

    本文并非解释什么是非阻塞socket 也不是介绍socket API的用法 取而代替的是让你感受实际工作中的代码编写 虽然很简陋 但你可以通过man手册与其它资源非富你的代码 请注意本教程所说的主题 如果细说 内容可以达到一本书内容 你会发
  • csgo 直连服务器,csgo你只可以从大厅连接此服务器解决办法

    csgo你只可以从大厅连接此服务器解决办法 请完成以下步骤刷新您的 Steam 设置 您注销并完全退出 Steam 请打开 Internet Explore Safari 或 Firefox 和并在网址列中输入 Steam flushcon
  • 高斯模糊处理

    借鉴 http blog sina com cn s blog 861912cd0101957x html http www ruanyifeng com blog 2012 11 gaussian blur html 二维高斯曲面的公式
  • input js number 整数_JS通过正则限制 input 输入框只能输入整数、小数(金额或者现金) 两位小数...

    第一 限制只能是整数 如果不是整数就直接alert 第二 限制是两位的小数 原理 通过 正则表达式判断 不满足 执行alert 第一个正则表达式是 d 表示可以是一个或者多个数字 第二个正则表达式是 d d 0 2 表示必须是数字开头 数字
  • 别踩雷了!交互设计必须遵守这10大规范!

    UI 设计师需要理解交互设计 因为不懂交互的 UI 设计师不能成为优秀的 UI 设计师 交互设计涉及用户与产品及其使用的服务之间的关系 而 UI 设计不仅仅是将功能需求可视化 还需要创造卓越的用户体验 因此 大多数 UI 设计师需要了解交互
  • 第二十一节:JS中的继承

    上节回顾 1 所有 函数 都有一个特殊属性 prototype prototype指向一个对象 称之为原型对象 原型对象上只有一个属性 constructor constructor又指向了构造函数 形成了一个闭环 2 所有 对象 都有一个
  • C++学习(四六九)LRU Least Recently Used算法

    LRU是Least Recently Used的缩写 即最近最少使用 最近一段时间最少使用 是一种常用的页面置换算法 选择最近最久未使用的页面予以淘汰 该算法赋予每个页面一个访问字段 用来记录一个页面自上次被访问以来所经历的时间 t 当须淘
  • python解释器多版本安装

    文章目录 1 python解释器的安装 2 配置环境变量 3 在cmd窗口使用python多版本 1 python解释器的安装 要想让计算机能够识别并运行高级语言 要对应类型的翻译官 python这种编程语言的翻译官就是python解释器
  • 网页设计手绘板绘画板,适合初学者学习使用,HTML

    作品如下动态图 下载链接在文末 点我免费下载资源 资源下载链接 https download csdn net download weixin 43474701 34854658
  • Linux系统管理

    磁盘管理 磁盘基本概述 Linux中磁盘的命名方式与磁盘的接口有关 规则如下 传统IDE接口硬盘 dev hd a z SCISI接口硬盘 dev sd a z 虚拟化硬盘 dev vd a z 在设备名称的定义规则如下 其他分区可以以此类
  • MongoDB安装(win)Redis安装

    下载MongoDB 全MonogoDB链接 win安装 进入e盘 找到安装好的文件路径 以E 盘为例 在bin目录同级下创建一个文件夹 data 在data里面创建一个db和logs文件夹 进入logs创建一个文本文档 monogo log
  • 为分布式做准备吧——深入理解JVM

    文章目录 类加载机制 类执行机制 字节码解释执行 运行时 编译执行 反射执行 内存回收 内存空间 收集器 Sun JDK可用的GC 之前我们文章提到过 反射 说的比较浅显 我们这里来理解JVM 一个标准的JVM是这样的 JVM负责装载cla
  • 关于 剪映电脑版无法打开的问题!

    剪映专业版 安装到电脑上使用几次后 突然就打不开了 经过几天的漫长查找网上也无一个答案 说什么字体冲突的 都不是病根 这个bug病根是业务层加载不到veCreator dll 代码里尝试去加载veCreator dll dll 导致异常 下
  • 使用OSWatcher来监控服务器

    OSWatcher是oracle提供的监控服务器资源的工具 配合AWR等工具为调优数据库提供基本信息 OSWatcher有支持不同平台 WINDOWS平台下 OSWatcher For Windows OSWFW LINUX平台 OS Wa
  • RGMII信号是什么样子的----大揭秘

    RGMII信号 测试 1 测试RGMII 先判断RGMII信号频率多少 就知道是千兆百兆的模式 发送时钟信号 速率为Gbit s时 时钟速率为125MHz 速率为100Mbit s时 速率为25MHz 速率为10Mbit s时 速率为2 5
  • java自动化测试语言基础之方法

    java自动化测试语言基础之方法 文章目录 java自动化测试语言基础之方法 Java 方法 Java 方法 在前面几个章节中我们经常使用到 System out println 那么它是什么呢 println 是一个方法 System 是
  • Linux网络通信----htonl()、htons()、ntohl()、ntohs()四个函数

    转载 https blog csdn net miao19920101 article details 69398158 前言 今天在工作中用到htonl 这个函数 不是很理解 查阅资料之后随笔就记录下来 方便以后工作和学习翻阅 首先需要说
  • python反复运行清空plot图_仅清除matplotlib图的一部分

    我正在使用嵌入在Wx Python GUI中的matplotlib图来呈现一些数据 图中的内容 显示的数据 随点击按钮的功能不断变化 数据有两种类型 1 轮廓线 self axes contour x scale map y scale m
  • 并发锁的学习

    锁 锁的定义 锁是用来协调多个线程并发访问同一共享资源时带来的安全问题 频繁用锁必然会带来性能问题 但不用锁又会造成安全问题 1 从性能上分 乐观锁和悲观锁 乐观锁 CAS自旋锁 是非常经典的乐观锁 并发性能比较好 但是自旋会造成很大的开销
  • Python经典练习题——求水仙花数

    严格来说 我并不知道何谓 水仙花数 因为以前读书时根本没听过这种数 也不知道这种数有什么特征 后来从事编程之后反而听说了所谓的 水仙花数 如果通过网络查询 则发现水仙花数的定义也不统一 比如通过baidu百科查到如下定义 水仙花数 Narc