1到100000的因数

将1到100000的因数存起来,要求用尽量快,且用尽量少的空间

首先很容易想到的就是就是将n个数的因数存进数组里,然后每个数分别用,时间复杂度为
不难求出1到100000里面最多的有128个因数,代码如下:
#include <stdio.h>
#include <math.h>

typedef struct
{
	int a[100000][128];
	int num[100000];
}Data;

Data d;

int main()
{
	int i, n, j;
	for (i = 0; i < 100000; i++)
	{
		for (j = 1; j <= sqrt(i + 1); j++)
		{
			if ((i + 1) % j == 0)
			{
				d.a[i][d.num[i]++] = j;
				if (j != (i + 1) / j)
				{
					d.a[i][d.num[i]++] = (i + 1) / j;
				}
			}
		}
	}
	printf("%dk\n", (100000 * 128 + 100000 + 3) * 4 / 1024);
	
	return 0;
}
用了50390k的空间,这可是很大的空间
说到省空间,用数组存起来,不就是对空间的一种浪费吗?比如1只有1这一个因数,而却开了那么大的空间,这时就可以用链表来存起来,代码如下:
#include <stdio.h>
#include <math.h>
#include <malloc.h>

typedef struct Node
{
	struct Node *next;
	int data;
}List;

List *l[100000];

int main()
{
	int i, j, n, sum, num;
	List *p;
	sum = 0;
	for (i = 0; i < 100000; i++)
	{
		for (j = 1; j <= sqrt(i + 1); j++)
		{
			if ((i + 1) % j == 0)
			{
				List *s = (List*)malloc(sizeof(List));
				s->data = j;
				s->next = NULL;
				if (l[i])
				{
					p->next = s;
					p = p->next;
				}
				else
				{
					l[i] = s;
					p = l[i];
				}
				if (j != (i + 1) / j)
				{
					List *s = (List*)malloc(sizeof(List));
					s->data = (i + 1) / j;
					s->next = NULL;
					p->next = s;
					p = p->next;
				}
			}
		}
	}
	
	for (i = 0; i < 100000; i++)
	{
		num = 0;
		if (l[i])
		{
			p = l[i]->next;
			while (p)
			{
				num++;
				l[i]->next = p->next;
				free(p);
				p = l[i]->next;
			}
			num++;
			free(l[i]);
		}
		sum += 2 * num + 1;
	}
	printf("%dk\n", (sum + 4) * 4 / 1024);
	
	return 0;
}
这样一来,虽然时间复杂度还是那样,但就只用了9505k,比之前节省了不少空间,但还是挺大的,还可以进一步减少空间
不难发现一个数的因数与其中的任两个因数的因数有着一定的关系,以100为例
100的因数有1,2,4,5,10,20,25,50,100
而100 = 2 * 50
50的因数又有1,2,5,10,25,50
2 * 50的各个因数,得2,4,10,20,50,100
这两个集合的并集即为100的因数
可能大家在想为什么这样就可以呢,其实道理很简单:
对于n = p * q,p为n大于1且不为本身的最小因数,q分解后的所有因数显然都为n的因数,而p*(q的因数)也肯定是n的因数,这样就快速地找出了所有因数
而且运用指针直接指过去,还省下了不少空间,代码如下:
#include <stdio.h>
#include <math.h>
#include <malloc.h>

typedef struct Node
{
	struct Node *next;
	int data;
}List;

List *l[100000];

