第39课 问题求解——求素数 【数字游戏 项目1-6】

第39课 问题求解——求素数




项目一【完数】

一个数如果恰好等于它的因子之和,这个数就称为“完数”。例如6=1+2+3,再如8的因子和是7(即1+2+4),8不是完数。
(1)输入一个数n,判断n是否是完数
(2)输出1000以内的所有完数
(3)亲密数:如果整数A的全部因子(包括1,不包括A本身)之和等于B;且整数B的全部因子(包括1,不包括B本身)之和等于A,则将整数A和B称为亲密数。求3000以内的全部亲密数。(提示:按照亲密数定义,要判断数a是否有亲密数,只要计算出a的全部因子的累加和为b,再计算b的全部因子的累加和为n,若n等于a则可判定a和b是亲密数。)

(1)代码:

自己摸索的做的,可循环输入,效率较低

/*
39课-循环-求完数-项目1.1
2015.12.25
BenjaVan
*/
#include<stdio.h>
int main()
{
    int i,num,sum;
    char c;
    printf("   * 完数计算 *\n");
    do//可循环使用计算程序
    {
        i=1;   //赋初值
        sum=0;   //赋初值
        printf("请输入数字:");
        scanf("%d",&num);
        printf("因子:");
        while(i<=(num-1))   //循环计算完数的因子
        {
            if(num%i==0)   //如果余数为零,则i是其因子
            {
                printf("%d ",i);
                sum=sum+i;   //把因子累加
            }
            i++;
        }
        printf("  和:%d\n",sum);
        if(sum==num)   //分支显示此数是否为完数
            printf("%d是完数!\n",num);
        else
            printf("%d不是完数!\n",num);
        printf("* 按N键退出程序,任意键继续 * ");   //退出程序的出口
        fflush(stdin);   //清空缓冲区
        scanf("%c",&c);
        if(c=='n'||c=='N')
            break;
        printf("\n");
    }
    while(1);   //可以再次输入数字,计算是否为完数的大循环
    printf("\n--程序已结束--\n");
    return 0;
}

运行结果:


做了一遍老师的代码:
学习其中更效率的算法

#include<stdio.h>
#include<math.h>
int main()
{
    int i,sum=1,num;
    printf("请输入:");
    scanf("%d",&num);
    for(i=2;i<sqrt(num);i++)
        if(num%i==0)
            sum+=(i+num/i);
    if(i*i==num)
        num+=i;
    printf("因子和为:%d\n",sum);
    if(num==sum)
        printf("%d是完数\n",num);
    else
        printf("%d不是完数\n",num);
    return 0;
}

(2)代码:

/*
39课-循环-求完数-项目1.2
2015.12.25
BenjaVan
*/
#include<stdio.h>
#include<math.h>
int main()
{
    int num,j,sum,n=1000;
    printf("1-%d之间的完数有:\n",n);
    for(num=2;num<=n;num++)
    {
        sum=1;
        j=2;
        do
        {
            if(num%j==0)
                sum+=(j+num/j);
            j++;
        }
        while(j<sqrt(num));
        if(j*j==num)
            sum=sum+j;
        if(sum==num)
            printf("%d ",num);
    }
    printf("\n");
    return 0;
}

运行结果:



(3)代码:

#include<stdio.h>
int main()
{
    int i,a,b,n;
    for(a=1;a<=3000;a++)
    {
        b=0;
        n=0;
        for(i=1;i<=(a-1);i++)
            if(a%i==0)
                b=b+i;
        for(i=1;i<=(b-1);i++)
            if(b%i==0)
                n=n+i;
        if(n==a&&a!=b)
            printf("%d-%d\n",a,b);
    }
    return 0;
}

运行结果:




————————————————————————————————————————————————————————————————————


项目二【 n=a!+b!+c!】

求满足条件n=a!+b!+c!的所有三位数n并输出,其中a,b,c分别为n的百、十、个位数。

