《算法基础》——2.4 有关素数的运算

2.4 有关素数的运算

众所周知,一个素数是一个大于1的自然数(一个比0大的整数),它的因数只有1和它本身。一个合数是一个大于1并且非素数的自然数。
素数在某些应用中起到重要的作用,在这些应用中素数的特质使得它们能够使特定程序变得简单或繁琐。例如,某些加密程序使用两个大素数的积来保证安全性。将一个由两个大素数相乘得到的数分解成两个大素数是十分困难的,而正是这个事实保证了算法的安全性。
以下章节将讨论一些处理素数的常见算法。
2.4.1 寻找素数因子
寻找一个数的素因子最显而易见方法是尝试将这个数用比其小而大于等于2的整数试除。当一个可能的因子将这个数整除时,保存这个因子,用该因子除这个数,然后继续尝试更多的因子。注意,每次试除需要将同一个因子再重复除一次,以防这个数含有多于一个该因子。
例如,为了找到127的素因子,需要将127被2、3、4、5…除,直到126。
下列伪代码展示了这个算法:

如果这个数是N,那么这个算法的运行时是O(N)。
通过三个重要注意事项可以来极大地改进这个方法:
不需要检验这个数是否能够被任何一个除了2以外的偶数整除,因为如果它能够被任意偶数整除,则其一定能够被2整除。这意味着只需要检验2和奇数是否能整除该数,而不用检查所有可能因数。这样做能够将运行时间减半。
仅需要检查不大于待检测数字平方根的因子。如果n=p×q,p或q一定要小于等于sqrt(n)。(如果这两个数都比sqrt(n)大,它们的积一定比n大。)如果检查了比sqrt(n)小的可能因子,若发现其中较小因子,而用这个较小因子整除n时,则会发现另一个因子。这把运行时间降到了O(sqrt(n))。
每次用一个因子试除这个数时,可以更新需要检查的可能因子的上界。
这些注意事项产生了以下改进的算法:

注 这个素因数分解算法的运行时间复杂性为O(sqrt(N)),N是该算法正在进行素因数分解的数,因此当这个数比较小时,这个算法运行速度相当快。如果N变得相当大,甚至O(sqrt(N))也不够快。例如,如果N为100位,那么sqrt(N)就有50位。如果这个数恰好是一个素数,那么即使运行速度很快的计算机也不能在合理时间内尝试所有的可能因子。这就是加密算法保密性得以保证的原因。
2.4.2 寻找素数
假设程序需要提取一个大素数。(这是另一个加密算法所需要的程序。)一种找到这些素数的方法是使用之前章节介绍的算法来检验大量的数,以寻找其中的素数。这种方法只适用于较小的数,但是对于较大的数,这种算法过于缓慢。
埃拉托色尼筛法(the sieve of Eratosthenes)是一种找出给定范围内所有素数的简单方法。此方法适用于较小的数,因为它需要一个由所有考虑到的数构成的表。因此,当数过大时,这种方法会占用十分大的内存空间。
其基本思想是建立一个以2和上界之间所有数构成的表。删去所有2的倍数(不包括2本身)。然后,从2开始,检索该表来寻找下一个没有被删去的数(例如3)。删去所有这个数的倍数(不包括这个数本身)。注意,其中有些数也是2的倍数,它们已经被删去了。重复这一步,寻找下一个没有被删去的数,删去其所有倍数,直到计算到上界的平方根。这时所有没有被删去的数就都是素数了。
以下伪代码展示了该基本算法:

可以看到该算法的运行时间复杂度为O(N×log(log N)),这超出了这本书的讨论范围。
2.4.3素性测试
之前2.4.1节中介绍的算法将数分解成因子。一种确定某数是否为素数的方法是使用这个算法来尝试分解它。如果这个算法一个因子都找不到,那么该数为素数。
2.4.1节中谈到这个算法适用于较小的数。但是如果这个数有100位,这个程序需要执行的运算次数会达到50位。即使是最快的计算机也不能在合理的时间内完成如此多的运算。(一台每秒运算1万亿次的计算机需要用3×1030年来完成这个运算。)
一些加密算法需要使用大素数,因此这种检验某数是否为素数的方法将无法有效工作。幸运的是还有其他方法。费马素性测试就是其中一种较简单的方法。
费马小定理表明如果p是素数,并且1≤n≤p,np-1 Mod p=1。换句话说,如果将p-1个n相乘,再用所得的数除以p,结果为1.
例如,假设p=11,n=2,那么np-1 Mod p=210 Mod 11=1 024 Mod 111 024=11×93+1,因此1 024 Mod 11=1。
请注意即使p不是素数,np-1 Mod p=1也可能成立。在这种情况下n被称作费马骗子(Fermat liar)。因为它错误地暗示了p是素数。
如果np-1 Mod p≠1,那么n被称作费马证人(Fermat witness)。因为n证明了p不是素数。
可以证明,对于自然数p,至少1和p中的一半数n是费马证人。也就是说,如果p不是素数,并且如果随机抽取了1和p中的一个数n,有50%的可能性n是一个费马证人,因此np-1 Mod p≠1。
当然,也有时候会不走运,随机选择的一个n是费马骗子。如果重复试验进行多次,可以增加能够选择到一个费马证人的机会,当然前提是它存在的话。
可以看出,在每个测试中有50%的机会,选择到一个费马证人。所以,如果p通过k
次测试,那么就有的机会每次都得到费马骗子。换句话说,有的机会p实际上是
一个合数却假装是素数。
例如,如果p通过测试10次,就有1/210≈0.000 98概率为p不是素数。如果想更加肯定,重复测试100次。如果p通过了所有的100个项目,就只有1/2100≈7.8×10-31概率证p不是素数。
下面的伪代码描述了使用此方法来确定一个数是否可能是素数的算法:

注 这是一个概率算法(一个产生正确结果有一定的概率的算法)的例子。
目前仍然有一个很小的可能性证明该算法是错误的,但可以重复测试直到达到想要的任何确定性级别。
有一种情况会使这整个问题变得相当有趣,那就是如果数p是非常大的,计算NP-1使用乘法可能需要相当长的时间。
幸运的是,我们已经知道如何通过快速求幂做到这一点,算法参见2.3节中所描述的“执行幂”。
一旦我们知道了如何判断一个数是否(可能)是素数,就可以写一个算法来选择素数:

时间: 2024-08-02 20:15:45

《算法基础》——2.4 有关素数的运算的相关文章

《算法基础》——导读

**前言**算法是使高效的程序成为可能的方法.它们解释了如何排列记录.搜索项.计算数值(比如质因子分解).查找一个街道网络中的最短路径.确定可能通过通信网络的最大流.算法好坏的差别可能意味着是在一秒.一个小时内解决问题,还是永远也不能解决问题.学习算法使你能建立有用的方法工具来解决具体的问题.它能帮助你理解在不同的情况下,哪个算法是最有效的,所以对于一个特定的问题,你就能选择最适合的算法.对某些数据而言性能优异的算法可能对其他的数据而言表现糟糕.所以知道如何选择一个最适合当前情况的算法是很重要的

一道算法基础题 uva1586

问题描述 一道算法基础题 uva1586 题目链接在这儿 http://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&category=830&page=show_problem&problem=4461 我自己做的代码如下 但是通不过 测了好多数据都没问题 #include<cstdio> #include<cstring> using namespace std; in

《算法基础:打开算法之门》一1.1 正确性

1.1 正确性 产生问题的一个正确解决方案意味着什么呢?我们通常会精确地定义一个正确的解决方案涉及的内容.例如,为了寻找出最佳旅行路线,你的GPS会产生一个正确的解决方案.该方案可能是从你所在位置到目的地的所有可能路线中最快的路线,也可能是具有最短距离的路线,或者是能使你最快到达目的地同时也能免交过路费的路线.当然,你的GPS确定路线时所使用的信息可能不完全匹配实际情况.除非你的GPS能够获取实时路况信息,否则它可能假定穿过一条道路的时间等于道路的长度除以道路的限定时速. 然而,如果道路拥挤,当

