筛法打表

10 篇文章 0 订阅
5 篇文章 0 订阅

有时候题目给的数据特别大,但是需要求素数的时候,应该用筛选法求素数,同理也可用筛选法求因子和

筛法求素数

筛素数法在这里不就详细介绍了,本文着重对筛素数法所使用的素数表进行优化来减小其空间占用。要压缩素数表的空间占用,可以使用位操作。下面是用筛素数法计算100以内的素数示例代码(注2):

[cpp]  view plain copy
  1. //by MoreWindows( http://blog.csdn.net/MoreWindows )  
  2. #include <stdio.h>  
  3. #include <memory.h>  
  4. const int MAXN = 100;  
  5. bool flag[MAXN];  
  6. int primes[MAXN / 3 + 1], pi;  
  7. //对每个素数,它的倍数必定不是素数。  
  8. //有很多重复如flag[10]会在访问flag[2]和flag[5]时各访问一次  
  9. void GetPrime_1()  
  10. {  
  11.     int i, j;  
  12.     pi = 0;  
  13.     memset(flag, falsesizeof(flag));  
  14.     for (i = 2; i < MAXN; i++)  
  15.         if (!flag[i])  
  16.         {  
  17.             primes[pi++] = i;  
  18.             for (j = i; j < MAXN; j += i)  
  19.                 flag[j] = true;  
  20.         }  
  21. }  
  22. void PrintfArray()  
  23. {  
  24.     for (int i = 0; i < pi; i++)  
  25.         printf("%d ", primes[i]);  
  26.     putchar('\n');  
  27. }  
  28. int main()  
  29. {  
  30.     printf("用筛素数法求100以内的素数\n-- by MoreWindows( http://blog.csdn.net/MoreWindows ) --\n\n");    
  31.     GetPrime_1();  
  32.     PrintfArray();  
  33.     return 0;  
  34. }  

运行结果如下:

在上面程序是用bool数组来作标记的,bool型数据占1个字节(8位),因此用位操作来压缩下空间占用将会使空间的占用减少八分之七。

下面考虑下如何在数组中对指定位置置1,先考虑如何对一个整数在指定位置上置1。对于一个整数可以通过将1向左移位后与其相或来达到在指定位上置1的效果,代码如下所示:

[cpp]  view plain copy
  1. //在一个数指定位上置1  
  2. int j = 0;  
  3. j |=  1 << 10;  
  4. printf("%d\n", j);  

同样,可以1向左移位后与原数相与来判断指定位上是0还是1(也可以将原数右移若干位再与1相与)。

[cpp]  view plain copy
  1.    //判断指定位上是0还是1  
  2. int j = 1 << 10;  
  3. if ((j & (1 << 10)) != 0)  
  4.     printf("指定位上为1");  
  5. else  
  6.     printf("指定位上为0");  

扩展到数组上,我们可以采用这种方法,因为数组在内存上也是连续分配的一段空间,完全可以“认为”是一个很长的整数。先写一份测试代码,看看如何在数组中使用位操作:

[cpp]  view plain copy
  1. //by MoreWindows( http://blog.csdn.net/MoreWindows )    
  2. #include <stdio.h>  
  3. int main()  
  4. {  
  5.     printf("     对数组中指定位置上置位和判断该位\n");  
  6.     printf("--- by MoreWindows( http://blog.csdn.net/MoreWindows )  ---\n\n");  
  7.     //在数组中在指定的位置上写1  
  8.     int b[5] = {0};  
  9.     int i;  
  10.     //在第i个位置上写1  
  11.     for (i = 0; i < 40; i += 3)  
  12.         b[i / 32] |= (1 << (i % 32));  
  13.     //输出整个bitset  
  14.     for (i = 0; i < 40; i++)  
  15.     {  
  16.         if ((b[i / 32] >> (i % 32)) & 1)  
  17.             putchar('1');  
  18.         else   
  19.             putchar('0');  
  20.     }  
  21.     putchar('\n');  
  22.     return 0;  
  23. }  

运行结果如下:

可以看出该数组每3个就置成了1,证明我们上面对数组进行位操作的方法是正确的。因此可以将上面筛素数方法改成使用位操作压缩后的筛素数方法:

[cpp]  view plain copy
  1. //使用位操作压缩后的筛素数方法  
  2. //by MoreWindows( http://blog.csdn.net/MoreWindows )   
  3. #include <stdio.h>  
  4. #include <memory.h>  
  5. const int MAXN = 100;  
  6. int flag[MAXN / 32 + 1];  
  7. int primes[MAXN / 3 + 1], pi;  
  8. void GetPrime_1()  
  9. {  
  10.     int i, j;  
  11.     pi = 0;  
  12.     memset(flag, 0, sizeof(flag));  
  13.     for (i = 2; i < MAXN; i++)  
  14.         if (!((flag[i / 32] >> (i % 32)) & 1))  
  15.         {  
  16.             primes[pi++] = i;  
  17.             for (j = i; j < MAXN; j += i)  
  18.                 flag[j / 32] |= (1 << (j % 32));  
  19.         }  
  20. }  
  21. void PrintfArray()  
  22. {  
  23.     for (int i = 0; i < pi; i++)  
  24.         printf("%d ", primes[i]);  
  25.     putchar('\n');  
  26. }  
  27. int main()  
  28. {  
  29.     printf("用位操作压缩后筛素数法求100以内的素数\n-- by MoreWindows( http://blog.csdn.net/MoreWindows ) --\n\n");    
  30.     GetPrime_1();  
  31.     PrintfArray();  
  32.     return 0;  
  33. }  

同样运行结果为:

另外,还可以使用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;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值