您的位置:首页 > 其它

算法学习(五)求解500万以内的亲和数,连续数据映射为数组

2016-04-18 21:31 302 查看
题目描述:

求500万以内的所有亲和数,如果两个数a和b,a的所有真因数之和等于b,b的所有真因数之和等于a,则称a,b是一对亲和数。

真因数:除了本身以外的所有因数,

列如:220的真因数:1,2,4,5,10,11,20,22,44,55,110;

284的真因数:1,2,4,71,142.

220= 1+2+4+71+142 = sum[284]

284 = 1+2+4+5+10+11+20+22+44+55+110 = sum[220]

分析:

一个数的因数不超过它的一半,我们可以利用数组,把数作为数组下标,数组对应元素为真因数之和,然后遍历满足sum[sum[i]] = i的i,和sum[i]为一对亲和数。

首先初始化时,数组每一项都有一个1,因为1是所有数的真因数。从然后因数i从2开始到n/2,由于本身不是真因数,所以对应项从2i开始,3i,4i,5i….直到k*i>n,这些项都包含因数i。所有这些项sum[2i],sum[3i]….都要加上i。然后就可以开始遍历寻找满足亲和数条件的项。

i= 2时,sum[4]+=2,sum[6]+= 2;sum[8]+=2….

i = 3时,sum[6]+= 3;sum[9]+= 3;sum[12]+=3….

i = 4时,sum[8]+=4;sum[12] +=4…

/*************************************************************************
> File Name: find_qinhe.c
> Author: zxl
> mail: 857317335@qq.com
> Created Time: 2016年04月18日 星期一 20时36分58秒
************************************************************************/

#include<stdio.h>
#define SIZE 5000000
int main()
{
static int sum[5000001];   //大数组声明为静态,放在静态存储区
int i,j;
for(i = 1;i<= SIZE;i++)
sum[i] = 1;        //每一项都含有1
for(i = 2;i+i<=SIZE;i++)  //遍历所有因数,
{
j = 2*i;
while(j<=SIZE)  //对于因数2,循环n/2次,对于因数3,循环n/3次,总的次数为n(1/2 + 1/3+ 1/4 + 1/n/2),总的时间复杂度为O(N* log N + N)
{
sum[j] +=i;     //包含因数的项要加上因数
j+=i;
}
}
for(i = 220;i<=SIZE;i++)
{
if(sum[i]>i && sum[i] <=SIZE&& sum[sum[i]] == i)//使用sum[i] >i可以去掉重复的,比如sum[248] = 220
{
printf("%d,%d\n",i,sum[i]);
}
}
return 0;
}


需要注意的时,大数组定义的地方,如果在函数内,分配空间都在堆栈上,堆栈是比较有限的,大的空间都是分配到静态存储区,可以放在全局或者在函数里面定义加上static。

顺便扯点别的,可编程内存分为:静态存储区,堆区和栈区

静态存储区:内存在程序编译的时候就已经分配好,这块内存在程序的整个运行期间都存在,它主要存放静态数据、全局数据和常量。

栈区:在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集中,效率高,但是分配的内存 容量有限。

堆区:称动态内存分配,程序在运行的时候用malloc或new申请任意大小的内存,可以自己负责在适当的时候free或delete释放内存。动态内存的生存期可以由我们决定。

总结:连续数据的映射可以通过数组结构本身的特点替代,用来节约空间,这是数据结构的艺术,在大规模连续数据的回溯法处理上,通过转化为递推生成的方法,逆向思维操作,这是算法的艺术。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: