【算法】—数论

数论

一.GCD

1.gcd的定义

GCD 即最大公约数Greatest Common Divisor。整数 a 和 b 的最大公约数是指能同时整除 a 和 b的最大整数,记为 gcd(a,b)


2.gcd的性质

  • gcd(a,b)=gcd(a,a+b)=gcd(a,k⋅a+b)
  • gcd(ka,kb)=k⋅gcd(a,b)。
  • 多个整数的最大公约数:gcd(a,b,c)=gcd(gcd(a,b),c)。
  • 若 gcd(a,b)=d,则 gcd(a/d,b/d)=1,即 a/d=与 b/d=互素
  • gcd(a+cb,b)=gcd(a,b)

3.gcd实现

库函数gcd()——python中gcd不会返回负数

import math

#求这两个整数的最大公约数
print (math.gcd(3, 6))
print (math.gcd(6, 12))
print (math.gcd(12, 36))
print (math.gcd(-12, -36))
print (math.gcd(5, 12))
print (math.gcd(10, 0))
print (math.gcd(0, 34))
print (math.gcd(48,96,120)) # 支持传入多个参数

自写gcd()

辗转相除法:

gcd(a,b)=gcd(b,a mod b)
# 递归
def gcd(a,b):
		if b==0:
				return a
		else:
				gcd(b,a % b)

二.LCM

1.lcm的定义

两个或多个整数公有的倍数叫做它们的公倍数,其中除0以外最小的一个公倍数就叫做这几个整数的最小公倍数。整数a,b的最小公倍数记为lcm(a,b)

2.lcm与gcd

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QGIeqvTm-1681797021287)(https://s3-us-west-2.amazonaws.com/secure.notion-static.com/359d6062-b3eb-4dce-b0c9-21efa26700c0/Untitled.png)]

3.lcm的实现

库函数实现

from math impport *
print(lcm(3,6,8,9))  # 支持多个参数
--->72

自写lcm

from math impport *
def lcm(x,y):
		return x*y//gcd(x,y) # 一定能整除gcd

三.快速幂

1.快速幂原理

传统的幂运算,是对底数进行连乘,时间复杂度为o(n),例如:2^ 13 = 2 2 *2……2,连乘十三次。但是我们可以通过增加底数,减少指数的做法,降低时间复杂度。从而能够实现复杂度为O(logn)的幂运算。还是以2^13为例,13的二进制为1101,因此2的13次方可以分解成以下形式:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-c3K03o4r-1681797021287)(https://s3-us-west-2.amazonaws.com/secure.notion-static.com/d6e945c8-4a6d-4e48-a827-de85d84d8f9c/Untitled.png)]

  • 其分解的幂次都是2的倍数

  • 幂次用二进制分解:13=1101 即化为0、4、8次

  • 取模的性质:
    a^n mod m = (a mod m)^n mod m

2.快速幂的实现

①自写

&:为按位与 是按二进制位与,比较两个二进制位数,如果上下同为0则为0,如果一个为1,一个为0则为0,如果同时为1则为1.

> > >> >>:右移位运算 将一个数的各二进制位全部右移若干位,正数左补0,负数左补1,右边丢弃

  • 求a的n次方
def fastpower(a,n):
    ans = 1
    while n:
        if n & 1:      
            ans *= a
        a *= a
        n >>= 1
    return ans
  • 求a的n次方对b取余
def fastpower(a,b,n):
    ans = 1
    while n:
        if n & 1:    # 处理n的最后一位
            ans = ans*a%b  # 如果最后一位是1 则乘
        a = a*a%b   # 递推:a^2--a^4--a^8...  
        n >>= 1   # 右移一位 把处理过的最后一位删除
    return ans%b

②pow()—python中比快速幂快

语法pow(x, y[, z])

函数是计算x的y次方,如果z在存在,则再对结果进行取模,其结果等效于pow(x,y) %z


注意:pow() 通过内置的方法直接调用,内置方法会把参数作为整型,而 math 模块则会把参数转换为 float


import math   # 导入 math 模块
 
print ("math.pow(100, 2) : ", math.pow(100, 2))
# 使用内置,查看输出结果区别
print ("pow(100, 2) : ", pow(100, 2))
print ("math.pow(100, -2) : ", math.pow(100, -2))
print ("math.pow(2, 4) : ", math.pow(2, 4))
print ("math.pow(3, 0) : ", math.pow(3, 0))
--->
math.pow(100, 2) :  10000.0
    pow(100, 2) :  10000
    math.pow(100, -2) :  0.0001
    math.pow(2, 4) :  16.0
    math.pow(3, 0) :  1.0

四.矩阵乘法

矩阵乘法类型

对位乘积:两个矩阵shape相同,各元素对应相乘,结果是一个相同shape的矩阵

矩阵乘法:数学上的矩阵乘法,结果是一个矩阵

向量内积:对应元素相乘,再相加,结果是一个数值

1.for循环

一个m行n列的矩阵,用二维数组matrix[][]存储

矩阵乘法:两个矩阵A,B相乘,要求A的列数等于B的行数,设A为mxn,B为nxu,C=AB 为mxn

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yWetkcqd-1681797021287)(https://s3-us-west-2.amazonaws.com/secure.notion-static.com/8678055d-4b5b-49ce-9940-088ca83ddf14/Untitled.png)]

**# 三层循环**
for i in range(1,m+1): # 表示A第一到m行
		for j in range(1,u+1):  # 表示B第一到u列
				for k in range(1,n+1): # A,B共有的行列数 
						c[i][j] += a[i][k] * b[k][j]
						# 对应元素相乘

2.np.multiply(A,B) 或 A * B(基于numpy)

作对位乘积

import numpy as np
A = np.array([
    [1,2],
    [3,4]])

B = np.array([
    [1,2],
    [3,4]])

C3 = A*B
C4 = np.multiply(A,B)
print(C3)
print('---------')
print(C4)
--->
	[[ 1  4]
   [ 9 16]]
  ---------
  [[ 1  4]
   [ 9 16]]

3.np.dot(A,B) 或 A @ B(基于numpy)

作矩阵乘法

import numpy as np
A = np.array([
    [1,2],
    [3,4]])

B = np.array([
    [1,2],
    [3,4]])

C1 = A @ B
C2 = np.dot(A,B)
print(C1)
print('---------')
print(C2)
--->
  [[ 7 10]
   [15 22]]
  ---------
  [[ 7 10]
   [15 22]]

五.素数

质数又称素数。指在一个大于1的自然数中,除了1和此整数自身外,不能被其他自然数整除的数。素数在数论中有着很重要的地位。比 1 大但不是素数的数称为合数。1 和 0 既非素数也非合数,2 是素数

1.试除法

这里我们就可以使用试除法,试除法的思路就是:我们给定一个数N范围在一百到两百之间,然后使用N除以2到N-1,如果都不能除尽则N为素数


试除法优化:将[2,n-1]缩小到[2,√n]即可

证明:若n=axb 则a≤√n,b≥√n,如果n有个因子为a,说明n不是素数 不用再判断b

**
from math import sqrt

def isPrimes1(n):
    if n <= 1:  **# 小于等于1的数不是素数**
        return False
    for i in range(2, int(sqrt(n) + 1)): **# 范围只到√n**
        if n % i == 0:
            return False
    return True

再优化试除法:将2之外的偶数排除,降低搜索

from math import sqrt

def isPrimes2(n):
    if n > 1:
        if n == 2:  # 2为素数
            return True
        if n % 2 == 0: # 能整除2 一定不是素数
            return False
        for x in range(3, int(sqrt(n) + 1), 2): # 步长为2 均为奇数
            if n % x == 0:
                return False
        return True
    return False


2.素数筛

①欧式筛

(由再优化试除法,可知是否为素数还与基础素数的倍数有关)

埃拉托斯特尼筛法,简称埃氏筛,是一种由希腊数学家埃拉托斯特尼所提出的一种简单检定素数的算法:

要得到自然数n以内的全部素数,必须把不大于根号n的所有素数的倍数剔除,剩下的就是素数

给出要筛数值的范围n,找出以内的素数。先用2去筛,即把2留下,把2的倍数剔除掉;再用下一个质数,也就是3筛,把3留下,把3的倍数剔除掉;接下去用下一个质数5筛,把5留下,把5的倍数剔除掉…(不断重复下去)

如果 x是质数,那么大于 x 的 n 的倍数 2x,3x,… 一定不是质数

在这里插入图片描述

代码实现:

n = 10**8  # 代求的范围中的最大值
k = 0
s = [True for i in range(n)]  # 首先默认所有数都是质数
z = []
for i in range(2,n):
    if s[i]:  **# 判断是否为质数,如果没有被标记过,就是质数**
        k+=1
        z.append(i) # 添加质数
        for j in range(i+i,n,**i(步长)**):  **# 将是指数的倍数的数都改为False**
            s[j] = False

print(k) # 质数个数
print(z) # 质数列表

②欧拉筛

在埃氏筛法的基础上,让每个合数只被它的最小质因子筛选一次,以达到不重复的目的

如:2的时候已经去掉了2的倍数,4的时候还要去掉4的倍数,这一步是不是很多余。所以欧拉筛法就是在埃式筛法的基础上加一句话 if ls[x]==True:

代码实现:

from math import sqrt
def hanshu(n):
    ls,x,y=[True]*(n+1),2,int(sqrt(n))+1 # 基本量
    for i in (2,n):
        **if ls[i]==True:**   **# 欧拉筛优化方式:防止重复判断**
            for j in range(x*2,n+1,x):
                ls[j]=False # 标记已处理过的数的倍数

    ls=[i for i in range(2,n+1) if ls[i]==True]
    return ls

print(hanshu(100))

3.求区间素数

当数字过大时,存储空间不足,无法用[0,b]中素数减去[0,a]中素数来求[a,b]中素数个数,因此,需要在埃式筛下对区间[a,b]进行素数判断

from math import *
def seg_sieve(a,b):
    for i in range(2,int(sqrt(b))+1):
        if vis[i]==True:   # 如果是素数
            for j in range(i*i,int(sqrt(b)),i):
                vis[j]=False  # 标记i的倍数不是素数
            for k in range(max(2,(a+i-1)//i)*i,b+1,i):
                # 不论a是素数还是非素数 都从i的倍数开始
                seg_prime[k-a]=False # 标记[a,b]区间内非素数
    num=0
    for i in range(0,b-a+1):
        if seg_prime[i]:
            prime[num]=i+a
            num+=1
    print(num) # 统计区间内素数个数
    # print(prime)

N=1000001
vis=[True]*N # 判断2,√b是否为素数
prime=[0]*N # 存2,√b的素数
seg_prime=[True]*N # 标记[a,b]之间是否有素数
a,b=map(int,input().split())
seg_sieve(a,b)

六.高斯消元

1.原理

线性方程组有很多种解法,可以最简单的直接代入消元计算,但是运算量较大,且过程复杂不直观

高斯消元法目的是预处理方程组的系数矩阵,将系数矩阵变换为上三角矩阵,这样整个方程就变得清晰直观很多,即使不借助计算机,也是可以很简单的手算出结果

原方程
在这里插入图片描述

高斯消元后的方程
在这里插入图片描述

列主元高斯消元法,是为了克服高斯消元法的”除法bug“的改进版本,因为消元过程中用到了除法,对于计算机来说,如果一个很小的值作为分母,则其在迭代中产生的误差,在做除法后会被放大很多倍。如0.0001与0.0002的误差小,但是1/0.0001与1/0.0002的误差巨大。列主元思想,就顺应而生,在消元前,把列中最大的元素所在行放到矩阵的最上方,那么就可以让较大值做分母,从而避免了误差被除法放大的问题


2.代码实现

在这里插入图片描述

代码实现:

eps=1e-7 **# 定义一个趋于0的极小数值 若x小于它 则x==0**
n=int(input()) 
a=[]
for _ in range(n): a.append(list(map(int,input().split()))) # 得到增广矩阵

for i in range(n): # 枚举行
    index=i # 记录当前行
    **# 选取该列的最大系数**
    for j in range(i+1,n+1): # 增广矩阵有n+1列
        if a[j][i] > a[index][i]: index=j
    
    **# 将该行的所有系数与首行交换**
    for j in range(i,n+1):
        a[index][j],a[i][j]=a[i][j],a[index][j] # i为当前行
    
    **# 换为最大后 若对角线上的主元为0 则该主元所在列为全0列 无解**
    if abs(a[i][i])<eps:
        print(-1)
        exit(0) # 退出
    
    **# 将处理后的该行主元变为1--->除以首元素**
    for j in range(n,-1,-1):
        a[i][j] /= a[i][i]
    
    **# 对1到n行处理 使主元所在列上所有元素除第i行外全部后变为0**
    for j in range(n):
        if j!=i:
            # i虽为行的定义 但随着消元的进行 化为上三角矩阵 i既为行也为列
            x=a[j][i]/a[i][i] # x表示其他行为1的几倍
            a[j][i]-=a[i][i]*x # 消为0

for i in range(n):
    **# 对角线所在行的最后一个元素(常熟项矩阵)即为xi的解**
    print('%.2f'%a[i][-1]) 

七.牛顿迭代法

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MNxDj7ws-1681797021290)(https://s3-us-west-2.amazonaws.com/secure.notion-static.com/2400fb8f-8e78-43fa-887d-80cfdae3fc9d/Untitled.png)]

step:

1. 我们在求解某一个方程的根时,可以先取函数上的一个易求解的特殊点,取其横坐标x0
2. 我们作x0处的切线,与x轴交于点x1,由于导数可求,因此,可以解出x1的值
3. 再在x1处作同样斜率的切线,交于x2,由此迭代,最终逼近最终解

如:若要求99的立方根,x^3-99=0,我们可以找出一个特殊值(如:x0=2)

代码实现:

import math
a = 99
x0 = 2
x1 = 2/3*x0+a/(3*x0**2)
while math.fabs(x1-x0) > 1E-5:
    x0 = x1
    x1 = 2/3*x0+a/(3*x0**2)
print(x1)

八.最小二乘法

1. 问题背景

在现实生活中,我们经常会遇到要拟合一些数据的情况,而且有时候我们并不能通过数学公式得到一个完美的函数来拟合这些数据,此时,我们可以使用最小二乘法来拟合这些数据。

💡 最小二乘法是一种用于拟合数据的常用方法,可以用于线性和非线性系统,可以用于单变量和多变量系统,可以用于有约束和无约束的系统。在Python 中,我们可以使用 numpy 库中的 polyfit 函数来实现最小二乘法

2. 原理

最小二乘法是一种数学优化技术,它通过最小化误差的平方和寻找数据的最佳函数匹配。最小二乘法可以用于线性和非线性系统,可以用于单变量和多变量系统,可以用于有约束和无约束的系统。

在使用最小二乘法时,我们需要先假设一个函数的形式,然后通过最小化误差的平方和来确定函数中的参数。例如,如果我们想要用一个线性函数来拟合一组数据,可以假设函数的形式为 y = a*x + b,然后通过最小化误差的平方和来确定 a 和 b 的值。

3. 使用方法

使用最小二乘法来拟合一组数据的步骤如下:

  1. 假设一个函数的形式。
  2. 定义一个误差函数,该函数为实际值与拟合值之差的平方和。
  3. 最小化误差函数,以确定函数中的参数。

4. 代码实现

Python 中可以使用 numpy 库中的 polyfit 函数来实现最小二乘法。例如,我们可以使用以下代码来拟合一组数据:

import numpy as np

# 定义数据
x = np.array([1, 2, 3, 4, 5])
y = np.array([1, 3, 5, 7, 9])

# 使用最小二乘法拟合数据
a, b = np.polyfit(x, y, 1)

# 输出结果
print("a = ", a)
print("b = ", b)

在上面的代码中,我们使用 polyfit 函数来拟合一组数据,其中:

  • xy 分别为输入的数据。
  • 1 表示我们要拟合的函数的阶数,这里我们假设拟合函数为一次函数。
    b 的值。

九.总结

  本章主要介绍了数论、埃氏筛法、欧拉筛法、求区间素数、高斯消元、牛顿迭代法和最小二乘法。其中,数论介绍了一些算法和原理,而其他部分则介绍了算法的实现和使用方法,数论往往依靠数学的知识和思维实现,因此代码并不会很复杂,而是考察思维能力,以上就是全部内容啦,如有错误,欢迎指正~~感谢!!

觉得本篇有帮助的话,就赏个三连吧~

在这里插入图片描述

  • 2
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
内容简介: 本书论述了算法数论的基本内容,其中包括:连分数、代数数域、椭圆曲线、素性检验、大整数因子分解算法、椭圆曲线上的离散对数、超椭圆曲线。本书的特点是内容涉及面广,在有限的篇幅内,包含了必要的预备知识和数学证明,尽可能形成一个完整的体系。并且本书的部分内容曾多次在中国科学院研究生院信息安全国家重点实验室和广州大学作为硕士研究生教材使用。本书可作为信息安全、数论等专业的研究生教材及相关专业的研究人员、高等学校的教师和高年级学生的参考。 目录: 序 前言 第一章 整数的因子分解 1.1 唯一分解定理 1.2 辗转相除法(欧氏除法) 1.3 Mersenne素数和Fermat素数 1.4 整系数多项式 1.5 环Z和Z[ω] 习题一 第二章 同余式 2.1 孙子定理 2.2 剩余类环 2.3 Euler函数ρ(m) 2.4 同余方程 2.5 原根 2.6 缩系的构造 习题二 第三章 二次剩余 3.1 定义及Euler判别条件 3.2 Legendre符号 3.3 Jacobi符号 习题三 第四章 特征 4.1 剩余系的表示 4.2 特征 4.3 原特征 4.4 特征和 4.5 Gauss和 习题四 第五章 连分数 5.1 简单连分数 5.2 用连分数表实数 5.3 最佳渐近分数 5.4 Legendre判别条件 习题五 第六章 代数数域 6.1 代数整数 6.2 Dedekind整环 6.3 阶的一些性质 第七章 椭圆曲线 7.1 椭圆曲线的群结构 7.2 除子类群 7.3 同种映射 7.4 Tate模和Weil对 7.5 有限域上的椭圆曲线 习题七 第八章 在密码学中的一些应用 8.1 RSA公钥密码 8.2 Uiffie-Hellman体制 8.3 ElGamal算法 8.4 基于背包问题的公钥密码 8.5 秘密共享 第九章 素性检验 9.1 Fermat小定理及伪素数 9.2 强伪素数及Miller-Rabin检验 9.3 利用n-1的因子分解的素性检验 9.4 利用n+1的因子分解的素性检验 9.5 分圆环素性检验 9.6 基于椭圆曲线的素性检验 第十章 大整数因子分解算法 10.1 连分数因子分解算法 10.2 二次筛法 10.3 Pollard的P-1因子分解算法 10.4 椭圆曲线因子分解算法 10.5 数域筛法 习题十 第十一章 椭圆曲线上的离散对数 11.1 椭圆曲线公钥密码 11.2 小步-大步法 11.3 家袋鼠和野袋鼠 11.4 MOV约化 11.5 FR约化 11.6 SSSA约化 11.7 有限域上离散对数的计算 第十二章 超椭圆曲线 12.1 超椭圆曲线的Jacobian 12.2 虚二次代数函数域 12.3 基于超椭圆曲线的公钥密码 附录 一些常用算法 A.1 不可约多项式的判别 A.2 有限域中平方根的求解 A.3 有限域上的分解 A.4 Hensel引理 A.5 格 A.6 Z[x]中多项式的分解 参考文献 免责申明:此书是我在网络上获取的,希望对大家有用。资源版权归作者及其公司所有,如果你喜欢,请购买正版。~~~

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

DAY Ⅰ

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值