第39节 求素数算法

一.判别m是否为素数

1.概念: 一个数m,如果仅能被1和m整除,则m为素数(质数).

2.理解: 如果m能够被2到m-1间的任意一个数整除,m就不是素数

二.朴素的算法

#include <stdio.h>
int main()
{
    
    
	int m, is_prime = 1;
	scanf_s("%d", &m);
	for (int i = 2; i <= m - 1; i++)
	{
    
    
		if (m % i == 0)
		{
    
    
			is_prime = 0;
			break;
		}
	}
	if (is_prime == 0)
	{
    
    
		printf("%d不是素数\n", m);
	}
	else printf("%d是素数\n", m);
}

三.改进减少循环次数

原理: 因子的对称性
 72 = 2 × 36
 72 = 3 × 24
 72 = 4 × 18
 72 = 6 × 12
 72 = 8 × 9
 72 = 9 × 8
 72 = 12 × 6
 72 = 18 × 4
 72 = 24 × 3
 72 = 36 × 2

#include <stdio.h>
#include <math.h>
int main()
{
    
    
	int m, k, is_prime = 1;
	scanf_s("%d", &m);
	k = sqrt(m);
	for (int i = 2; i <= k; i++)//最多循环到k
	{
    
    
		if (m % i == 0)
		{
    
    
			is_prime = 0;
			break;
		}
	}
	if (is_prime == 0)
	{
    
    
		printf("%d不是素数\n", m);
	}
	else printf("%d是素数\n", m);
}

四.找出100~200间的全部素数

#include <stdio.h>
#include <math.h>
int main()
{
    
    
	int m, k, is_prime = 1,n=0;
	for (m = 101; m <= 200; m += 2)//偶数不是素数
	{
    
    
		is_prime = 1;
		k = sqrt(m);
		for(int i=2;i<=k;i++)//最多循环到k
		if (m % i == 0)
		{
    
    
			is_prime = 0;
			break;//及时退出
		}
		if (is_prime == 1)
		{
    
    
			printf("%d\t", m);
			++n;
			if (n % 5 == 0)
				printf("\n");
		}
	}
}

五.实践项目
【项目1-完数】
一个数如果恰好等于它的因子之和,这个数就称为“完数”。例如6=1+2+3,再如8的因子和是7(即1+2+4),8不是完数。
(1)输入一个数n,判断n是否是完数

解法1:常规解法
#include <stdio.h>
int main()
{
    
    
	int n, m, s = 1;      //n为完数,m为n的因子,s为因子的和;
	printf("输入数n: ");
	scanf_s("%d", &n);
	for (m = 2; m < n; m++)
	{
    
    
		if (n % m == 0)
		{
    
    
			s += m;
		}
	}
	if (n == s)
	{
    
    
		printf("YES!\n");
		printf("%d=1",n);
		for ( m = 2; m < n; m++)
		{
    
    
			if (n % m == 0)
			{
    
    
				printf("+%d", m);
			}
		}
	}
	else
	{
    
    
		printf("NO!\n");
	}
	printf("\n");
}
解法2:利用因子对称性降低循环次数
#include <stdio.h>
#include <stdio.h>
#include <math.h>
int main()
{
    
    
	int n, m, s = 1;              //n为完数,m为n的因子,s为因子的和;
	printf("输入数n: ");
	scanf_s("%d", &n);
	for (m = 2; m < sqrt(n); m++) //利用因子的对称性
	{
    
    
		if (n % m == 0)
		{
    
    
			s += m + n / m;
		}
	}
	printf("m=%d\n", m);   //测试m值用,此时m已被for循环中m++赋值
	if (m * m == s) //若m=sqrt(n)时,s需要在加上一个m值;
	{
    
    
		s += m;
	}
	if (n == s)
	{
    
    
		printf("YES!\n");
		printf("%d=1", n);
		for (m = 2; m < sqrt(n); m++)
		{
    
    
			if (n % m == 0)
			{
    
    
				printf("+%d+%d", m, n / m);
			}
		}
		if (m * m == s) //若m=sqrt(n)时,s需要在加上一个m值;
		{
    
    
			printf("+%d", m);
		}
	}
	else
	{
    
    
		printf("NO!\n");
	}
	printf("\n");
}

