代码改变世界

亲和数问题

2013-08-06 17:14  夜与周公  阅读(1007)  评论(0编辑  收藏  举报

求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],则不会有这样的限制。