• “拨”取数字的典例:N位水仙花数判断及水仙花数变种


    本文以C语言实现。


    目录

    前言:再论“拨”取数位上的数

    一、引例:三位水仙花数

    1. 题干

    2. 思路

    3. 代码

    二、推广:N位水仙花数

    1. 题干

    2. 思路

    3. 代码

    说明 

    三、拓展:水仙花数的变种 -- 灵活“拨”取数字

    1. 题干

    2. 思路

    三板斧的使用

    3. 代码

     四、总结


    前言:再论“拨”取数位上的数

    在我曾经的博客中提到过,“水仙花数”类型的题目,精髓就在于“拨数”。关于“拨数”的内容,在往期的文章中已经详细讨论过,本文便不再赘述。我们将“拨数”作为一个大家已经掌握了的知识储备,再来讨论水仙花数系列的习题。

    C语言编程必会技能:“拨”取数位上的数icon-default.png?t=M666http://t.csdn.cn/W0iPx

    三位水仙花数,即其个位、十位、百位数字的立方和等于该数本身。这是我们常见的水仙花数题,它的讨论范围限定在3位数。事实上,水仙花数可以不止有3位数,但它们的运算规则是相同的:

    一个n位数,其各位数字的n次方之和恰好等于该数本身。于是就有:

    三位的水仙花数共有4个:153,370,371,407;
    四位的四叶玫瑰数共有3个:1634,8208,9474;
    五位的五角星数共有3个:54748,92727,93084;
    六位的六合数只有1个:548834;
    七位的北斗七星数共有4个:1741725,4210818,9800817,9926315;
    八位的八仙花数共有3个:24678050,24678051,88593477

    我们干脆将所有具有该特点的数成为水仙花数。

    本文讨论的重点在于,如何判断一个任意数位的水仙花数。我们再次巩固和运用“拨数”大法来解决问题,文末附上了水仙花数的变种:

    把任意的数字,从中间拆分成两个数字,如果所有拆分后的乘积之和等于该数本身。

    希望本文能让诸位读者对数位拨取类习题有更深刻的理解。 


    一、引例:三位水仙花数

    1. 题干

    输出所有水仙花数。

    水仙花数是一个3位数,各位数字的立方之和等于他本身,例如:153= 13+53+33。


    2. 思路

    实现3位水仙花数是一件很容易构思的事情。先把三位数中个位、十位、百位上的数字取下来,然后进行立方求和运算,最后判断是否与原数相等即可。

    3位水仙花数数位确定,且幂仅是三次方(幂数较少),实现起来并不复杂。大致分为两步:

    1. 取数字

    2. 计算并判断是否相等。


    3. 代码

    1. #include
    2. int main(){
    3. int flowerN,ge,shi,bai;
    4. for(flowerN = 100; flowerN < 1000; flowerN++){
    5. //拨取数位上的数
    6. ge = flowerN%10;
    7. shi = flowerN/10%10;
    8. bai = flowerN/100%10;
    9. if(flowerN == ge*ge*ge + shi*shi*shi + bai*bai*bai){
    10. printf("%d ",flowerN);
    11. }
    12. }
    13. return 0;
    14. }

    二、推广:N位水仙花数

    1. 题干

    求出0~100000之间的所有“水仙花数”并输出。

    “水仙花数”是指一个n位数,其各位数字的n次方之和确好等于该数本身,如:153=1^3+5^3+3^3,则153是一个“水仙花数”。


    2. 思路

    有些同学在这道题卡住,很大的一个原因是,当具象的“3”变成抽象的“n”时,比较难以想象该如何让代码做到普适。当感觉一团乱麻的时候,我们依旧可以考虑用上次讲到的分析问题“三板斧”(举例、模拟(必要时画图)、找规律)进行考虑。

    碳基肥宅:分析破题“三板斧”icon-default.png?t=M666http://t.csdn.cn/ykGeV

    有了计算3位水仙花数的经验(3位水仙花数正是一个特例,很好地帮助我们完成了“举例”这第一板“斧”),我们可以直接将问题切割为三个大步骤,再逐步求精:

    1. 一共有多少个数位?这决定了一共有多少个数要拨取并相加,以及相应的幂数。

    --求位数

    2.求数的n次方。

    3.累加:求每个数位上的数的n次方之和。这需要对叠加的算法熟悉。

    以上,我们将一个大问题分割成了几个小的功能模块。在编程时,我们只需要照着这些小的功能模块逐步完善求精即可。

    详细说明上述思路不是没有意义的。我们需要有意识地在编程分析问题时“慢下来”,分割大问题,完善小问题,才能保证编程的顺利。相比起拿到题目就从头开始干,想到哪里写到哪里,一运行bug肆虐,我认为这样有规划地写代码,确实是更好的选择。


    3. 代码

    1. #include
    2. #include
    3. //求出整数的位数n -- 拨取数位上的数
    4. int digits(int num){
    5. int n = 1;
    6. while(num/10){
    7. n++;
    8. num /= 10; //这一句注意不要遗漏!!否则就是死循环
    9. }
    10. return n;
    11. }
    12. //判断是不是水仙花数,是返回1,不是返回0
    13. int isNumber(int num){
    14. int n = digits(num); //调用函数,获取数位
    15. int sum = 0;
    16. int tmp = num; //把num的值先保存起来,便于后续比较
    17. while(num){
    18. sum += pow(num%10,n); //可以类比叠加的算法
    19. num /= 10;
    20. }
    21. if(sum == tmp){
    22. return 1;
    23. }else{
    24. return 0;
    25. }
    26. }
    27. int main(){
    28. for(int num = 0; num <= 100000; num++){
    29. if(isNumber(num)){
    30. printf("%d\n",num);
    31. }
    32. }
    33. return 0;
    34. }

    说明 

    1. while循环中,千万不要忘记num /= 10这一步骤。这是循环变量的调整步骤,没有这一步程序会死循环。由于while(num / 10)和num /= 10这一步有些相像,num /= 10这一步就很容易漏写。

    2. int tmp = num;这一步很关键。它是将num的值暂时保存到tmp中,最后比较时,也是将sum与tmp比。因为num同时作为循环变量,它是不断变化调整的。当while执行结束后,num早已不是我们最初输入的那个值。最后sum要和“该数本身比较”,num早已不是“该数本身”。因此我们需要再创建一个变量,拷贝出num最初的值备用。

    这一步非常隐蔽,很容易直接按照预先想好的逻辑上手而将它忽略。这样程序当然会出bug。我第一次编写代码时,正是遇到了这个问题,看了很久调试后才发现问题所在。一不小心,就花费了很多的时间。


    三、拓展:水仙花数的变种 -- 灵活“拨”取数字

    1. 题干

    牛客网习题链接

    BC38 变种水仙花icon-default.png?t=M666https://www.nowcoder.com/practice/c178e3f5cc4641dfbc8b020ae79e2b71?tpId=107&&tqId=33319&rp=1&ru=/ta/beginner-programmers&qru=/ta/beginner-programmers/question-ranking 


    2. 思路

    三板斧的使用

    举例

    这道题相比上面的题目,难度未必有所提升。我们直接以五位数12345为例,对这个情况进行模拟。

    模拟

    num:12345。以下示意程序运行时达到的效果:

    • 1234  5
      • 12345/10 * 12345%10
    • 123  45    
      • 12345/100 * 12345%100
    • 12  345
      • 12345/1000 * 12345%1000
    • 1  2345
      • 12345/10000 * 12345%10000
         

    找规律

    我们发现,同一组内,除数与模数始终相等,且从10开始变化到10000.我们将该数设定为循环变量i,i从10变化到10000,且每次增长10倍。

    而被分隔成的左右两个数,一个是num%i的结果,一个是num/i的结果。

    因此,将规律整合成为如下代码即可:

    3. 代码

    1. #include
    2. /*
    3. 12345
    4. 1234 5
    5. 12345/10 * 12345%10
    6. 123 45
    7. 12345/100 * 12345%100
    8. 12 345
    9. 12345/1000 * 12345%1000
    10. 1 2345
    11. 12345/10000 * 12345%10000
    12. */
    13. int isLily(int num){
    14. int tmp = num;
    15. int sum = 0;
    16. for(int i = 10; i <= 10000; i*=10){
    17. sum += (num/i)*(num%i);
    18. }
    19. if(sum == tmp){
    20. return 1;
    21. }
    22. return 0;
    23. }
    24. int main(){
    25. for(int i = 10000; i < 100000; i++){
    26. if(isLily(i)){
    27. printf("%d ",i);
    28. }
    29. }
    30. return 0;
    31. }

     四、总结

    1. 编写代码时可以考虑先分隔功能模块,再逐步求精。

    2. 水仙花数从3位到n位的推广:找相似。

     

  • 相关阅读:
    python基于django学生成绩管理系统o8mkp
    国科云:什么是DHCP?DHCP是怎么工作的?
    [SwiftUI 开发] @State @Binding @ObservedObject @EnvironmentObject
    配置开启Docker2375远程连接与解决Docker未授权访问漏洞
    面试题常考:LRU缓存
    【光学】基于Matlab实现二维光子晶体的能带图和场
    [附源码]java毕业设计同城搬家平台
    无重复字符的最长子串
    10月4日作业
    Qt QSVG使用详解
  • 原文地址:https://blog.csdn.net/wyd_333/article/details/126167918