求第100,000个质数的C++算法

janchin 2009-10-15 10:50:03
Attention:是第第第100,000个,不是100,000以内!!!!
貌似这个数很大啊~这是金山的笔试题目,怎么算?说出大致思想也成,拜谢了!遍历肯定是不行的吧?虽然我答案就是用写的遍历-_-""!
...全文
1685 15 打赏 收藏 转发到动态 举报
AI 作业
写回复
用AI写文章
15 条回复
切换为时间正序
请发表友善的回复…
发表回复
leesaiya 2011-06-30
  • 打赏
  • 举报
回复
变量n是用来保存个数的,稍微改一下就能回答你的问题了!
这个对于大数,算起来很慢,需用别的算法!
leesaiya 2011-06-30
  • 打赏
  • 举报
回复
我的网站http://www.evecalm.com
	#include <iostream>
#include <fstream>
#include <cmath>
using namespace std;

int main()
{
int m;
int n=0;
//vector<int> prime;
char ch;
fstream fp;
cout<<"What prime numbers do you want get within? ";
if((cin>>m)==0)
{
cout<<"Bad input! Please try agin!\n";
return 1;
}
if(m<2)
{
cout<<"There are no prime numbers within "<<m<<endl;
return 0;
}
else if(m==2)
{
fp.open("prime.txt",ios::in|ios::out|ios::trunc);
fp<<"There are only 1 prime number within 2.\n";
fp<<"2\n";
fp.close();
cout<<"Congratulations! It has worked out!\n";
return 0;
}
else
{
int j;
int sq;
fp.open("prime.txt",ios::in|ios::out|ios::trunc);
fp<<"2\t\t";
n++;
for(int i=3;i<=m;i+=2)
{
sq=static_cast<int>(sqrt(i))+1;
fp.seekg(0,ios::beg);
fp>>j;
for(;j<sq;)
{
if(i%j==0)
{
break;
}

else
{
if((fp>>j)==NULL)
{
j=3;
}
}
}
if(j>=sq)
{
fp.seekg(0,ios::end);
fp<<i<<"\t\t";
n++;
if(n%4==0)
fp<<'\n';
}
}
fp.seekg(0,ios::end);
fp<<"\nThere are only "<<n<<" prime number within "<<m<<".\n";
fp.close();
cout<<"Congratulations! It has worked out!\n";
return 0;
}
}
mgw2314 2010-10-19
  • 打赏
  • 举报
回复

#include <iostream>
#include <iomanip>
using namespace std;
int main(){
int max;
cout<<"请输入要显示的个数:";
cin>>max;
long* prime=new long[max];
int count=1;
*prime=2;
int trial=3;
while(count<max){
for(int i=0;i<count;i++)
if(trial%*(prime+i)==0){
trial+=2;
i=0;
}
*(prime+count++)=trial;
trial+=2;
}
for(int i=0;i<max;i++){
if(i%5==0)
cout<<endl;
cout<<setw(5)<<*(prime+i);
}
cout<<endl;
}
mLee79 2009-10-17
  • 打赏
  • 举报
回复
10W很小,啥办法都可以,10G就小有挑战了。。。

zhengjiankang 2009-10-17
  • 打赏
  • 举报
回复
之前找出来的素数可以用数组存起来,在判断后1个数是否为素数的时候可以从两个方面去约束,用来检验n是否为素数的数:1:不大于n^0.5的数,2:之前已经找出来储存起来的数,,所以感觉找起来也不是特别复杂的哈。
baihacker 2009-10-16
  • 打赏
  • 举报
回复

//暂时只给第一种的实现,大家可以测试一下在自己电脑上跑多少时间.
#include <cstdio>
#include <cstring>
using namespace std;

const int MAX_PRIME = 2000000;

int PrimeTable[6000000];
int IsPrime[MAX_PRIME+1];
int PrimeCnt = 0;

void get_prime()
{
memset(IsPrime, 1, sizeof(IsPrime));
IsPrime[0] = IsPrime[1] = 0;
for (int i = 2 ; i < MAX_PRIME; i++)
if (IsPrime[i])
{
for (long long j = (PrimeTable[PrimeCnt++] = i) * (long long)i; j < MAX_PRIME; j += i)
IsPrime[j] = 0;
}
}

int main()
{
get_prime();
printf("%d\n", PrimeTable[99999]);
return 0;
}
baihacker 2009-10-16
  • 打赏
  • 举报
回复
两种方法:
1.就是暴力,筛选出第十万个
已经知道:
1百以内:25 97
1千以内:168 997
1万以内:1229 9973
10万以内:9592 99991
100万以内:78498 999983
1000万以内:664579 9999991
所以,大概在200万以内筛选就OK了.
(筛选1亿以内的素数大概是不超过10秒(我的计算机), 100万差不多是瞬间)

2.二分枚举

记函数search(n)的功能为计算n以内的素数的个数
那么
int l = 1, r = max;
while (l <= r)
{
int mid = (l + r) >> 1;
if (search(mid) >= 100000) r = mid - 1;
else l = mid + 1;
}
l为所求.

现在问题的关键在于高效计算search(n),可以用容斥原理...
pcboyxhy 2009-10-16
  • 打赏
  • 举报
回复
目前性能比较好的生成素数表的方法是筛数法
测试单个素数性能比较好的方法是mr伪素数测试
James__Zhan 2009-10-15
  • 打赏
  • 举报
回复
答案:1299709.
一个素数为素数的条件是不能被它之前的任何素数给整除,事实上,还有一个条件,假设n为合数,则存在除一和它自身的两个数a,b,使得a×b=n,两个数之中必定有一个小于根号n,一个大于根号n,故此,可以得出结论,如果一个数n不能被小于根号n的所有输整除,则此数是素数。算法如下,不过是Java的。希望能满足你的要求。

public static void main(String[] args)
{
int len = 100000;
int[] arr = new int[len];
arr[0] = 2;
int m = 3;
int i = 1;
while(i < len){

boolean isLeap = true;
for(int j = 0; j < i && arr[j] < Math.sqrt(m) + 1; j++){
if(m % arr[j] == 0){
isLeap = false;
break;
}
}
if(isLeap){
arr[i++] = m;
}
m += 2;
}

System.out.println(arr[len - 1]);

}
pcboyxhy 2009-10-15
  • 打赏
  • 举报
回复
[Quote=引用 4 楼 la_feng 的回复:]
ls的算过那个数是否是unsigned int能表示的没有,我想估计表示不了吧,随着数的增大,素数越来越少的
[/Quote]

一个复数域上的函数 - Riemann ζ 函数 - 的非平凡零点 (在无歧义的情况下我们有时将简称其为零点) 的分布怎么会与风马牛不相及的自然数域中的素数分布产生关联呢?这还得从 Euler 乘积公式 谈起。

我们知道, 早在古希腊时期, Euclid 就用精彩的反证法证明了素数有无穷多个。 随着数论研究的深入,人们很自然地对这些素数在自然数域上的分布产生了越来越浓厚的兴趣。 1737 年, 著名数学家 Leonhard Euler (1707-1783) 在圣彼得堡科学院 (St. Petersburg Academy) 发表了一个极为重要的公式,为数学家们研究素数分布的规律奠定了基础。 这个公式就是 Euler 乘积公式:

∑n n-s = ∏p(1-p-s)-1

公式中左边的求和对所有的自然数进行,右边的连乘积则对所有的素数进行。 可以 证明, 这个公式对所有 Re(s)>1 的复数 s 都成立。这个公式的左边正是我们在 上文 中介绍过的 Riemann ζ 函数, 而右边则是一个纯粹有关素数 (且包含所有素数) 的表达式, 这样的形式正是 Riemann ζ 函数与素数分布之间存在关联的征兆。那么这个公式究竟蕴涵着有关素数分布的什么样的信息呢? Riemann ζ 函数的零点又是如何出现在这种关联之中的呢?这就是本节及未来几节所要介绍的内容。

Euler 本人率先对这个公式所蕴涵的信息进行了研究。 他注意到在 s=1 的时候, 公式的左边 ∑n n-1 是一个发散级数 (这是一个著名的发散级数, 称为调和级数), 这个级数以对数方式发散。这些对于 Euler 来说都是不陌生的。 为了处理公式右边的连乘积,他对公式两边同时取了对数, 于是连乘积变成了求和, 由此他得到:

ln (∑n n-1) = -∑p ln(1 - p-1) = ∑p (p-1 + p-2/2 + p-3/3 + ... ...)

由于上式右端括号中除第一项外所有其它各项的求和都收敛,而且这些求和的结果累加在一起仍然收敛 (有兴趣的读者不妨自己证明一下)。 因此右边只有第一项的求和是发散的。由此 Euler 得到了这样一个有趣的渐近表达式:

∑p p-1 ~ lnln(∞)

或者, 更确切地说:

∑p<N p-1 ~ lnln(N)

这个结果 - 即 ∑p p-1 以 lnln(N) 的方式发散 - 是继 Euclid 证明素数有无穷多个以来有关素数的又一个重要的研究结果。它同时也是对素数有无穷多个这一命题的一种崭新的证明 (因为假如素数只有有限多个, 则求和就只有有限多项, 不可能发散)。 但 Euler 的这一新证明所包含的内容要远远多于 Euclid 的证明,因为它表明素数不仅有无穷多个,而且其分布要比许多同样也包含无穷多个元素的序列 - 比如 n2 序列 - 密集得多 (因为后者的倒数之和收敛)。 不仅如此,如果我们进一步注意到上式的右端可以改写为一个积分表达式:

lnln(N) ~ ∫ x-1ln-1(x) dx

而左端通过引进一个素数分布的密度函数 ρ(x) - 它给出在 x 附近单位区间内发现素数的几率 - 也可以改写为一个积分表达式:

∑p<N p-1 ~ ∫ x-1ρ(x) dx

将这两个积分表达式进行比较, 不难猜测到素数的分布密度为 ρ(x)~1/ln(x), 从而在 x 以内的素数个数 - 通常用 π(x) 表示 - 为:

π(x) ~ Li(x)

其中 Li(x) ≡ ∫ ln-1(x) dx 是对数积分函数[注一]。这正是著名的素数定理 (当然这种粗略的推理并不构成对素数定理的证明)。因此 Euler 发现的这个结果可以说是一扇通向素数定理的暗门。 可惜 Euler 本人并没有沿着这样的思路走, 从而错过了这扇暗门,数学家们提出素数定理的时间也因此而延后了几十年。



素数分布与素数定理


提出素数定理的荣誉最终落到了另外两位数学家的肩上:他们是德国数学家 Friedrich Gauss (1777-1855) 和法国数学家 Adrien-Marie Legendre (1752-1833)。

Gauss 对素数分布的研究始于 1792 到 1793 年间, 那时他才 15 岁。在那期间, 每当“无所事事” 的时候 Gauss 就会挑上几个长度为一千的自然数区间, 计算这些区间中的素数个数,并进行比较。 在做过了大量的计算和比较后, Gauss 发现素数分布的密度可以近似地用对数函数的倒数来描述, 即 ρ(x)~1/ln(x), 这正是上面提到的素数定理的主要内容。 但是 Gauss 并没有发表这一结果。 Gauss 是一位追求完美的数学家,他很少发表自己认为还不够完美的结果,而他的数学思想与灵感犹如浩瀚奔腾的江水, 汹涌激荡,常常让他还没来得及将一个研究结果完美化就又展开了新课题的研究。 因此 Gauss 一生所做的数学研究远远多过他正式发表的。 但另一方面, Gauss 常常会通过其它的方式 - 比如书信 - 透露自己的某些未发表的研究成果,他的这一做法给一些与他同时代的数学家带来了不小的尴尬。 其中 “受灾” 较重的一位便是 Legendre。 这位法国数学家在 1806 年率先发表了线性拟合中的最小平方法, 不料 Gauss 在 1809 出版的一部著作中提到自己曾在 1794 年 (即比 Legendre 早了 12 年) 就发现了同样的方法, 使 Legendre 极为不快。

有道是: 不是冤家不聚首。 在素数定理的提出上, 可怜的 Legendre 又一次不幸地与数学巨匠 Gauss 撞到了一起。 Legendre 在 1798 年发表了自己关于素数分布的研究,这是数学史上有关素数定理最早的文献[注二]。由于 Gauss 没有发表自己的研究结果, Legendre 便理所当然地成为了素数定理的提出者。 Legendre 的这个优先权一共维持了 51 年。 但是到了 1849 年, Gauss 在给德国天文学家 Johann Encke (1791-1865) 的一封信中提到了自己在 1792 至 1793 年间对素数分布的研究, 从而把尘封了半个世纪的优先权从 Legendre 的口袋中勾了出来, 挂到了自己已经鼓鼓囊囊的腰包上。

幸运的是, Gauss 给 Encke 写信的时候 Legendre 已经去世十六年了,他用最无奈的方式避免了再次遭受残酷打击。

