原链接:http://blog.csdn.net/v_JULY_v/article/details/6441279
第一节、亲和数问题
题目描述:
求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。
如此,是否已看出丝毫端倪?
如上所示,考虑到1是每个整数的因子,把出去整数本身之外的所有因子叫做这个数的“真因子”。如果两个整数,其中每一个真因子的和都恰好等于另一个数,那么这两个数,就构成一对“亲和数”(有关亲和数的更多讨论,可参考这:http://t.cn/hesH09)。
求解:
了解了什么是亲和数,接下来咱们一步一步来解决上面提出的问题(以下内容大部引自水的原话,同时水哥有一句原话,“在你真正弄弄懂这个范例之前,你不配说你懂数据结构和算法”)。
第二节、伴随数组线性遍历
依据上文中的第3点思路,编写如下代码:
运行结果:
部分运行结果:
@上善若水:
1、可能大家理解的还不是很清晰,我们建立一个5 000 000 的数组,从1到2 500 000 开始,在每一个下标是i的倍数的位置上加上i,那么在循环结束之后,我们得到的是什么?是 类似埃斯托拉晒求素数的数组(当然里面有真的亲和数),然后只需要一次遍历就可以轻松找到所有的亲和数了。时间复杂度,线性。
2、我们可以清晰的发现连续数据的映射可以通过数组结构本身的特点替代,用来节约空间,这是数据结构的艺术。在大规模连续数据的回溯处理上,可以通过转化为递推生成的方法,逆向思维操作,这是算法的艺术。
3、把最简单的东西运用的最巧妙的人,要比用复杂方法解决复杂问题的人要头脑清晰。
第三节、程序的构造与解释
我再来具体解释下上述程序的原理,ok,举个例子,假设是求10以内的亲和数,求解步骤如下:
因为所有数的真因数都包含1,所以,先在各个数的下方全部置1
特别鸣谢
litaoye专门为本亲和数问题开帖子继续阐述,有兴趣的朋友可继续参见:http://topic.csdn.net/u/20110526/21/129c2235-1f44-42e9-a55f-878920c21e19.html。同时,任何人对本亲和数问题有任何问题,也可以回复到上述帖子上。
- //求解亲和数问题
- //copyright@ litaoye
- //July、胡滨,updated,2011.05.26。
- using System;
- using System.Collections.Generic;
- namespace CSharpTest
- {
- class Program
- {
- public static void Main()
- {
- int max = 5000000;
- DateTime start = DateTime.Now;
- int[] counter = CreateCounter(max);
- for (int i = 0; i < counter.Length; i++)
- {
- int num = counter[i] - i;
- //if (num < counter.Length && num > i && counter[num] == counter[i])
- // Console.WriteLine("{0} {1}", i, num);
- }
- Console.WriteLine((DateTime.Now - start).TotalSeconds);
- Console.ReadKey();
- }
- static int[] CreateCounter(int n)
- {
- List<int> primes = new List<int>();
- int[] counter = new int[n + 1];
- counter[1] = 1;
- for (int i = 2; i <= n; i++)
- {
- if (counter[i] == 0)
- {
- counter[i] = i + 1;
- primes.Add(i);
- }
- for (int j = 0; j < primes.Count; j++)
- {
- if (primes[j] * i > n)
- break;
- if (i % primes[j] == 0)
- {
- int k = i;
- int l = primes[j] * primes[j];
- while (k % primes[j] == 0)
- {
- l *= primes[j];
- k /= primes[j];
- }
- counter[primes[j] * i] = counter[k] * (l - 1) / (primes[j] - 1);
- break;
- }
- else
- counter[primes[j] * i] = counter[i] * (primes[j] + 1);
- }
- }
- return counter;
- }
- }
- }
- /*
- 测试结果:
- 0.484375
- 0.484375
- 0.46875
- 单位second。
- */
本章完。