java 算法基础之二快速排序算法

http://www.cnblogs.com/hexiaochun/archive/2012/09/03/2668324.html java 算法基础之二快速排序算法 所谓的快速排序的思想就是,首先把数组的第一个数拿出来做为一个key,在前后分别设置一个i,j做为标识,然后拿这个key对这个数组从后面往前遍历,及j--,直到找到第一个小于这个key的那个数,然后交换这两个值,交换完成后,我们拿着这个key要从i往后遍历了,及i++;一直循环到i=j结束,当这里结束后,我们会发现大于这个key的值

《算法技术手册》一第3章 算法基础

第3章 算法基础 虽说开发软件是为了解决问题,但程序员们往往太执着于是否能够解决问题本身,而不去确认这一问题是否已有解决之法.即便程序员们知道前人已在类似情况下解决了问题,但"已有的解决之法"最终是否适用于特写的问题仍是一个未知数.更重要的是,要找到完全不需要修改或者只需要稍作修改便能解决手头问题的代码并不容易.不同的人对待算法的态度各有千秋.很多人就只是简单地在一本书中或者网站上找个算法,复制代码,运行一次,然后可能还会测试一次,如果结果正确,就开始做下一个任务.但是,在我们看来,这

《算法基础》——第1章 算法基础知识 1.1 方法

第1章 算法基础知识 在开始算法学习之前,你需要一点背景知识.简单地说,首先需要知道的是算法是完成某些事情的方法.它定义了用某个方法执行一个任务的步骤.这个定义看起来足够简单,但是没有人为了做一件十分简单的工作而写算法.没有人写指令来获取数组中的第四个元素.这只是假设这是一个数组定义的一部分,并且你知道怎么去做(如果你知道如何在这个问题中使用编程语言).一般来说,人们只是为了完成复杂的任务而写算法.算法解释了如何找到一个复杂代数问题的答案,如何在一个包含数千条街道的网络中找到最短路径,抑或是如何

基础才是重中之重~位的运算

位运算无论在C#,VB还是在T-SQL里都有对它支持,位运算即对数值类型的每位进行计算,对于程序里,你可以使用十进制,十六进制,二进制对它进行位运算,事实上无论你使用哪种进制,对于计算机来说最后都为把它转换成二进制(0,1)的形式,因为二进制为简单,呵呵. 下面是关于位运算的表格 运算符号 位运算操作符 运算对象类型 运算结果类型 对象操作数 实例 ~ 非运算 整型 整型 1个 ~x & 与运算 2个 x & y | 或运算 2个 x | y ^ 异或运算 2个 x ^ y <<

《算法基础》——2.3 求幂运算

2.3 求幂运算 有时程序需要计算某数的正整数次幂,这在该幂指数不大时容易完成.例如,73可以通过计算7×7×7很容易地得到结果343.对于较大的幂,例如7102×187×291,这种计算过程是十分缓慢的. 注 计算较大的幂如7102×187×291可能很缓慢.但如果不是这种求幂运算在某些重要密码学中得到应用,人们也许不会十分关心它. 幸运的是,有一种较快的方法来执行这种运算.这种方法基于乘方运算的两个关键法则: 当这个幂是二次幂时,第一个法则可以迅速地计算出A的幂. 第二个法则能将这些A的幂结

《算法基础:打开算法之门》一导读

前言 Algorithms Unlocked 计算机是如何解决问题的呢?小小的GPS是如何只在几秒钟内就从无数条可能路径中找出到达目的地的最快捷路径的呢?在网上购物时,又如何防止他人窃取你的信用卡账号呢?解决这些问题,以及大量其他问题的答案均是算法.我写本书的目的就是为你打开算法之门,解开算法之谜. 我是<算法导论>的合著者之一.<算法导论>是一本特别好的书(当然,这是我个人的主观评价),但是它确实相当专业. 本书并不是<算法导论>,甚至不能被称为一本教材.它既没有对计