Axe*_*son 3 c++ primes sieve-of-atkin
最近我一直在研究使用Atve的Sieve(http://en.wikipedia.org/wiki/Sieve_of_atkin)生成其素数的C++素数生成器.我的目标是能够生成任何32位数字.我将主要用于项目的euler问题.大多数情况下,这只是一个夏季项目.
该程序使用一个位板来存储素数:即一系列的1和0,例如第11位将是1,第12位是0,第13位是1等.为了有效的内存使用,这实际上是和字符数组,每个字符包含8位.我使用标志和按位运算符来设置和检索位.该算法的陀螺很简单:使用我不假装理解的一些方程式进行第一次传递,以定义数字是否被认为是"素数".这将在很大程度上得到正确的答案,但一些非主要数字将被标记为素数.因此,在遍历列表时,将刚刚找到的素数的所有倍数设置为"非素数".这具有便利的优点,即需要较少的处理器时间,而素数越大.
我已经完成了90%,只有一个问题:一些素数缺失.
通过检查位板,我已经确定在第一次传递期间省略了这些素数,这基本上为每个方程式切换一个数字(参见维基百科条目).我一次又一次地浏览了这段代码.我甚至尝试增加维基百科文章中显示的界限,效率较低,但我想可能会打几个我已经忽略的数字.没有任何效果.这些数字只是评估为不是素数.我的大部分测试都是在128以下的所有质数下.在这个范围内,这些是省略的素数:
23和59.
我毫不怀疑,在更高的范围内,会有更多的缺失(只是不想计算所有这些).我不知道为什么缺少这些,但它们是.这两个素数有什么特别之处吗?我已经进行了两次和三次检查,发现并修复了错误,但是我仍然缺少一些愚蠢的东西.
无论如何,这是我的代码:
#include <iostream>
#include <limits.h>
#include <math.h>
using namespace std;
const unsigned short DWORD_BITS = 8;
unsigned char flag(const unsigned char);
void printBinary(unsigned char);
class PrimeGen
{
public:
unsigned char* sieve;
unsigned sievelen;
unsigned limit;
unsigned bookmark;
PrimeGen(const unsigned);
void firstPass();
unsigned next();
bool getBit(const unsigned);
void onBit(const unsigned);
void offBit(const unsigned);
void switchBit(const unsigned);
void printBoard();
};
PrimeGen::PrimeGen(const unsigned max_num)
{
limit = max_num;
sievelen = limit / DWORD_BITS + 1;
bookmark = 0;
sieve = (unsigned char*) malloc(sievelen);
for (unsigned i = 0; i < sievelen; i++) {sieve[i] = 0;}
firstPass();
}
inline bool PrimeGen::getBit(const unsigned index)
{
return sieve[index/DWORD_BITS] & flag(index%DWORD_BITS);
}
inline void PrimeGen::onBit(const unsigned index)
{
sieve[index/DWORD_BITS] |= flag(index%DWORD_BITS);
}
inline void PrimeGen::offBit(const unsigned index)
{
sieve[index/DWORD_BITS] &= ~flag(index%DWORD_BITS);
}
inline void PrimeGen::switchBit(const unsigned index)
{
sieve[index/DWORD_BITS] ^= flag(index%DWORD_BITS);
}
void PrimeGen::firstPass()
{
unsigned nmod,n,x,y,xroof, yroof;
//n = 4x^2 + y^2
xroof = (unsigned) sqrt(((double)(limit - 1)) / 4);
for(x = 1; x <= xroof; x++){
yroof = (unsigned) sqrt((double)(limit - 4 * x * x));
for(y = 1; y <= yroof; y++){
n = (4 * x * x) + (y * y);
nmod = n % 12;
if (nmod == 1 || nmod == 5){
switchBit(n);
}
}
}
xroof = (unsigned) sqrt(((double)(limit - 1)) / 3);
for(x = 1; x <= xroof; x++){
yroof = (unsigned) sqrt((double)(limit - 3 * x * x));
for(y = 1; y <= yroof; y++){
n = (3 * x * x) + (y * y);
nmod = n % 12;
if (nmod == 7){
switchBit(n);
}
}
}
xroof = (unsigned) sqrt(((double)(limit + 1)) / 3);
for(x = 1; x <= xroof; x++){
yroof = (unsigned) sqrt((double)(3 * x * x - 1));
for(y = 1; y <= yroof; y++){
n = (3 * x * x) - (y * y);
nmod = n % 12;
if (nmod == 11){
switchBit(n);
}
}
}
}
unsigned PrimeGen::next()
{
while (bookmark <= limit)
{
bookmark++;
if (getBit(bookmark))
{
unsigned out = bookmark;
for(unsigned num = bookmark * 2; num <= limit; num += bookmark)
{
offBit(num);
}
return out;
}
}
return 0;
}
inline void PrimeGen::printBoard()
{
for(unsigned i = 0; i < sievelen; i++)
{
if (i % 4 == 0)
cout << endl;
printBinary(sieve[i]);
cout << " ";
}
}
inline unsigned char flag(const unsigned char bit_index)
{
return ((unsigned char) 128) >> bit_index;
}
inline void printBinary(unsigned char byte)
{
unsigned int i = 1 << (sizeof(byte) * 8 - 1);
while (i > 0) {
if (byte & i)
cout << "1";
else
cout << "0";
i >>= 1;
}
}
Run Code Online (Sandbox Code Playgroud)
我尽力清理它并使其可读.我不是专业的程序员,所以请怜悯.
这是我得到的输出,当我初始化一个名为pgen的PrimeGen对象时,用pgen.printBoard()打印它的初始位板(请注意在next()迭代之前缺少23和59),然后遍历next()并且打印所有返回的素数:
00000101 00010100 01010000 01000101
00000100 01010001 00000100 00000100
00010001 01000001 00010000 01000000
01000101 00010100 01000000 00000001
5
7
11
13
17
19
29
31
37
41
43
47
53
61
67
71
73
79
83
89
97
101
103
107
109
113
127
DONE
Process returned 0 (0x0) execution time : 0.064 s
Press any key to continue.
Run Code Online (Sandbox Code Playgroud)
尤里卡!
正如所料,这对我来说是一个愚蠢的错误.
3x ^ 2 - y ^ 2方程有一个我忽略的小警告:x> y.考虑到这一点,我多次切换23和59,导致它们失败.
谢谢你们的帮助.救了我的培根.