筛法的应用

                                   筛法的应用
一.用筛法求指定区间上的所有素数,并统计素数的个数.
   筛法: 这是一种效率较高的算法.
   求素数的筛法是公元前3世纪的厄拉多塞(Eratosthenes)提出来的:对于一个大整数X,只要知道不超过   sqrt(X)的所有素数,划去所有p的倍数2p,3p,...剩下的整数就是不超过X的全部素数.
   应用筛法求素数,为了方便实施"划去"操作,应设置数组.每一数组元素对应一个待判别的奇数,并赋初值0.如果该奇数为p的倍数则应划去,对应元素加一个划去标记,通常给该元素赋值-1.最后,打印元素值不是-1(即没有划去)的元素对应的奇数即所求素数.
   在实际应用筛法的过程中,p通常不限于取不超过sqrt(x)的素数,而是适当放宽取不超过sqrt(x)的奇数(从3开始),这样做尽管多了一些重复划去操作,但程序实现要简便些,而且只用考虑所取p的奇数倍(p的偶数倍肯定不是素数).
   应用筛法求素数的程序设计
   在指定区间[c,d](约定c为奇数)上所有奇数表示为 j=c+2k,(k=0,1,2,3,...e,这里e=(d-c)/2.).于是k=(j-c)/2是奇数j在数组中的序号(下标).如果j为奇数的倍数时,对应数组元素作划去标记,即a[(j-c)/2]=-1.最后,凡数组元素a[k]不等于-1,对应的奇数j=c+2k则为素数.
 
 /*筛法求指定区间上的素数程序*/
    #include<iostream>
#include<cmath>
using namespace std;
int main()
{
  long int c,d,i,j,k;
  static long int a[11000];
  int e,n;
  cout<<"求区间[c,d]上的素数"<<endl;
  cout<<"请输入c,d(c>2):"<<endl;
  cin>>c>>d;
  if(c%2==0)
   c++;
  e=(d-c)/2;
  i=1;
  while(i<=sqrt(d))  /*在[c,d]中筛选素数*/
  {
   i+=2;
   j=i;
   while(j<=d)
   {
    j+=2*i;  /*计算3*i,5*i,7*i.... */
    if(j>=c)
      a[(j-c)/2]=-1; /*筛去标记-1*/
   }
  }
   for(n=0,k=0;k<=e;k++)
    if(a[k]!=-1)
    {
     n++;
     cout<<c+2*k<<" ";
     if(n%10==0)
       cout<<endl;
    }   
  cout<<endl<<"共有素数"<<n<<endl;

  return 0;
}
                            筛法求指定区间上的素数程序的改进
  当区间的下限c较大时,上述程序还得从3*3,3*5,3*7,...做是否筛选判别,做了大量无效操作,显然是不必要的.
  作为改进的一个方面,根据c与奇数i,确定 g=2*int(c/(2*i))+1,使得g*i接近区间下限c,从而使划去的g*i,(g+2)*i
  大体在[c,d]中,减少无效操作,以提高对大区间的筛选效率.
  对上述程序作以下改进:
 while(i<=sqrt(d))  /*在[c,d]中筛选素数*/
  {
   i+=2;
   g=2*(c/(2*i))+1;
   if(g*i>d)
     continue;
   if(g==1)
    g=3;
   j=i*g;
         while(j<=d)
   {
  
    if(j>=c)
      a[(j-c)/2]=-1; /**/
    j+=2*i;  /*计算3*i,5*i,7*i.... */
   }
  }

 


二.筛法的又一应用.题目来自浙江大学在线ACM题库,题目编号:2095
Divisor Summation


Give a natural number n (1 <= n <= 500000), please tell the summation of all its proper divisors.
Definition: A proper divisor of a natural number is the divisor that is strictly less than the number.

e.g. number 20 has 5 proper divisors: 1, 2, 4, 5, 10, and the divisor summation is: 1 + 2 + 4 + 5 + 10 = 22.

 

Input

An integer stating the number of test cases, and that many lines follow each containing one integer between 1 and 500000.


Output

One integer each line: the divisor summation of the integer given respectively.


Sample Input

3
2
10
20


Sample Output

1
8
22

具体算法如下:
//筛法,定义了一个 500001 的int数组,
//初始化为0,然后,从1开始,每个向大处循环,
//遇到一个数的倍数就把数组中的数加上该数,直到最大的数(250000)。
//然后来一个打印一个
#include<iostream.h>
#include<string.h>
long a[500001];
int main()
 {  int i,j,k,N,n;
    memset(a,0,sizeof(a));
    for(i=1;i<=250000;i++)
      {
          k=2;
          while(k*i<=500000)
            {
                a[k*i]+=i;
                k++;
            }
      }
      cin>>N;
      for(i=1;i<=N;i++)
        {
            cin>>n;
            cout<<a[n]<<endl;
        }
     return 0;
 }                             
           

 三。

例:

 

传说中有一个残暴的国王,喜欢杀戮百姓。有一次,他抓到30个百姓并要一一杀掉。在这30个百姓中间有一个聪明人,他站出来对国王说:“请国王大发慈悲,赦免二人不死。”国王问:“赦免哪二人不死?”那个聪明人回答说:“我们30个人围成一圈,从1开始报数,凡数到5的人就拉出去杀掉。剩下的人继续从1开始报数,循环反复,直到剩下两个人为止,这两个人被赦免。”

国王一听很有意思,采纳了聪明人的建议,赦免了两个人,而那个聪明人就是其中之一。请你设计一个程序,由计算机判断聪明人要站在什么位置,才能躲过这一场屠杀

这到题其实就是约瑟夫环问题的一个特例。

首先,设百姓的人数为m人,设数到n的人被杀掉。
用数组r[m]存放m个人是否还在圈中的信息。其中,
r[i]=1 表示第i个人还在圈中。
r[i]=0 表示第i个人已被杀掉。

开始时,数组r中所有的元素都是1,表示每个人都站在圈中

void check()
{
  int i=0,k=0,j=0;//j为统计的出圈人数,j=m-2时退出循环
  while(j!=m-2)
   {
     k++;
     if(k>m) k=1;
     if(r[k]==1)
       {
             i++;
             if(i%n==0)
                {
                  r[k]=0; j++
                }
        }
   }
}

for(i=1;i<=m;i++)
 if(r[i]==1)
  cout<<i<<endl;

四:

请你设计一个程序,让计算机找出40个自然数来,使得其中任意两个数之差均不相等。

问题分析:

首先,开辟一个数组S(I),准备存放这40个数,再开辟一个数组CHA(I),用来存放两个数的差。

寻找某一个满足条件的自然数的过程如下:

把1和2放进数组S中;
把1放进数组CHA中;
当寻找下一个自然数时,要把这个自然数与数组S中的每一个数相减,再判断所得的差是否在数组CHA中;
如果所得的差不在数组CHA中,说明又找到一个满足条件的自然数。把这个自然数放进数组S中,同时把这个自然数与数组S中原有的每一个自然数的差记录在数组S中去。
如果所得的差与数组CHA中的某一个数重复,说明这个自然数不符合条件,继续寻找下一个自然数。
重复步骤(3),直到找到40个自然数为止。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值