(2)输出1000以内的所有完数

#include <stdio.h>
#include <math.h>
int main()
{
    
    
	int n,m, s;              //n为完数,m为n的因子,s为因子的和;
	printf("1000以内的完数: ");
	for ( n = 2; n <= 1000; n++)//1不算完数
	{
    
    
		s = 1;//每次计算s要初始化为1;
		for (m = 2; m < sqrt(n); m++) //利用因子的对称性
		{
    
    
			if (n % m == 0)
			{
    
    
				s += m + n / m;
			}
		}
		if (m * m == s) //若m=sqrt(n)时,s需要在加上一个m值;
		{
    
    
			s += m;
		}

		if (n == s)
		{
    
    
			printf("%d, ", n);
		}
	}
	printf("\n");
}
运行结果:
1000以内的完数: 6, 28, 496,

(3)亲密数:如果整数A的全部因子(包括1,不包括A本身)之和等于B;且整数B的全部因子(包括1,不包括B本身)之和等于A,则将整数A和B称为亲密数。求3000以内的全部亲密数。(提示:按照亲密数定义,要判断数a是否有亲密数,只要计算出a的全部因子的累加和为b,再计算b的全部因子的累加和为n,若n等于a则可判定a和b是亲密数。)

#include <stdio.h>
#include <math.h>
int main()
{
    
    
	int a, b, n, m;
	printf("3000以内的亲密数: \n");
	for (a = 2; a <= 3000; a++)
	{
    
    
		b = 1;
		for (m = 2; m < sqrt(a); m++)
		{
    
    
			if (a % m == 0)
			{
    
    
				b += m + a / m;
			}
		}
		if (m * m == a)
		{
    
    
			b += m;
		}

		n = 1;
		for (m = 2; m < sqrt(b); m++)
		{
    
    
			if (b % m == 0)
			{
    
    
				n += m + b / m;
			}
		}
		if (m * m == b)
		{
    
    
			n += m;
		}
		if (n == a && a != b && a < (a + b) / 2)
			printf("%d\t%d\n", a, b);
	}
}
运行结果:
3000以内的亲密数:
220     284
1184    1210
2620    2924

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

解法一:
#include <stdio.h>
int main()
{
    
    
	int a, b, c, x, i, suma, sumb, sumc;
	for (x = 100; x < 1000; x++)
	{
    
    
		a = x % 10;
		b = x / 10 % 10;
		c = x / 100;
		suma = 1;
		sumb = 1;
		sumc = 1;
		for (i = 1; i <= a; i++) //求a!
		{
    
    
			suma *= i;
		}
		for (i = 1; i <= b; i++) //求b!
		{
    
    
			sumb *= i;
		}
		for (i = 1; i <= c; i++) //求c!
		{
    
    
			sumc *= i;
		}
		if (x == suma + sumb + sumc)
		{
    
    
			printf("%d\n", x);
		}
	}
}
运行结果:
145
解法二:优化降低循环次数
#include <stdio.h>
int main()
{
    
    
	int a, b, c, x, suma, sumb, sumc; //a百位,b十位,c个位
	for (suma = 1, a = 1; a < 10; a++)
	{
    
    
		suma *= a;
		for (sumb = 1, b = 0; b < 10; b++)
		{
    
    
			if (b > 0) sumb *= b;
			for (sumc = 1, c = 0; c < 10; c++)
			{
    
    
				if (c > 0) sumc *= c;
				x = 100 * a + 10 * b + c;
				if (x == suma + sumb + sumc) printf("%d\n", x);
			}
		}
	}
}
运行结果:
145

【项目3-反序数】
(1)输入一个正整数,输出它的反序数(反序数,即将其所有位的数字反过来。例如,123是321的反序数)

#include <stdio.h>
int main()
{
    
    
	int n, m, k;
	scanf_s("%d", &n);
	m = 0;
	k = n;
	while (k > 0)
	{
    
    
		m = 10 * m + k % 10; //求末位数字,相加,乘10进位;
		k = k / 10;          //舍去末位数字,再循环;
	}
	printf("%d的反序数为%d", n, m);
}

(2)求1000000以内的正整数n,要求9n是n的反序数。

