零基础学算法第六章

6.1有趣的整数

6.1.1完数

完全数(Perfect number),又称完美数完备数,是一些特殊的自然数。它所有的真因子(即除了自身以外的约数)的和(即因子函数),恰好等于它本身。如果一个数恰好等于它的因子之和,则称该数为“完全数”。第一个完全数是6,第二个完全数是28,第三个完全数是496,后面的完全数还有8128、33550336等等。

简言之,如果一个数恰好等于其因子之和,这个数就称为完数。

求出10000以内的完数的C语言代码

#include<stdio.h>
int main()
{
	long p[300];  //保存分解的因子
	long i, num, count, s, c = 0;
	for (num = 2; num < 10000; num++)
	{
		count = 0;
		s = num;
		for (i = 1; i < num / 2 + 1; i++)   //循环处理每一个数
		{
			if (num % i == 0)   //能被i整除 
			{
				p[count++] = i; //保存因子,计数器count增加1
				s -= i;  //减去一个因子
			}
		}
		if (s == 0)    //已被分解完成,则输出
		{
			printf("%4ld是一个完数,因子是",num);
			printf("%ld=%ld",num,p[0]);  //输出完数
			for (i = 1; i < count; i++)  //输出因子
				printf("+%ld",p[i]);
			printf("\n");
			c++;
		}
	}

	printf("\n共找到%d个完数.\n",c);
	getchar();
	return 0;
}

6.1.2亲密数

如果a的所有正因子和等于b,b的所有正因子和等于a,因子包括1但不包括本身,且a不等于b,则称a,b为亲密数对。一般通过叠代编程求出相应的亲密数对。

求出10000以内的亲密数

#include<stdio.h>
int main()
{
	int i, a, b1, b2, m, g1[100], g2[100], count;
	printf("输入最大范围:");
	scanf("%d",&m);
	for (a = 1; a < m; a++)    //循环次数
	{
		for (i = 0; i < 100; i++)    //清空数组
			g1[i] = g2[i] = 0;
		count = 0; //数组下标
		b1 = 0;  //累加和
		for (i = 1; i < a / 2 + 1; i++) //求数a的因子
		{
			if (a%i == 0)   //a能被i整除
			{
				g1[count++] = i;  //保存因子到数组,方便输出
				b1 += i;  //累加因子之和
			}
		}
		count = 0;
		b2 = 0;
		for (i = 1; i < b1 / 2 + 1; i++) //将数a因子之和再进行因子分解
		{
			if (b1%i == 0) //b1能被i整除
			{
				g2[count++] = i;  //保存因子到数组g2
				b2 += i;//累加因子之和
			}
		}
		if (b2 == a&&a < b1)   //判断A,B的输出条件,a<b1避免重复输出
		{
			printf("\n\n%d--%d是亲密数,各因子为:",a,b1);  //输出亲密数
			printf("\n%d=1",a);
			count = 1;
			while (g1[count]>0)  //大于0表示有因子,输出一个数的因子
			{
				printf("+%d",g1[count]);
				count++;
			}
			printf("\n%d=1",b1);
			count = 1;
			while (g2[count]>0)  //输出另一个数的因子
			{
				printf("+%d",g2[count]);
				count++;
			}
		}
	}
	printf("\n");
	getchar();
	return 0;
}

6.1.3水仙花数

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

求水仙花数

#include<stdio.h>
int main()
{
	int i, j, k, c;
	printf("100-999之间的水仙花数:");
	for (c = 100; c < 999; c++)
	{
		i = c / 100;  //分解百位数
		j = (c - i * 100) / 10;  //分解十位数
		k = c % 10;//分解分位数
		if (i*i*i + j*j*j + k*k*k == c)
		{
			printf("%d ",c);
		}
	}
	getchar();
	return 0;
}

6.1.4自守数

该博客内容较好理解:https://blog.csdn.net/hanshileiai/article/details/8877890

自守数是指某个数的平方的末尾几位数等于这个数的数。代码如下:

#include<stdio.h>
int main()
{
	long faciend, num, mod, n_mod, p_mod;//mod被乘数的系数,n_mod乘数的系数,p_mod部分乘积的系数
	long i, t, n;//临时变量
	scanf("%ld",&num);
	printf("1-%ld之间有以下自守数:\n",num);
	for (i = 1; i < num; i++)
	{
		faciend = i;//被乘数
		mod = 1;
		do {
			mod *= 10;//被乘数的系数
			faciend /= 10;
		} while (faciend>0);    //循环求出被乘数的系数
		p_mod = mod;//p_mod为截取部分积时的系数
		faciend = 0;//积的最后N位
		n_mod = 10;//11为截取乘数相应位时的系数
		while (mod>0)
		{
			t = i % (mod * 10);//获取被乘数
			n = i%n_mod - i % (n_mod / 10);//分解出每一位乘数作为乘数
			t = t*n;//相乘的结果
			faciend = (faciend + t) % p_mod;//截取乘积的后面几位
			mod /= 10;//调整被乘数的系数
			n_mod *= 10;  //调整乘数的系数
		}
		if (i == faciend)  //若为自守数,则输出
			printf("%ld ",i);
	}
	getchar();
	return 0;
}

6.1.5最大公约数和最小公倍数

详见:https://blog.csdn.net/Holmofy/article/details/76401074(转载)

欧几里得算法(辗转相除法)

缺陷:欧几里德算法是计算两个数最大公约数的传统算法,无论从理论还是从实际效率上都是很好的。但是却有一个致命的缺陷,这个缺陷在素数比较小的时候一般是感觉不到的,只有在大素数时才会显现出来:一般实际应用中的整数很少会超过64位(当然现在已经允许128位了),对于这样的整数,计算两个数之间的模是很简单的。对于字长为32位的平台,计算两个不超过32位的整数的模,只需要一个指令周期,而计算64位以下的整数模,也不过几个周期而已。但是对于更大的素数,这样的计算过程就不得不由用户来设计,为了计算两个超过64位的整数的模,用户也许不得不采用类似于多位数除法手算过程中的试商法,这个过程不但复杂,而且消耗了很多CPU时间。对于现代密码算法,要求计算128位以上的素数的情况比比皆是,比如说RSA加密算法至少要求500bit密钥长度,设计这样的程序迫切希望能够抛弃除法和取模。

#include<stdio.h>
int main()
{	
	//函数声明
	int gcd(int, int);
	int lcm(int, int);
	int a, b;
	printf("输入两个整数(用空格分隔):");
	scanf("%d%d",&a,&b);
	printf("最大公约数:%d\n",gcd(a,b));
	printf("最小公倍数:%d\n",lcm(a,b));
	getchar();
	return 0;
}

int gcd(int a, int b)
{
	int m, n, r;
	m = a >= b ? a : b;//m保存较大数
	n = a < b ? a : b;//n保存较小数
	r = m%n;  //求余数
	while (r != 0)   //辗转相除
	{
		m = n;
		n = r;
		r = m%n;
	}
	return n; //返回最大公约数
}
int lcm(int a, int b)  //最小公倍数
{
	int t = gcd(a, b);  //获取最大公约数
	return (a*b) / t;//返回最小公倍数
}

改进:stein算法

#include<stdio.h>
int gcd(int a, int b)  //最大公约数
{
	int m, n, r;  
	m = a >= b ? a : b;   //m保存最大数
	n = a < b ? a : b;//n保存较小数
	if (n == 0)  //弱较小数为0
		return m;   //返回另一个数为最大公约数
	if (m % 2 == 0 && n % 2 == 0)  //m,n都是偶数
		return 2 * gcd(m / 2, n / 2); //递归调用gcd函数,将m,n都除以2
	if (m % 2 == 0)//m为偶数
		return gcd(m / 2, n); //递归调用gcd函数,将m除以2
	if (n % 2 == 0)//n为偶数
		return gcd(m, n / 2);//递归调用gcd函数,将n除以2
	return gcd((m+n)/2,(m-n)/2);  //m,n都是奇数,递归调用gcd
}

int lcm(int a, int b)//最小公倍数
{
	int t = gcd(a, b);//获取最大公约数
	return (a*b) / t;
}

int main()
{
	int a, b;
	printf("输入两个整数(用空格分隔):");
	scanf("%d%d", &a, &b);
	printf("最大公约数:%d\n", gcd(a, b));
	printf("最小公倍数:%d\n", lcm(a, b));
	getchar();
	return 0;
}

6.3阶乘

6.3.1用递归方法求阶乘

#include<stdio.h>
unsigned long factorial(unsigned long n) 
{
	if (n < 0)
		return 1;
	if (n == 1)
		return 1;
	else
		return (unsigned long)n*factorial(n - 1);
}


int main()
{
	unsigned long n;
	printf("输入一个整数n(n>0):");
	scanf("%d",&n);
	printf("%lu!=%lu\n",n,factorial(n));
	getchar();
	return 0;
}

6.3.2大数阶乘

发布了33 篇原创文章 · 获赞 1 · 访问量 5万+

猜你喜欢

转载自blog.csdn.net/futurech/article/details/88607505