您的位置:首页 > 其它

各种高性能算法 求素数 太牛 挑战算法

2011-08-19 19:22 429 查看
发个某牛根据intfree改写的程序,计算10000000000(100亿)以素数个数,只要18秒多,我的CPU是AMD1.8G,要在INTEL的3.0G机子上跑肯定在10秒内。。。

C/C++ code
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <time.h>

__int64 maxn = 10000000L;
const long maxlen = 100000;
long m = 5;
const long dots = 80;

BYTE *mask;
long primes[maxlen+2], mprimes[maxlen+2], ans[maxlen+2];
long bits[256];
long len, r;

void solve(long a, long b, long c, long &x, long &y)
{
if(a == 0)
{
x = 0;
y = c/b;
}
else
{
solve(b%a, a, c, y, x);
x -= b/a*y;
}
}

// primes数组从下标1开始存放素数,例如primes[1]为2
void init()
{
long p, k;

k = 2;
len = 0;

// 求待求范围的平凡根范围内的素数
do
{
++len;
primes[len] = k;

do
{
++k;
p = 1;
while((primes[p]*primes[p] < k) && (k%primes[p] != 0))
{
++p;
}
}while(k%primes[p] == 0);
}while(1.0*k*k <= maxn);

bits[0] = 0;
for(k=1; k<=255; ++k)
{
bits[k] = bits[k>>1] + (k & 1);
}
}

void makeprimes()
{
long i, k, n, size, kmax, min;
long last, p, x, dot;

// 以2*3*5*7*11*13为基,循环完后n为30030
n = 1;
for(i=1; i<=m; ++i)
n *= primes[i];

for(i=m+1; i<=len; ++i)
{
p = primes[i];
solve(p, -n, 1, x, mprimes[i]);
mprimes[i] = (mprimes[i]%p+p)%p;
}

r = len;
size = long(maxn/n+7)>>3;
last = 0;
dot = 0;

memset(ans, 0, sizeof(ans));
mask = new BYTE[size];
assert(mask);

for(i=1; i<n; ++i)
{
k = 1;
while(k < m && (i%primes[k] != 0))
++k;
if(i%primes[k] == 0) // 最小值即为2,3,5,7,11,13的倍数的段可以直接排除
continue;

p = i-last;
last = i;
kmax = (long)((maxn-i)/n);
for(k=m+1; k<=len; ++k)
ans[k] = (ans[k] + mprimes[k]*p)%primes[k];

// 重置mask数组为0
memset(mask, 0, sizeof(BYTE)*size);
for(k=m+1; k<=len; ++k)
{
p = primes[k];
if(ans[k] == 0)
min = p-1;
else
min = ans[k]-1;

// if((i>(maxn-maxn/n*n) && (min > maxn/n-2)) || (i<=(maxn-maxn/n*n) && (min > maxn/n-1)))
// {
// printf("min>size*8: i=%d, k = %d, min=%d, size=%d,prime=%d\n", i, k, min,size, primes[k]);
// continue;
// }

// 下面的汇编代码用来给所有的合数设置标志位
__asm
{
mov eax, mask
mov ebx, kmax
mov ecx, min
mov edx, p
$001:
bts [eax], ecx // 置mask数组的第min位为1
add ecx, edx // min位置指向下一个合数
cmp ecx, ebx // 是否到了kmax位置
jl $001 // 不到则转$001位置
}
}

// 去掉本段内所有的合数的个数
for(k=0; k<size; ++k)
{
assert(k <= size);
kmax -= bits[mask[k]];
}
// 加上本段内的素数个数
r += kmax;
if(i/n > dot/dots)
{
++dot;
putchar('.');
}
}

delete []mask;

putchar('\n');
}

int main()
{
long start, end, t;

printf("Input a range: ");
scanf("%I64d", &maxn);

start = clock();
init();
end = clock();
printf("Init time=%lf\n", (double)(end-start)/1000);

start = clock();
makeprimes();
end = clock();

t = end-start;
printf("sum=%ld, cacl time=%lf\n", r, (double)t/1000);

return 0;
}

结果:
Input a range: 10000000000
Init time=0.032000

sum=455052511, cacl time=18.578000
Press any key to continue

#include "iostream"
#include "windows.h"
using namespace std;

