`
duoerbasilu
  • 浏览: 1479643 次
文章分类
社区版块
存档分类
最新评论

趣味的数

 
阅读更多

趣味的数

(1)素数

素数的定义:只能被1和自己本身整除的数叫素数。那么应该如何判断一个数n(n为正整数)是否为素数呢?

一,定义法


既然只能被被自己和本身整除那么我们就用枚举,让数n除以2到n-1若有一次可以被整除,显然违反了素数的定义。反之则为素数。代码实现如下:

#include<stdio.h>
//判断n是不是素数,是返回true,否则返回false;
bool is_prime(int n)
{
     inti = 0;
     if(n==0 || n==1)
     {
         returntrue;
     }
     for(i=2; i<n; i++)
     {
         if(n%i == 0)
         {
              returnfalse;
         }
     }
     returntrue;
 
}
int main()
{
     intn = 0;
     while(scanf("%d",&n)!=EOF)
     {
         if(is_prime(n))
         {
              printf("%dis prime.\n",n);
         }
         else
         {
              printf("%dis not prime.\n",n);
         }
     }
     return0;
}

优化一


我们思考这么一个问题,一个数n的最大的约数和最小的约数分别是什么?很明显就是1和n(也就是他自己本身)。其实在看看素数的定义我们会发现其实素数就是只有两个约数的数,而任何一个数至少有两个约数(它们分别是1和它本身),也就是一个数的最大约数和最小约数对吧。那么我们想象一个数的次(第二)大约数和次小约数是什么呢?ok,有一个很明显那就是次小约数是2(当然1和0这两个特殊情况不在我们考虑范围之内),但是次大约数好像就不是那么明显了,这是为什么呢?其实因为我们每一个数不一定能被2整除,那么自然这个次大约数就不知道了,但是我们可以得出下面这个结论:

任何数n的次大约数小于或等于n/2。

那么我们可以利用这个结论让我is_prime(int n)函数运算的得快一倍,在上面这个函数我们枚举了(2~n-1),而现在我们只需要枚举(2~n/2)了。是不是快了一倍啊。代码实现很简单如下:

//判断n是不是素数,是返回true,否则返回false;
bool is_prime(int n)
{
     inti = 0;
     if(n==0 || n==1)
     {
         returntrue;
     }
     for(i=2; i<=n/2; i++)
     {
         if(n%i == 0)
         {
              returnfalse;
         }
     }
     return true;
}


优化二


其实还有这么一个规律:任何数n的约数都有大约数集和小约数集之分。我们加入数n存在约数1那么n/1=n,n也是n的约数。其实很明显任何一个数的约数都是成对的。如:12有(3-4)这个约数对(因为3×4=12),当然向3*3=9这种特殊情况除外,我们称约数对中小的那个数叫小约数,包含所有小约数的集合叫小约数集,约数对中大的数大约数,包含所有大约数的集合大小约数集。其中小约数集中最大的约数我们叫最小最大约数。那么12有约数对(1-12),(2-6),(3-4)。其中小约数集为1,2,3。而大约数集为4,6,12. 12的最小最大约数是3. 这样我们会发现下面这条规律:

任何数n(除了能开2次方的数)的约数都是成对出现的。且任何数(包括能开2次方的数)的最小最大约数小于或等于i(其中i满足i*i=n)

那么我们可以进一步减少枚举的数的个数,代码实现如下:

//判断n是不是素数,是返回true,否则返回false;
bool is_prime(int n)
{
     int i = 0;
     if (n==1 || n==0)
     {
         return true;
     }
     for (i=2; i*i<=n; i++)
     {
         if (n%i == 0)
         {
              return false;
         }
     }
     return true;
}


优化三

当然还有其他的素数的测试方法,有点难,要用到很强的数论知识,不好证明。在此省略,有兴趣的同学可以参考其他资料。其中《算法导论》数论那一章就详细的介绍了。网上也有 不少好的博客哦,下面是

techq's blog的博客大素数测试http://blog.csdn.net/techq/article/details/6125696写得不错可以参考下下。

(2)最大公约数

本节默认讨论的都是非负整数集的最大公约数。最大公约数的定义就省略了。下面介绍:

GCD递归定理:

gcd(a,b)=gcd(b,a%b)。gcd(a,b)就是a,b最大公约数。

l       欧几里得算法
int gcd(int a,int b)
{
     if(b == 0)
     {
         returna;
     }
     returngcd(b, a%b);
}


简单吧!

->课后思考???

我们都知道递归会消耗更多系统资源,甚至可能造成栈溢出。综合考虑我们还是决定将上面递归式给成迭代,这个任务就叫给你了?????

欧几里得算法推广

我们推广欧几里德算法计算满足下列条件的整数系x和y:d=gcd(a,b)=ax+by。

下面是该算法的伪代码:

gcd_extend(a,b)

1,if b=0

2,    then return (a,1,0)

3,(d',x',y')<-gcd_extend(b,a%b)

4,(d,x,y)<-(d',y',x'-a/b*y')

5,return(d, x, y)

C语言实现:

struct Node{
     intd;
     intx;
     inty;
};
struct Node gcd_extend(int a,int b)
{
     structNode node,temp;
     if(b == 0)
     {
         node.d= a;
         node.x= 1;
         node.y= 0;
         returnnode;
     }
     temp= gcd_extend(b, a%b);
     node.d= temp.d;
     node.x= temp.y;
     node.y= temp.x-a/b*temp.y;
 
     returnnode;
}


->课后思考???

题目内容:

求一堆数的最大公约数

输入描述:

先输入m,代表测试数据的组数。在每一组先输入n(n<=100),表示这次要求的这堆数的个数,后面跟着n个数,n都为正整数。

输出描述:

输出他们的最大公约数。

输入样例:

3

2 1 2

3 1 2 3

6 4 2 8 12 24 68

输出样例:

1

1

2

解题策略:

我们只要明白下面这个简单的例子就可以了,看看这个定理gcd(a,b,c)=gcd(gcd(a,b),c)。思考一下!!!是不是对的?有了它无论我们求多少个数的最大公约数,我们都能求出来了。

C++实现如下:

#include<iostream>
using namespace std;
const int N = 100;
//求a,b的最大公约数
int gcd(int a, int b)
{
     if(b == 0)
     {
         returna;
     }
     returngcd(b, a%b);
}
int main()
{
     inta[N];
     inti =0,j = 0;
     intm,n;
     cin>>m;
     while(i<m)
     {
         cin>>n;
         j= 0;
         while(j<n)
         {
              cin>>a[j];
              ++j;
         }
 
         int gc = gcd(a[0],a[1]);
         j= 2;
         while(j<n)
         {
              gc= gcd(gc, a[j]);
              ++j;
         }
         cout<<gc<<endl;
         ++i;
     }
     return0;
}


当然问题也就来,你觉得上面的方法可以优化吗?或者你有更好的想法????

参考书目《算法导论》

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics