先放普通代码:

#include <iostream>
using namespace std; void getPrime_1()
{
const int MAXN = 100;
bool flag[MAXN];
int primes[MAXN / 3 + 1], pi=0;
primes[pi++]=2; //2 是一个素数,先记录下来
int i, j;
for(i=0;i<MAXN;++i)flag[i]=false;//全部置假,没访问一个,相应位置真
for (i = 3; i < MAXN; i+=2){//大于2的偶数一定不是素数,所以只要判断奇数即可
if (!flag[i])//如果是素数
{
primes[pi++] = i;
for (j = i; j < MAXN; j += i)//i的倍数一定都不是素数
flag[j] = true;
}
}
for(i=0;i<pi;++i)
cout<<primes[i]<<" ";
cout<<endl;
}

这里使用了素数表,每一个bool型占用1个字节,共8位二进制位。而且这里除了多用了很多的无用bool变量在flag数组里,可以看到,我们使用的flag数组只用到2号位之后的所有奇数位。因此,这里可以进行压缩改进一下:

  1. flag数组减少一半
  2. 使用位操作符使空间占用减少为原来的八分之一

这里使用到对指定位置置1的操作:对于一个整数X可以通过将1左移n位后,与X进行操作,使X的第n置1

int X=0;
int n=10;
X |= 1<< n; // 将 X 的第 n 位置 1

所以根据上面两条,优化后的代码如下:

void getPrime_2()
{
const int MAXN = 200;
const int BitN=(MAXN/2)/32+1;//
int flag[BitN];
int primes[MAXN / 3 + 1], pi=0;
primes[pi++]=2; //2 是一个素数,先记录下来
int i, j;
for(i=0;i<BitN;++i)flag[i]=0;//全置0,每访问过一个,相应位置1
for (i = 3; i < MAXN; i+=2){ //大于2的偶数一定不是素数,所以只要判断奇数即可
if (!((flag[(i/2) / 32] >> ((i/2) % 32)) & 1))
{
primes[pi++] = i;
//i的倍数一定都不是素数,其中,j加上一个i后为偶数,上一级已经不考虑了,所以还要加上一个i
for (j = i; j < MAXN; j =j+i+i)
flag[(j/2) / 32] |= (1 << ((j/2) % 32));
}
}
for(i=0;i<pi;++i)
cout<<primes[i]<<" ";
cout<<endl;
}

首先,根据最大数,判断需要32的整型多少个:

BitN=(MAXN/2)/32+1;

MAXN/2去除了所有偶数位,(MAXN/2)/32+1代表需要多少32位的整型。

其次,所有求位的操作,都要除以2以去除偶数位的影响。

同时:

for (j = i; j < MAXN; j =j+i+i)
flag[(j/2) / 32] |= (1 << ((j/2) % 32));

其中的 j=j+i+i; 是因为 ji 本身都是一个奇数,相加后为偶数,不考虑,所以还要加上一个 i

通过这种方式,缩小了占用空间。

代码文件

MAXN=200 时,实际的素数只有45个,但primes的大小却是66,那么这里怎么能更进一步的优化呢?

附:

由于这里使用的C++,那么为什么不使用STL中已有的容器类bitset呢:

#include <bitset>
void getPrime_3(){
const int MAXN = 100;
bitset<(MAXN/2+1)> flag(0); //不考虑偶数位
int primes[MAXN / 3 + 1], pi=0;
primes[pi++]=2;
int i, j;
for (i = 3; i < MAXN; i+=2){ //大于2的偶数一定不是素数,所以只要判断奇数即可
if (!(flag.test(i/2)))//如果对应位为false
{
primes[pi++] = i;
//i的倍数一定都不是素数,其中,j加上一个i后为偶数,上一级已经不考虑了,所以还要加上一个i
for (j = i; j < MAXN; j =j+i+i)
flag.set(j/2);//设置对应位为true
}
}
for(i=0;i<pi;++i)
cout<<primes[i]<<" ";
cout<<endl;
}

可以看到,使用STL的代码还是比较简洁的!

下面去除重复计算的部分

刚才已经将偶数的计算去除了,但仍然还会有一部分的重复计算,比如:

i=3时,会访问15,同时,当i=5时,也会访问15

先上代码:

void getPrime_4()
{
const int MAXN = 100;
bitset<(MAXN/2+1)> flag(0); //不考虑偶数位
int primes[MAXN / 3 + 1], pi=0;
primes[pi++]=2;
int i, j;
for (i = 3; i < MAXN; i+=2)
{
if (!(flag.test(i/2)))
primes[pi++] = i;
for (j = 1; (j < pi) && (i * primes[j] < MAXN); j++) // 1
{
flag.set(i*primes[j]/2);
if (i % primes[j] == 0) // 2
break;
}
} for(i=0;i<pi;++i)
cout<<primes[i]<<" ";
cout<<endl;
}

注释 1 表示让当前奇数和已经查出来的素数进行逐个相乘,相乘后的结果数肯定不是素数!

注释 2 对于任何数来说,如果它如果是该素数的倍数那么它就不能再与素数表中该素数之后的素数相乘了,如9是3的倍数,所以在9*3之后就不能再去用计算9*5了。

当数据量很大时,getPrime_4()getPrime_1()的差别将在时间和空间上是很大的!

如果阅读效果不好,请查看GitHub对应页面:

使用位操作符求素数

GitHubBlog /

C++学习之【使用位操作符求素数分析】的更多相关文章

  1. C语言学习笔记之位运算求余

    我们都知道,求一个数被另一个数整除的余数,可以用求余运算符”%“,但是,如果不允许使用求余运算符,又该怎么办呢?下面介绍一种方法,是通过位运算来求余,但是注意:该方法只对除数是2的N次方幂时才有效. ...

  2. c语言小技巧:C语言学习笔记之位运算求余

    我们都知道,求一个数被另一个数整除的余数,可以用求余运算符”%“,但是,如果不 允许使用求余运算符,又该怎么办呢?下面介绍一种方法,是通过位运算来求余,但是注 意:该方法只对除数是2的N次方幂时才有效 ...

  3. Java【基础学习】之暴力求素数【用数组返回】

    Java[基础学习]之暴力求素数[用数组返回] */ import java.util.*; public class Main{ public static void main(String[] a ...

  4. 【Java】位操作符

    位运算符 java支持的位运算符有7个,分为两类:位逻辑运算和移位运算.位逻辑运算符包括按位取反(~).按位与(&).按位或(|)和按位异或(^)4种,.移位运算符包括左移(<<) ...

  5. C++位操作符总结

    #include <stdio.h> #include <memory.h> #include <malloc.h> #define MaxBinLength 16 ...

  6. ECMAScript位操作符

    在ECMAScript中,有少数的几个操作符可以对二进制位进行直接操作,这几个操作符本身直接对二进制进行操作,所有它们的本身是非常效率的,学习这一段有助于以后的优化以及理解. ECMAScript中采 ...

  7. [转] PostgreSQL学习手册(函数和操作符)

    一.逻辑操作符: 常用的逻辑操作符有:AND.OR和NOT.其语义与其它编程语言中的逻辑操作符完全相同. 二.比较操作符: 下面是PostgreSQL中提供的比较操作符列表: 操作符 描述 < ...

  8. 浅谈JavaScript位操作符

    因为ECMAscript中所有数值都是以IEEE-75464格式存储,所以才会诞生了位操作符的概念. 位操作符作用于最基本的层次上,因为数值按位存储,所以位操作符的作用也就是操作数值的位.不过位操作符 ...

  9. Algorithm --> 筛法求素数

    一般的线性筛法 genPrime和genPrime2是筛法求素数的两种实现,一个思路,表示方法不同而已. #include<iostream> #include<math.h> ...

随机推荐

  1. C++ GUI Qt4编程(08)-3.2spreadsheet-resource

    1. C++ GUI Qt4编程第三章,图片使用资源机制法. 2. 步骤: 2-1. 在resource文件夹下,新建images文件,存放图片. 2-2. 新建spreadsheet.qrc文件,并 ...

  2. maven-javadoc-plugin

    <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-javad ...

  3. PIE SDK位深转换

      1.算法功能简介 位深转换功能是一种用于更改一个给定输入文件数据范围的灵活方法.可以完全控制输入和输出直方图,以及输出数据类型(字节型.整型.浮点型等). PIE支持算法功能的执行,下面对位深转换 ...

  4. PIE SDK PCA融合

    1.算法功能简介 PCA 融合分三步实现,首先将多光谱数据进行主成分变换,然后用高分辨单波段替换第一主成分波段,最后进行主成份逆变换得到融合图像. PIE支持算法功能的执行,下面对PCA融合算法功能进 ...

  5. 转 C#对多个集合和数组的操作(合并,去重,判断)

    在开发过程中.数组和集合的处理是最让我们担心.一般会用for or foreach 来处理一些操作.这里介绍一些常用的集合跟数组的操作函数. 首先举例2个集合A,B. List<int> ...

  6. ajax请求方法及参数说明

    $.ajax()请求示例 <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> ...

  7. python-OS.path.join()路径拼接

    os.path.join()函数: 第一个以”/”开头的参数开始拼接,之前的参数全部丢弃. 以上一种情况为先.在上一种情况确保情况下,若出现”./”开头的参数,会从”./”开头的参数的上一个参数开始拼 ...

  8. 9 Essential Free Linux Transcoders(转码)

    需要转码的理由千万种,所幸除了硬件转码之外,Linux平台还有很多开源工具可以借鉴,如该文章所示: 原文来自:9 Essential Free Linux Transcoders(http://www ...

  9. zstu 4212 ——String Game ——————【字符串处理】

    4212: String Game Time Limit: 1 Sec  Memory Limit: 128 MBSubmit: 337  Solved: 41 Description Alice a ...

  10. bzoj 5217: [Lydsy2017省队十连测]航海舰队

    Description Byteasar 组建了一支舰队!他们现在正在海洋上航行着.海洋可以抽象成一张n×m 的网格图,其中有些位置是" .",表示这一格是海水,可以通过:有些位置 ...