无论 Gauss 还是 Legendre,他们对于素数分布规律的研究都是以猜测的形式提出的 (Legendre 的研究带有一定的推理成份, 但离证明仍然相距甚远)。 因此确切地说,素数定理在那时只是一个猜想 - 素数猜想,我们所说的提出素数定理指的也只是提出素数猜想。素数定理的数学证明直到一个世纪之后的 1896 年, 才由法国数学家 Jacques Hadamard (1865-1963) 与比利时数学家 Charles de la Vallée-Poussin (1866-1962) 彼此独立地给出。 他们的证明与 Riemann 猜想有着很深的渊源, 其中 Hadamard 的证明出现的时机和场合还有着很大的戏剧性, 这些我们将在 后文 中加以叙述。

素数定理是简洁而优美的,但它对于素数分布的描述仍然是比较粗略的,它给出的只是素数分布的一个渐近形式 - 也就是当 N 趋于无穷时的分布形式。从前面有关素数分布与素数定理的图示中我们也可以看到, π(x) 与 Li(x) 之间是有偏差的, 而且这种偏差的绝对值随着 x 的增加似有持续增加的趋势 (所幸的是, 这种偏差的增加与 π(x) 及 Li(x) 本身的增加相比仍是微不足道的 - 否则素数定理也就不成立了)[注三]。

那么有没有一个公式可以比素数定理更精确地描述素数的分布呢?这便是 Riemann 在 1859 年想要回答的问题。 那一年是 Gauss 去世后的第五年, 32 岁的 Riemann 继 Johann Dirichlet (1805-1859) 之后成为了 Gauss 在 Göttingen 大学的继任者。同年八月十一日, 他被选为柏林科学院 (Berlin Academy) 的通信院士 (Corresponding Member)。 作为对这一崇高荣誉的回报, Riemann 向柏林科学院提交了一篇论文。 这是一篇只有短短八页的论文, 标题是:论小于给定数值的素数个数。 正是这篇论文将 Euler 乘积公式 蕴涵的信息破译得淋漓尽致, 也正是这篇论文将 Riemann ζ 函数的零点分布与素数的分布联系在了一起。
liushuiyou_sheng 2009-10-15
  • 打赏
  • 举报
回复

楼上还是老算法啊。
la_feng 2009-10-15
  • 打赏
  • 举报
回复
ls的算过那个数是否是unsigned int能表示的没有,我想估计表示不了吧,随着数的增大,素数越来越少的
pcboyxhy 2009-10-15
  • 打赏
  • 举报
回复
稍作修改,更快一些

#include <stdio.h>
#include <math.h>

unsigned int s[100000] = {2};
unsigned int s_i = 1;

inline int is_sushu(unsigned int n)
{
int m = sqrt(n)+1;
for (int i=0; s[i]<=m; i++) {
if (i==s_i)
return 1;
if (n%s[i]==0)
return 0;
}
return 1;
}

int main()
{
for (int i=3; s_i!=100000; i+=2)
if (is_sushu(i))
s[s_i++] = i;
printf("%d\n", s[99999]);
}
pcboyxhy 2009-10-15
  • 打赏
  • 举报
回复
这个数据量很小啊,最朴素的穷举法就行了
代码正确性不保证,自己验证

#include <stdio.h>
#include <math.h>

unsigned int s[100000] = {2};
unsigned int s_i = 1;

int is_sushu(unsigned int n)
{
int i = 0;
int m = sqrt(n)+1;
for (i=0; i<=m; i++) {
if (i==s_i)
return 1;
if (n%s[i]==0)
return 0;
}
return 1;
}

int main()
{
int i;
for (i=3; ; i++) {
if (is_sushu(i))
s[s_i++] = i;
if (s_i>=100000)
break;
}
printf("%d\n", s[99999]);
}
wensheng_zh2007 2009-10-15
  • 打赏
  • 举报
回复
期待高手出现,出从头遍历之外还有什么办法?
但是增加的步长可以是2,从3开始起。

65,186

社区成员

发帖
与我相关
我的任务
社区描述
C++ 语言相关问题讨论,技术干货分享,前沿动态等
c++ 技术论坛(原bbs)
社区管理员
  • C++ 语言社区
  • encoderlee
  • paschen
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
  1. 请不要发布与C++技术无关的贴子
  2. 请不要发布与技术无关的招聘、广告的帖子
  3. 请尽可能的描述清楚你的问题,如果涉及到代码请尽可能的格式化一下

试试用AI创作助手写篇文章吧