unsigned int prime(unsigned int lmax)
{
bool* prime_num = new bool[lmax];
memset(prime_num, true, sizeof(bool) * lmax);

for (unsigned int i = 2; i < lmax; ++i)
{
if (prime_num[i])
for (unsigned int j = 2; i * j < lmax; ++j)
prime_num[i * j] = false;
}

unsigned int pnum = 0;
for (unsigned int i = 2; i < lmax; ++i)
{
if (prime_num[i])
++pnum;
}

delete[] prime_num;
return pnum;
}

int main()
{
int a = 1000000000;
DWORD s = GetTickCount();
unsigned int pnum = prime(1000000000);
float use_time = ((float)(GetTickCount() - s)) / 1000.f;

cout<<"1000000000 以内有素数个数 : "<<pnum<<endl;
cout<<"用时 : "<<use_time<<"秒"<<endl;

return 0;
}

1000000000 以内有素数个数 : 50847534
用时 : 353.641秒
请按任意键继续. . .

前段日子我也在做素数,下面是我的一个总结,看到那个2秒搞定的,我还真想去看看

当数字小于1000000时,可以用简单的判断

C/C++ code
int isprime(int n)
{
int i;
for(i=2;i<=sqrt(n);i++)
if (n%i==0)
return 0;
return 1;
}

但当数值在1000000到100000000时
介绍一种方法
prime={2,3,5,7,11,13,17,……}为事先做好的素数表
如果需要判断的数最大为100000000,则prime的最大元素为大于10000的最小素数即可

C/C++ code
//素数表生成法:
int t[10010]={0},prime[3000];//3000可能有点大,具体多少运行了,就知道了
int len;
void getprime(void)
{
int i,j;
t[0]=t[1]=1;          //t[i]=1,表示该数不是素数,被筛除
for(i=2;i  <=sqrt(10010);i++)
{
if(!t[i])
{
for(j=i+i;j <10010;j+=i)
t[j]=1;
}
}
len=0;
for(i=2;i  <10010;i++)
{
if(!t[i])
prime[len++]=i;
}
}

int isprime(long n)
{
int i;
while(prime[i]*prime[i]  <=n)
{
if(n%prime[i]==0)
return 0;
i++;
}
return 1;
}


如果数值大于100000000时
可以用Miller-Rabbin素数测试法,判断是否为素数

C/C++ code
int Miller_Rabbin(long long n)
{
long long i,s,a;
s=10; //s的值可以根据需要变大
// randomize();
for(i=0;i <s;i++)
{
a=long long(rand()%(n-1)+2); //自动生成受限
if(modular_exp(a,n-1,n)>1)
return 0;
}
return 1;
}
long long modular_exp(long long a,long long b,long long c)//求a^b%c该函数受限
{
if(a==0)
return 0;
if(b==0)
return 1;
if(b==1)
return a%c;
return (a*modular_exp(a,b-1,c))%c;
}

刷选法:

#include <stdio.h>
#include <dos.h>

typedef struct node
{
unsigned long i;
struct node *next;
} TPm;

void main()
{
struct time tm;
unsigned long i,n;
bool q;
TPm *p,*t,*t1,*t2;
printf( "Input N: ");
scanf( "%lu ",&n);
gettime(&tm);
printf( "The program started at %02d:%02d:%02d.%02d\n\n ",tm.ti_hour, tm.ti_min, tm.ti_sec, tm.ti_hund);
p=new TPm;
t=new TPm;
p-> i=2;
p-> next=t;
t-> i=3;
t-> next=NULL;
for(i=5;i <=n;i+=2)
{
t=p;
q=true;
while(t)
{
if(!(i%t-> i))
{
q=false;
break;
}
t1=t;
t=t-> next;
}
if(q)
{
t2=new TPm;
t2-> i=i;
t2-> next=NULL;
t1-> next=t2;
printf( "%lu\t ",i);
}
}
t=p;
i=0;
while(t)
{
i++;
p=t;
// printf( "%lu\t ",t-> i);
t=t-> next;
delete p;
}
printf( "\n\nTotal: %lu\n ",i);
gettime(&tm);
printf( "The program stopped at %02d:%02d:%02d.%02d\n ",tm.ti_hour, tm.ti_min, tm.ti_sec, tm.ti_hund);

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: