求500万以内的亲和数
“朋友是你灵魂的倩影,要像220与284一样亲密。”
——毕达哥拉斯
首先需要知道什么事亲和数?亲和数是指,两个数a和b,如果a的所有真因子之和等于b,而b所有的真因子之和也等于a,那么着两个数被称为亲和数(真因子包括1,但不包括自身)。
Example:
220的真因子: 1, 2, 4, 5, 10, 11, 20, 22, 44, 55, 110
284的真因子: 1, 2, 4, 71, 142
284 = 1+2+4+5+10+11+20+22+44+55+110(220的真因子之和)
220 = 1+2+4+71+142(284的真因子之和)
解决方案:首先想到的办法,即穷举法:对于任意的两个数a和b,判断a和b是否能满足亲和数的条件。算法的时间复杂度O(N3),在数量级很大的情况下,算法的效率就显得非常低下。在穷举的时候可以做一些剪枝:从小数开始遍历,当前数a,求出其真因子之和b,然后在判断b对应的真因子之和是否等于a。然而这些类似处理办法,仅是“细节”上的优化,大方向“算法框架”不变,时间的复杂度也不会有多大的变化。
那么,如何选择优化的方向呢?判断两个数是否互为亲和数,必须要求出他们的真因子,当数量级较大时,算法的耗时体现在求这些数的真因子上。一种解决该问题的方法是,从低向上,以空间换时间(类似与动态规划算的基本思想)构建解空间:
1 #include <iostream> 2 3 using namespace std; 4 5 int main() 6 { 7 const int love_size = 5000000; 8 int *love_sum_array = new int[love_size]; 9 for (int i = 1; i < love_size; i++) //1是所有数的真因子 10 { 11 love_sum_array[i] = 1; 12 } 13 14 for (int i = 2; i + i < love_size; i++) //从2开始作为真因子项,为包含真因子项的love_sum加上该真因子项 15 { 16 int j = i + i; //自身不作为真因子项,从2倍的项开始 17 while (j < love_size) 18 { 19 love_sum_array[j] += i; 20 j += i; 21 } 22 } 23 24 for (int i = 220; i < love_size; i++) 25 { 26 //越界与亲和数判断 27 if (love_sum_array[i] < love_size && love_sum_array[love_sum_array[i]] == i) 28 { 29 cout<<i<<" "<<love_sum_array[i]<<endl; 30 } 31 } 32 33 delete[] love_sum_array; 34 system("pause"); 35 return 0; 36 }
程序的运行结果如下:
上面的代码涉及的一个C/C++简单知识点:C/C++程序默认的栈内存大小是1M(约等于106)字节,而我们程序需要的内存空间4*06字节。因此,如果在程序中开辟内存:int love_sum_array[love_size],程序会报栈内存溢出错误,而采用堆的内存申请方式:int *love_sum_array = new int[love_size],则不会有这样的限制。