zoukankan      html  css  js  c++  java
  • 整数的故事(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位的”

  • 相关阅读:
    CF1454F Array Partition
    leetcode1883 准时抵达会议现场的最小跳过休息次数
    leetcode1871 跳跃游戏 VII
    leetcode1872 石子游戏VIII
    CF1355C Count Triangles
    CF1245D Shichikuji and Power Grid
    CF1368C Even Picture
    CF1368D AND, OR and square sum
    CF1395C Boboniu and Bit Operations
    SpringBoot和开发热部署
  • 原文地址:https://www.cnblogs.com/bigmonkey/p/10237155.html
Copyright © 2011-2022 走看看