int main()
{
	int i, j, n, sum, num;
	List *p, *q;
	sum = 0;
	for (i = 0; i < 100000; i++)
	{
		num = 0;
		for (j = 2; j <= sqrt(i + 1); j++)
		{
			if ((i + 1) % j == 0)
			{
				q = l[(i + 1) / j - 1];
				while (q)
				{
					if ((i + 1) / j % (j * q->data))
					{
						num++;
						List *s = (List*)malloc(sizeof(List));
						s->data = j * q->data;
						s->next = NULL;
						if (l[i])
						{
							p->next = s;
							p = p->next;
						}
						else
						{
							l[i] = s;
							p = l[i];
						}
					}
					q = q->next;
				}
				break;	
			}
		}
		if (j > sqrt(i + 1))
		{
			num++;
			List *s = (List *)malloc(sizeof(List));
			s->data = 1;
			s->next = NULL;
			l[i] = s;
			if (i)
			{
				num++;
				List *s = (List *)malloc(sizeof(List));
				s->data = i + 1;
				s->next = NULL;
				l[i]->next = s;
			}
		}
		else
		{
			p->next = l[(i + 1) / j - 1];
		}
		sum += 2 * num + 1;
	}
	printf("%dk\n", (sum + 5) * 4 / 1000);
	
	return 0;
}
这时只需要3871k的空间,时间复杂度也降低了,不过仍可以继续降低时间复杂度
在上面的做法中,很显然地,我们找的最小的因数其实可以说是最小的质因数,可以利用埃拉托色尼筛选法找出所有素数后,快速找到最小的质因数
#include <stdio.h>
#include <math.h>
#include <malloc.h>

typedef struct Node
{
	struct Node *next;
	int data;
}List;

List *l[100000];
int r[100000];

int main()
{
	int i, j, n, sum, num;
	List *p, *q, *l2, *k;
	for (i = 0; i < 100000; i++)
	{
		r[i] = 1;
	}
	for (i = 2; i <= sqrt(100000); i++)
	{
		for (j = i; i * j <= 100000; j++)
		{
			r[i * j - 1] = 0;
		}
	}
	List *s = (List *)malloc(sizeof(List));
	s->data = 2;
	s->next = NULL;
	l2 = s;
	p = l2;
	sum = 1;
	for (i = 2; i < 100000; i++)
	{
		if (r[i])
		{
			sum++;
			List *s = (List *)malloc(sizeof(List));
			s->data = i + 1;
			s->next = NULL;
			p->next = s;
			p = p->next;
		}
	}
	sum = sum * 2 + 1;
	for (i = 0; i < 100000; i++)
	{
		num = 0;
		k = l2;
		while (k && k->data <= sqrt(i + 1))
		{
			if ((i + 1) % k->data == 0)
			{
				q = l[(i + 1) / k->data - 1];
				while (q)
				{
					if ((i + 1) / k->data % (k->data * q->data))
					{
						num++;
						List *s = (List*)malloc(sizeof(List));
						s->data = k->data * q->data;
						s->next = NULL;
						if (l[i])
						{
							p->next = s;
							p = p->next;
						}
						else
						{
							l[i] = s;
							p = l[i];
						}
					}
					q = q->next;
				}
				break;	
			}
			k = k->next;
		}
		if (k->data > sqrt(i + 1))
		{
			num++;
			List *s = (List *)malloc(sizeof(List));
			s->data = 1;
			s->next = NULL;
			l[i] = s;
			if (i)
			{
				num++;
				List *s = (List *)malloc(sizeof(List));
				s->data = i + 1;
				s->next = NULL;
				l[i]->next = s;
			}
		}
		else
		{
			p->next = l[(i + 1) / k->data - 1];
		}
		sum += 2 * num + 1;
	}
	printf("%dk\n", (sum + 100007) * 4 / 1024);
	
	return 0;
}
这样优化后,时间复杂度约为O(nlgn),用了7852k的空间
不过空间仍可以减少,只需利用位掩码实现埃拉托色尼筛选法即可大幅度减少空间(所开数组长度为原来的1/8)
全部评论

相关推荐

不愿透露姓名的神秘牛友
11-21 17:16
科大讯飞 算法工程师 28.0k*14.0, 百分之三十是绩效,惯例只发0.9
点赞 评论 收藏
分享
已老实求offer😫:有点像徐坤(没有冒犯的意思哈)
点赞 评论 收藏
分享
点赞 评论 收藏
分享
1 1 评论
分享
牛客网
牛客企业服务