#include <stdio.h>
int main()
{
    
    
	int n, m, k;
	for (n = 1; n < 1000000; n++)
	{
    
    
		for (k = n, m = 0; k > 0; k /= 10) //求n的反序数m;
		{
    
    
			m = m * 10 + k % 10;
			if (m==9 * n) printf("%d\t%d\n", n, m);
		}
	}
}
运行结果:
1089    9801
10989   98901
109989  989901

【项目4-回文数】
(1)输入一个正整数,判断其是否为一个回文数(例1221、12321都是回文数,正着看、倒着看,是同一个数).

#include <stdio.h>
int main()
{
    
    
	int x, y, k;
	scanf_s("%d", &x);
	for (k = x, y = 0; k > 0; k /= 10)
	{
    
    
		y = y * 10 + k % 10;
	}
	if (x == y) printf("YES!\n");
	else printf("N0!\n");
}

(2)输出10000以内的所有回文数。

#include <stdio.h>
int main()
{
    
    
	int x, y, k,i=-1;
	for (x = 1; x < 10000; x++)
	{
    
    
		for (k = x, y = 0; k > 0; k /= 10)
		{
    
    
			y = y * 10 + k % 10;
		}
		if (x == y)
		{
    
    
			i++;
			if (i % 5 == 0)
			{
    
    
				printf("\n");
			}
			printf("%d\t", x);
		}	
	}
}

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

解法一:
#include <stdio.h>
int main()
{
    
    
	int x, a, b, c;
	for (a = 0; a < 10; a++)
	{
    
    
		for (b = 0; b < 10; b++)
		{
    
    
			for (c = 0; c < 10; c++)
			{
    
    
				x = a * a * a + b * b * b + c * c * c;
				if (x == 100 * a + 10 * b + c)
					printf("%d\t", x);
			}
		}
	}
}
运行结果:
153     370     371     407
解法2:
#include <stdio.h>
int main()
{
    
    
    int i,m,k,n;
    for(n=0; n<=1000; ++n)
    {
    
    
        //计算n的各位数的立方和m
        k=n;
        m=0;
        while(k>0)
        {
    
    
            i=k%10;
            m+=i*i*i;
            k=k/10;
        }
        if(m==n)  //n和其各位数的立方和相等
        {
    
    
            printf("%d\t", m);
        }
    }
    printf("\n");
}

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

#include <stdio.h>
int main()
{
    
    
	int year, month, day, y, ymd;
	int is_cycle;
	int count = 0;
	for (year = 2015; year < 2100; year++)
	{
    
    
		y = year;//保留year数值,用y去做运算;
		month = year % 100;//年份的后两位对应月;如:2015中取出15;
		month = (month % 10) * 10 + month / 10;//把得到的月两位倒过来,15变51;
		day = y / 100;//年份的前两位对应日,如2015中取出20;
		day = day % 10 * 10 + day / 10;//将得到的日两位倒过来,20变02;
		switch (month)
		{
    
    
		case 1:
		case 3:
		case 5:
		case 7:
		case 8:
		case 10:
		case 12:
			if (day <= 31) is_cycle = 1;
			break;
		case 4:
		case 6:
		case 9:
		case 11:
			if (day <= 30) is_cycle = 1;
			break;
		case 2:
			if (year % 4 == 0 && year % 100 == 0 || year % 400 == 0)
			{
    
    
				if (day <= 29)//闰年2月29天;
					is_cycle = 1;
			}
			else
			{
    
    
				if (day <= 28)//非闰年2月28天;
					is_cycle = 1;
			}
			break;
		default:
			continue;//如果你在switch中使用continue,continue生效是对于循环
			         //如果你在switch中使用break,break生效是对于switch。
		}
		if (is_cycle)
		{
    
    
			y = year;
			ymd = year;   //构造出年月日的形式供输出
			while (y > 0) //应用倒序数的方法,合成年月日输出格式;
			{
    
    
				ymd = ymd * 10 + y % 10;
				y = y / 10;
			}
			printf("%d\n", ymd);
			count++;
		}
		
	}
	printf("本世纪共有%d个回文日.\n", count);
	printf("\n");
}

猜你喜欢

转载自blog.csdn.net/m0_51439429/article/details/115364276