有时候题目给的数据特别大,但是需要求素数的时候,应该用筛选法求素数,同理也可用筛选法求因子和
筛法求素数
筛素数法在这里不就详细介绍了,本文着重对筛素数法所使用的素数表进行优化来减小其空间占用。要压缩素数表的空间占用,可以使用位操作。下面是用筛素数法计算100以内的素数示例代码(注2):
- //by MoreWindows( http://blog.csdn.net/MoreWindows )
- #include <stdio.h>
- #include <memory.h>
- const int MAXN = 100;
- bool flag[MAXN];
- int primes[MAXN / 3 + 1], pi;
- //对每个素数,它的倍数必定不是素数。
- //有很多重复如flag[10]会在访问flag[2]和flag[5]时各访问一次
- void GetPrime_1()
- {
- int i, j;
- pi = 0;
- memset(flag, false, sizeof(flag));
- for (i = 2; i < MAXN; i++)
- if (!flag[i])
- {
- primes[pi++] = i;
- for (j = i; j < MAXN; j += i)
- flag[j] = true;
- }
- }
- void PrintfArray()
- {
- for (int i = 0; i < pi; i++)
- printf("%d ", primes[i]);
- putchar('\n');
- }
- int main()
- {
- printf("用筛素数法求100以内的素数\n-- by MoreWindows( http://blog.csdn.net/MoreWindows ) --\n\n");
- GetPrime_1();
- PrintfArray();
- return 0;
- }
运行结果如下:
在上面程序是用bool数组来作标记的,bool型数据占1个字节(8位),因此用位操作来压缩下空间占用将会使空间的占用减少八分之七。
下面考虑下如何在数组中对指定位置置1,先考虑如何对一个整数在指定位置上置1。对于一个整数可以通过将1向左移位后与其相或来达到在指定位上置1的效果,代码如下所示:
- //在一个数指定位上置1
- int j = 0;
- j |= 1 << 10;
- printf("%d\n", j);
同样,可以1向左移位后与原数相与来判断指定位上是0还是1(也可以将原数右移若干位再与1相与)。
- //判断指定位上是0还是1
- int j = 1 << 10;
- if ((j & (1 << 10)) != 0)
- printf("指定位上为1");
- else
- printf("指定位上为0");
扩展到数组上,我们可以采用这种方法,因为数组在内存上也是连续分配的一段空间,完全可以“认为”是一个很长的整数。先写一份测试代码,看看如何在数组中使用位操作:
- //by MoreWindows( http://blog.csdn.net/MoreWindows )
- #include <stdio.h>
- int main()
- {
- printf(" 对数组中指定位置上置位和判断该位\n");
- printf("--- by MoreWindows( http://blog.csdn.net/MoreWindows ) ---\n\n");
- //在数组中在指定的位置上写1
- int b[5] = {0};
- int i;
- //在第i个位置上写1
- for (i = 0; i < 40; i += 3)
- b[i / 32] |= (1 << (i % 32));
- //输出整个bitset
- for (i = 0; i < 40; i++)
- {
- if ((b[i / 32] >> (i % 32)) & 1)
- putchar('1');
- else
- putchar('0');
- }
- putchar('\n');
- return 0;
- }
运行结果如下:
可以看出该数组每3个就置成了1,证明我们上面对数组进行位操作的方法是正确的。因此可以将上面筛素数方法改成使用位操作压缩后的筛素数方法:
- //使用位操作压缩后的筛素数方法
- //by MoreWindows( http://blog.csdn.net/MoreWindows )
- #include <stdio.h>
- #include <memory.h>
- const int MAXN = 100;
- int flag[MAXN / 32 + 1];
- int primes[MAXN / 3 + 1], pi;
- void GetPrime_1()
- {
- int i, j;
- pi = 0;
- memset(flag, 0, sizeof(flag));
- for (i = 2; i < MAXN; i++)
- if (!((flag[i / 32] >> (i % 32)) & 1))
- {
- primes[pi++] = i;
- for (j = i; j < MAXN; j += i)
- flag[j / 32] |= (1 << (j % 32));
- }
- }
- void PrintfArray()
- {
- for (int i = 0; i < pi; i++)
- printf("%d ", primes[i]);
- putchar('\n');
- }
- int main()
- {
- printf("用位操作压缩后筛素数法求100以内的素数\n-- by MoreWindows( http://blog.csdn.net/MoreWindows ) --\n\n");
- GetPrime_1();
- PrintfArray();
- return 0;
- }
同样运行结果为:
另外,还可以使用C++ STL中的bitset类来作素数表。筛素数方法在笔试面试出现的几率还是比较大的,能写出用位操作压缩后的筛素数方法无疑将会使你的代码脱颖而出,因此强烈建议读者自己亲自动手实现一遍,平时多努力,考试才不慌。
筛法求因子和#include <iostream>
using namespace std;
int ans[55555555];
int main()
{
int i,j,n;
ans[0] = ans[1] = 1;
cin>>n;
for(i = 2; i <= n; ++i)
{
++ans[i];
for(j = i*2; j <= n; j += i)
{
ans[j] += i;
}
}
for(int i =1 ; i<=n ;i++)
{
cout<<ans[i]<<" ";
}
return 0;
}
return 0;}
下面附上山东省ACM竞赛题一道,需要用到筛法求因子和,然后打表求解。
1022:
Phone Number题目描述
We know that if a phone number A is another phone number B’s prefix, B is not able to be called. For an example, A is 123 while B is 12345, after pressing 123, we call A, and not able to call B.
Given N phone numbers, your task is to find whether there exits two numbers A and B that A is B’s prefix.
输入
The input consists of several test cases.
The first line of input in each test case contains one integer N (0<N<1001), represent the number of phone numbers.
The next line contains N integers, describing the phone numbers.
The last case is followed by a line containing one zero.
输出
For each test case, if there exits a phone number that cannot be called, print “NO”, otherwise print “YES” instead.
示例输入
2
012
012345
2
12
012345
0
示例输出
NO
YES
//AC代码
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <cmath>
using namespace std;
int ans[71][2] = {
220,284,
1184,1210,
2620,2924,
5020,5564,
6232,6368,
10744,10856,
12285,14595,
17296,18416,
63020,76084,
66928,66992,
67095,71145,
69615,87633,
79750,88730,
100485,124155,
122265,139815,
122368,123152,
141664,153176,
142310,168730,
171856,176336,
176272,180848,
185368,203432,
196724,202444,
280540,365084,
308620,389924,
319550,430402,
356408,399592,
437456,455344,
469028,486178,
503056,514736,
522405,525915,
600392,669688,
609928,686072,
624184,691256,
635624,712216,
643336,652664,
667964,783556,
726104,796696,
802725,863835,
879712,901424,
898216,980984,
947835,1125765,
998104,1043096,
1077890,1099390,
1154450,1189150,
1156870,1292570,
1175265,1438983,
1185376,1286744,
1280565,1340235,
1328470,1483850,
1358595,1486845,
1392368,1464592,
1466150,1747930,
1468324,1749212,
1511930,1598470,
1669910,2062570,
1798875,1870245,
2082464,2090656,
2236570,2429030,
2652728,2941672,
2723792,2874064,
2728726,3077354,
2739704,2928136,
2802416,2947216,
2803580,3716164,
3276856,3721544,
3606850,3892670,
3786904,4300136,
3805264,4006736,
4238984,4314616,
4246130,4488910,
4259750,4445050,
};
int main()
{
int m,n;
while(cin>>m>>n)
{
int cent = 0;
for(int i = 0 ; i < 71 ; i++)
{
if(ans[i][0] >= m &&ans[i][0] <=n && ans[i][1] >= m &&ans[i][1] <=n)
cent++;
}
cout<<cent<<endl;
}
return 0;
}