代码:

  这个题因为考虑了一些效率方面的问题,人为在程序上做了改变,但算法变复杂了,出错的可能性更大了。 — —任何的工程,遵循“简单即是美”的法则
在检测程序运行没问题后,将它记录了下来
#include<stdio.h>
int main()
{
    int i,a1,a2,b1,b2,c1,c2,num;
    for(num=100;num<556;num++)   //556以上没有计算意义
    {
        a2=1;
        b2=1;   //不受0的影响,0的阶乘为1
        c2=1;
        c1=num%10;
        b1=(num/10)%10;
        a1=(num/100)%10;
        if(b1>=6||c1>=6)   //大于等于6时跳过
        {
            num+=3;
            continue;
        }
        for(i=1;i<=a1;i++)   //计算阶乘
            a2*=i;
        for(i=1;i<=b1;i++)
            b2*=i;
        for(i=1;i<=c1;i++)
            c2*=i;
        if((a2+b2+c2)==num)
            printf("%d\n",num);
    }
    return 0;
}

运行结果:




————————————————————————————————————————————————————————————————————


项目三【反序数】

(1)输入一个正整数,输出它的反序数(反序数,即将其所有位的数字反过来。例如,123是321的反序数)
(2)求1000000以内的正整数n,要求9n是n的反序数。

  做这道题时真的写了好久,从一开始看到题目时感觉挺简单,跟着脑子里的思路走,但发现所学到的知识完全不知道怎么把一个随机数字的位数解决,差点就放弃了,试了很多次之后才走向了 /10 的方向,我先计算了数字的位数,然后分离各个数,再根据位数累加成反序数,过程太昂乱,必然是比不上老师的参考答案,不过能通过自己努力做出来确实很开心了,我想价值在这个坚持求解的过程,好过直接学一个解决方案,所以把这个笨解保留了下来,留念一下: )

(1)代码:

#include<stdio.h>
int main()
{
    int i=1,j,n,t,k,num,num2,a,sum=0;
    scanf("%d",&num);
    num2=num;
    for(i=0;num2!=0;i++)
        num2=num2/10;
    j=1;
    k=i;
    while(j<=i)
    {
        t=1;
        a=num%10;
        for(n=1;n<=(k-1);n++)
            t=t*10;
        sum=sum+a*t;
        num=(num-a)/10;
        j++;
        k--;
    }
    printf("%d",sum);
    return 0;
}

运行结果:



学习老师的算法:
真的太巧妙了,赞啊

#include<stdio.h>
int main()
{
    int i=1,num1,num2=0;
    scanf("%d",&num1);
    i=num1;
    while(i!=0)
    {
        num2=num2*10+i%10;  //新数通过乘10来让位,加上原来数的个位数,循环做这个
        i=i/10;  //以原来数的位数确定循环的次数,并缩小位数
    }
    printf("%d\n",num2);
    return 0;
}


(2)代码:

#include <stdio.h>
int main()
{
    int n,m,k;
    for(n=1; n<1000000; n++)
    {
        k=n;
        m=0;  //m将为n的反序数
        while(k>0)
        {
            m=m*10+k%10;
            k=k/10;
        }
        if(m==9*n)   //n的反序数m等于9n
            printf("%d--%d\n",n,m);
    }
    printf("\n");
    return 0;
}

运行结果:




————————————————————————————————————————————————————————————————————


项目四【回文数】

(1)输入一个正整数,判断其是否为一个回文数(例1221、12321都是回文数,正着看、倒着看,是同一个数)。
(2)输出10000以内的所有回文数。

(1)有了前面的基础,可以在此基础上来解题

代码:

#include<stdio.h>
int main()
{
    int i,num1,num2;
    char c;
    do
    {
        printf("请输入:");
        scanf("%d",&num1);
        i=num1;
        num2=0;
        while(i!=0)
        {
            num2=num2*10+i%10;
            i=i/10;
        }
        if(num2==num1)
            printf("  是回文数");
        else
            printf("  不是哦");
        printf("(按N键退出,其他键退出) ");
        fflush(stdin);
        c=getchar();
        if(c=='N'||c=='n')
        {
            printf("\n已退出\n");
            break;
        }
        printf("\n");
    }
    while(1);
    return 0;
}

运行结果:


(2)一共两种方法,一种用反序数的原理,一种直接组成符合回文的数然后输出

代码1:

#include<stdio.h>
int main()
{
    int i,k=1,num1,num2;
    for(num1=1;num1<=10000;num1++)
    {
        num2=0;
        i=num1;
        while(i!=0)//计算原数的反序数
        {
            num2=num2*10+i%10;
            i=i/10;
        }
        if(num1==num2)//原数与其反序数相等则是回文数
        {
            printf("%d  ",num2);
            if(k%10==0)//10个一行:)
                printf("\n");
            k++;
        }
    }
    printf("\n");
    return 0;
}

运行结果:



代码2:

#include<stdio.h>
int main()
{
    int m,n;
    for(m=1;m<=9;++m)//一位数的
        printf("%d ",m);
    printf("\n");//十个换行
    for(m=1;m<=9;++m)//两位数的
        printf("%d ",m*10+m);
    printf("\n");//十个换行
    for(m=1;m<=9;++m)//三位数
    {
        for(n=0;n<=9;++n)
            printf("%d ",m*100+n*10+m);
        printf("\n");//十个换行
    }
    for(m=1;m<=9;++m)//四位数
    {
        for(n=0;n<=9;++n)
            printf("%d ",m*1000+m+n*100+n*10);
        printf("\n");//十个换行
    }
    return 0;
}

运行结果:



————————————————————————————————————————————————————————————————————


项目五【阿姆斯特朗数】

如果一个正整数等于其各个数字的立方和,则称该数为阿姆斯特朗数(亦称为自恋性数)。如 407=43+03+73就是一个阿姆斯特朗数。试编程求1000以内的所有阿姆斯特朗数。

代码:

#include<stdio.h>
int main()
{
    int num,a,b,c;
    printf("1000内的阿姆斯特朗数:\n");
    for(num=1;num<=955;++num)//996的立方和已经大于1000,不再计算了
    {
        c=num%10;
        b=(num/10)%10;
        a=num/100;
        a=a*a*a;//简单的求立方
        b=b*b*b;
        c=c*c*c;
        if(num==(a+b+c))
            printf("%d ",num);
    }
    printf("\n");
    return 0;
}

运行结果:




————————————————————————————————————————————————————————————————————


项目六【回文日】

很有趣的一个题目:2011年11月02日是一个回文日:2011 1102,在2011级同学做这道题时我们刚刚度过这一天!请列出本世纪还有多少个回文日(假如我们能活到百岁,你和我的……)。注意:一年只有12个月。

代码1:

   因为问题所求的是本世纪,所以2000年的回文是0002(2100年的回文数是0012,没有0月这一说,所以只算到2090年就行),也就是说,只有每年每月2号才可能是回文日,所以做了简单的算法,虽然不会出错,但是不严谨

#include<stdio.h>
int main()
{
    int year,moon,day,n;
    for(year=0;year<=90;++year)//90年足够
    {
        for(moon=1;moon<=12;++moon)  //每月都循环,其实只有月份才是变化回文日的关键数
        {
            day=2+moon*100;  //只有每月2日能构成回文
            n=2000+(moon%10)*10+moon/10;  //做一个反序数,加上大的年份
            if(n==(year+2000))
                printf("%d-%04d\n",year+2000,day);
        }
    }
    return 0;
}

其中可以直接去掉年份的循环,更简略的代码如下:

#include<stdio.h>
int main()
{
    int month;
        for(month=1;month<=12;++month)
            printf("%d-%04d\n",2000+(month%10)*10+month/10,2+month*100);
    return 0;
}






知识点总结

        感觉这一节的知识点大多是源自反序数算法的拓展,之外更多的是使用了很多的技巧及算法,于是决定将它们整理一下,以后遇到就记录一下。












  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值