整数的故事(1)

  程序设计课程总是充满趣味,在学习了判断和循环后就可以编写一些有意思的代码。记得我在初学编程时,老师曾出过一个题目:找出两个整数的最大公约数。当时我在黑板上写下了自己的实现方式:

 1 def gcd(a, b):
 2     if a == b:
 3         return a
 4 
 5     if a < b:
 6         a,b = b,a
 7 
 8     result = [i for i in range(1, b + 1) if a % i == 0 and b % i == 0]
 9 
10     return  result.pop()

   运行结果是正确的,回到座位上,我为此高兴了两分钟。

  后来老师写出了另一个实现:

1 def gcd(a, b):
2     if b == 0:
3         return a
4     else:
5         return gcd(b, a % b)

   我的第一反应是:“嗯?”

  遗憾的是,我并没有对这段代码深究,只是简单的记住了这种方法,反正都交给计算机计算,何必在乎快慢呢?

  后来学了数据结构,知道了用大O评估算法效率,我这才开始重新审视那段寻找最大公约数的代码——它实际上使用了传说中的“辗转相除”,要真正弄清楚来龙去脉,还要从整数说起……

整除和余数

  我们都曾经用笨拙的声音从1数到10,这大概是人生中第一次接触数学。稍大一点后,懂得了零的概念,再后来知道了有负数存在……这些美好的记忆都有整数伴随在左右。随着年龄的增长和知识的提升,我们知道了更多关于整数的知识,这其中就包括整除和余数。

欧几里德算式

  数学中是以数轴分段的方式定义整除的,如果n是一个正整数,那么可以用n的倍数将数轴分成很多段:

 

  如果将一个整数m放在数轴上,那么m将正好位于qn和(q+1)n之间,其中q也是一个整数:

 

  如果m正好是n的整数倍,那么m=qn,否则可以写成m=qn+r形式,qn是在m左侧最近的n的整数倍,r是qn到m的距离。如果把两种情况合并,那么m总是可以写成下面的形式:

 

  对于特定的n来说,m的表达式唯一的,这种表达式叫做欧几里德算式,也叫做除法算式。

  看着挺唬人,其实欧几里德算式有更常见的描述:如果m,n都是整数,并且n≠0,那么总是存在q和r,0≤r<|n|,使得m有唯一的表达式:

  其中q是商,r是余数,如果r=0,则称m 能够被n整除,或n能整除m,记作n|m。看来欧几里德算式只不过是从代数上解释了什么是整除,什么是余数。

   示例   找出q和r

   

  1和2比较简单:

 

  3可能会出点差错:

 

  计算机运行的结果:

  看来计算机认为是另一种答案:

 

  定义终于显现出作用了,在余数的定义中0≤r<|n|,r=-1不满足这个条件,所以正确答案是q=-3,r=4。

整除的性质

  整除有一些被人们熟知的性质,如果a,b,c都是整数,则:

  1. 如果a|b且a|c,则a|(b+c)

  2. 如果a|b,则a|cb

  3. 如果a|b且b|c,则a|c

  由于0不能作为除数,所以a|b包含的默认条件是a≠0。

  此外还有一个推论,如果a,b,c都是整数,当a|b且a|c时,对于任意整数m和n,都有a|(mb+nc)。

  除法是乘法的逆运算,这些性质和推论其实都是根据乘法的分配律和结合律推导而来的。

素数

  整数的故事中少不了素数,它的另一个名称是质数(prime number),是一种大于1整数。素数是这样定义的:设p是大于1的正整数,如果能整除p的正整数只有p和1,那么p就是一个素数。其中1比较特殊,它不是素数,是单位数。2,3,5,7,11是素数,4,6,8,10,12不是素数。

寻找素数

  看起来判断一个整数是否是素数很简单,但这句话仅适用于较小的整数,稍大一点的整数就没那么容易判断了,1234567是否是素数?给你10秒钟时间。

  这种复杂的问题还是交给计算机去处理:

 1 # 判断a是否是素数
 2 def is_prime(a):
 3     if a < 2:
 4         return False,a
 5     elif a == 2:
 6         return True,a
 7 
 8     # 用从 2 到 a-1 之间的每个数除以a,看看那个能被整除
 9     divisors = [x for x in range(2, a) if a % x == 0]
10 
11     return len(divisors) == 0,divisors 
12 
13 print(is_prime(1234567))

  is_prim返回一个包含两个元素的元组,第一个元素回答了a是否是素数,另一个回答了除了1和自身外,a还有哪些约数。

  结果显示1234567是不一个素数,除了1和自身外,它还有127和9721两个因数。

寻找素数2.0版

  is_prime方法能够正确运行,但仍然有改进的余地。根据欧几里德算式,m=qn,q和n二者此消彼长,它们的平衡点是根号m,这意味着只要判断2到根号m间是否存在能够被m整除的数就可以;此外,一个大于2偶数一定不是素数,所以只需要判断奇数即可。由此得到了判断素数的改进版:

 1 import math
 2 
 3 # 判断a是否是素数
 4 def is_prime_2(a):
 5     if a < 2:
 6         return False
 7     elif a == 2:
 8         return True
 9     elif a % 2 == 0:
10         return False
11 
12     result = True
13     # 取 math.sqrt(a) 的整数部分
14     end = int(math.sqrt(a))
15     q = 3
16     while(q <= end):
17         if a % q == 0:
18             result = False
19             break
20         q += 2
21 
22     return result

整数分解

  我们知道多项式的因式分解,类似地,整数也可以分解,可以将一个正整数写成它的几个约数因子的乘积,这就是整数分解(integer divisorization)。

素因子表达式

  大于1的整数分解可以更进一步,使每个因子都是素数。对于每一个大于1的正整数m来说,可以唯一地写成:

  其中pi是能整除m的素数因子,p1<p2<…<pt;ki是pi出现的次数。这种表达式被称为整数m的素因子表达式,对于任意m,它的素因子表达式是独一无二的。将m写素因子表达式的过程叫做素因子分解。

  素因子表达也从另一个层面(非素数的层面)定义了素数:如果一个大于1的整数m不是素数,那么m一定能够分解成2个或两个以上素数的乘积,并且这个表达式是唯一的。

 

  示例  写出792030的素因子表达式

  7本身是一个素数,只能整数分解成1×7,但1并不是素数,所以7的素因子表达式就是7本身。

  可以使用下面的代码进行整数的素因子分解:

 1 # 获取a的所有除1和自身外的约数
 2 def get_divisors(a):
 3     return [x for x in range(2, a) if a % x == 0]
 4 
 5 # 素因子分解
 6 def prim_division(a):
 7     if is_prime_2(a):
 8         return [a]
 9 
10     result = []
11     divisors = get_divisors(a)
12     for divisor in divisors :
13         while True:
14             if a % divisor != 0:
15                 break
16             if is_prime_2(divisor):
17                 result.append(divisor)
18                 a /= divisor
19             else:
20                 break
21 
22     return result

   好了,我们已经知道整数可以素因子分解,但是这有什么用呢?

  数学的发展来源于实践,不能用的东西大概也没人去研究。素因子分解的用处还挺多,它可以用来证明素数有无穷个,根号2是无理数,还可以用于密码学、计算复杂理论,甚至用于量子计算等等。接下来就举几个例子,看看素因子分解究竟是怎么使用的。

素数有无穷个吗?

  老师:素数有无穷个吗?

  同学:当然了!

  老师:为什么呢?

  同学:没有为什么啊,它当然是无穷的,这还要正明吗?随便给出一个素数,不是很容易的例举出比它更大的素数吗?

  老师:但是素数没什么先验的理由必须有无穷多个啊。如果写出一个相当长的,能够绕地球一圈的素数,你能保证一定有一个更大的素数吗?

  同学:……

  老师:其实你已经回答了,随便给出一个素数,确实能够例举出比它更大的素数,只不过我们需要使用反证法来证明。

  假设素数的个数是有限的,那么这些素数都可以用集合的形式列举出来:

  P就是集合中最大的素数。

  根据整数的素因子分解,一个大于1的正整数可以分解成若干个素数的乘积,那么存在一个整数M,它等于Ω中所有元素的乘积加1:

  M肯定比P更大,P已经被假定是最大的素数,所以M肯定不是素数。素因子表达式告诉我们,M如果不是素数,则一定能够分解成若干个素数的乘积,因为已经假设Ω中包含了所有的素数,所以M也一定能够分解成Ω中若干个元素的乘积。但是现在M除以Ω中的所有元素都会产生余数1,这意味着M只能被M或1整除,所以M也是一个素数,这与“M肯定不是素数”矛盾,因此“素数有无穷个”。

  注:“素数有无穷个”这一命题最早的证明出现在古希腊数学家欧几里得 (Euclid) 的《几何原本》上,这一命题也因此被称为了 “欧几里得定理” (Euclid's theorem) 或 “欧几里得第二定理” (Euclid's second theorem)。

根号2为什么“无理”

  老师:根号2是一个无理数,它无限不循环,没有尽头。

  同学:为什么呢?也许它在绕地球一圈后循环了。

  老师:它确实是不循环的,能试试自己证明吗?

  同学:还是使用反证法吗?

  老师:是的。证明的时候别忘了素因子分解。

  假设根号2 是一个有理数,根据有理数的定义,有理数是一个整数a和一个正整数b的比,这样一来,就可以得出:

  其中a/b不能通分,也就是说a和b是互素的(a和b的公约数只有1)。

  现在将等式两侧同时平方:

  2b2肯定是偶数,所以a2也是偶数,如果a是奇数,则a2也是奇数,所以a只能是偶数,a一定可以素因子分解成:

  将a2=2b2等式两侧同时除以2:

 

  a2是偶数,a2/2还是偶数。a2/2= b2所以b2也是偶数,b仍然是偶数,b可以素因子分解成:

 

  现在2|a并且2|b,这和a、b 互素矛盾,所以根号2是无理数。

 

  待续

 


   作者:我是8位的

  出处:http://www.cnblogs.com/bigmonkey

  本文以学习、研究和分享为主,如需转载,请联系本人,标明作者和出处,非商业用途! 

  扫描二维码关注公众号“我是8位的”

posted on 2019-01-08 10:25  我是8位的  阅读(1232)  评论(0编辑  收藏  举报

导航