第四章、亲和数问题--求解500万以内的亲和数

第一节、亲和数问题
题目描述:求500万以内的所有亲和数

如果两个数a和b,a的所有真因数之和等于b,b的所有真因数之和等于a,则称a,b是一对亲和数。例如220和284,1184和1210,2620和2924。

分析:
首先得明确到底是什么是亲和数?

亲和数问题最早是由毕达哥拉斯学派发现和研究的。他们在研究数字的规律的时候发现有以下性质特点的两个数:
220的真因子是:1、2、4、5、10、11、20、22、44、55、110;
284的真因子是:1、2、4、71、142。
而这两个数恰恰等于对方的真因子各自加起来的和(sum[i]表示数i 的各个真因子的和),即
220=1+2+4+71+142=sum[284],
284=1+2+4+5+10+11+20+22+44+55+110=sum[220]。
得284的真因子之和sum[284]=220,且220的真因子之和sum[220]=284,即有sum[220]=sum[sum[284]]=284。

第二节、程序的构造与解释
    我再来具体解释下上述程序的原理,举个例子,假设是求10以内的亲和数,求解步骤如下:因为所有数的真因数都包含1,所以,先在各个数的下方全部置1

  1. 然后取i=2,3,4,5(i<=10/2),j依次对应的位置为j=(4、6、8、10),(6、9),(8),(10)各数所对应的位置。
  2. 依据j所找到的位置,在j所指的各个数的下面加上各个真因子i(i=2、3、4、5)。
    整个过程,即如下图所示(如sum[6]=1+2+3=6,sum[10]=1+2+5=8.):
    1  2  3  4  5  6  7  8  9  10
    1  1  1  1  1  1  1  1  1  1
               2      2      2      2
                       3          3 
                               4
                                       5
  3. 然后一次遍历i从220开始到5000000,i每遍历一个数后,
    将i对应的数下面的各个真因子加起来得到一个和sum[i],如果这个和sum[i]==某个i’,且sum[i‘]=i,
    那么这两个数i和i’,即为一对亲和数。
  4. i=2;sum[4]+=2,sum[6]+=2,sum[8]+=2,sum[10]+=2,sum[12]+=2...
    i=3,sum[6]+=3,sum[9]+=3...
    ......
  5. i=220时,sum[220]=284,i=284时,sum[284]=220;即sum[220]=sum[sum[284]]=284,
    得出220与284是一对亲和数。所以,最终输出220、284,...
    代码如下:
    #include<iostream>
    #define SIZE 50000
    
    using namespace std;
    
    int main() 
    {
        int i, j;
    	int *sum = new int[SIZE + 10];
    	for (i = 0; i <=SIZE ; i++) 
    		sum[i] = 1;  //1是所有数的真因数所以全部置1
    	
    	for (i = 2; i + i <= SIZE; i++)		//预处理,预处理是logN(调和级数)*N。调和级数1/2 + 1/3 + 1/4......的和近似为ln(n),
    										//因此O(n *(1/2 + 1/3 + 1/4......)) = O(n * ln(n)) = O(N*log(N))。
    	{  
            j = i + i;  //因为真因数,所以不能算本身,所以从它的2倍开始
            while (j <= SIZE) 
    		{  
    			//将所有i的倍数的位置上加i
                sum[j] += i;  
                j += i;     
            }
        }
    	
    	for (i = 220; i <= SIZE; i++)   //扫描,O(N)。
    	{
    		// 一次遍历,因为知道最小是220和284因此从220开始
            if (sum[i] > i && sum[i] <= SIZE && sum[sum[i]] == i)
    		{
    			//去重,不越界,满足亲和
    			cout << i << " " << sum[i] << endl;
            }
        }
    	delete []sum;
    	return 0;
    }
    
    
    
    
    
    
    
    
第三节、运用上述的方法查找素数

代码如下:

#include<stdio.h>
#define SIZE 100

int main() 
{
    int i, j;
	int *sum = new int[SIZE + 10];
	for (i = 0; i <= SIZE; i++) 
		sum[i] = 1; 
	
	for (i = 2; i + i <= SIZE; i++)  //预处理,预处理是logN(调和级数)*N。
		//@litaoye:调和级数1/2 + 1/3 + 1/4......的和近似为ln(n),
		//因此O(n *(1/2 + 1/3 + 1/4......)) = O(n * ln(n)) = O(N*log(N))。
	{  
        j = i + i;  //因为真因数,所以不能算本身,所以从它的2倍开始
        while (j <= SIZE) 
		{  
            sum[j] = 0;  
            j += i;     
        }
    }
	
	for (i = 2; i <= SIZE; i++)   //扫描,O(N)。
	{
		if(sum[i] != 0)
		{
			printf("%d\n",i);
		}
    }
	delete []sum;
	return 0;
}



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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值