南开大学软件学院2021年秋季学期研究生算法课程(复习)贪心分治之分治(包含快速傅里叶变换)

本文主要是介绍南开大学软件学院2021年秋季学期研究生算法课程(复习)贪心分治之分治(包含快速傅里叶变换),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

状态空间中的算法

状态空间:状态(点)+状态转移(边)+价值函数(点权

  • 求解过程的抽象模型
  • 暴力搜索动态规划都是一类算法框架(或算法思想),本质都是在状态空间中寻找解
  • 二者都需要准确地用最少的状态变量建模
  • 而对于动态规划,还需要定义正确的价值函数和转移更新

分治:子问题互不重叠,但最优子结构需考虑全部子问题

贪心:子问题互不重叠,但最优子结构仅考虑极少子问题

若认为动态规划算法的状态空间是有向无环图的话,对于分治和贪心,其状态空间通常可以建模成,分治需要评估整个树,贪心则是树上的几条链

分治与贪心

经典算法回顾

归并排序:用两个子状态的解构造按序拼接当前状态的解,分治算法,时间复杂度O(n logn)

逆序对个数:用两个子状态的解构造统计个数当前状态的解,分治算法,时间复杂度O(n logn)

快速排序:需用两个子状态的解构造按序拼接当前状态的解,分治算法,期望时间复杂度O(n logn)

第k大数:仅需某一个子状态的解即可,贪心算法,期望时间复杂度O(n)

矩阵快速幂:两个子状态的解相同,贪心算法,时间复杂度O(logn)

分治

Strassen矩阵乘法

给出矩阵A,B\in \mathbb{R}^{n\times n},求C=A\times B

  • 直接乘法的时间复杂度O(n^3)
  • Strassen乘法的核心思想:分块矩阵
    • A=\begin{pmatrix} A_{11} & A_{12} \\ A_{21} & A_{22} \end{pmatrix}B=\begin{pmatrix} B_{11} & B_{12}\\ B_{21} & B_{22} \end{pmatrix}C=\begin{pmatrix} C_{11} & C_{12}\\ C_{21} & C_{22} \end{pmatrix}
  • Strassen算法会复用分块矩阵乘法结果,先10次加法计算:
    • S_1=B_{12}-B_{22}\ S_2=A_{11}+A_{12}\ S_3=A_{21}+A_{22}\ S_4=B_{21}-B_{11}
    • S_5=A_{11}+A_{22}\ S_6=B_{11}+B_{22}\ S_7=A_{12}-A_{22}
    • S_8=B_{21}+B_{22}\ S_9=A_{11}-A_{21}\ S_{10}=B_{11}+B_{12}
  • 再用7次乘法计算:
    • P_1=A_{11}S_1\ P_2=S_2B_{22}\ P_3=S_3B_{11}\ P_4=A_{22}S_4
    • P_5=S_5S_6\ P_6=S_7S_8\ P_7=S_9S_{10}
  • 最终使用8次加法得出结果:
    • C_{11}=P_5+P_4-P_2+P_6\ C_{12}=P_1+P_2
    • C_{21}=P_3+P_4\ C_{22}=P_5+P_1-P_3-P_7
  • 时间复杂度?T(n)=7T(\frac{n}{2})+O(n^2)

分治算法的时间复杂度:T(n)=aT(n/b)+O(n^d)

主定理

  • if\ d>\log_ba,T(n)=O(n^d)
  • if\ d=\log_ba,T(n)=O(n^d\log_ba)
  • if\ d<\log_ba,T(n)=O(n^{\log_ba})

一维数值积分

给出一维函数f(x),计算\int_{a}^{b}f(x)dx(无闭式解)

  • 梯形近似法:将区间等分n份,求n个梯形的面积和
  • 对于区间\left ( x_0,x_1 \right ),其梯形面积等价于过点(x_0,f(x_0))(x_1,f(x_1))线性函数P(x)积分\int_{x_0}^{x_1}P(x)dx
    • P(x)=f(x_0)+\frac{f(x_1)-f(x_0)}{x_1-x_0}(x-x_0)=f(x_0)\frac{x-x_1}{x_0-x_1} +f(x_1)\frac{x-x_0}{x_1-x_0}
    • \int_{x_0}^{x_1}P(x)dx=\frac{x_1-x_0}{2}(f(x_0)+f(x_1))
  • 过2个点的线性函数P(x)线性插值(一次插值)

一维数值积分

给出一维函数f(x),计算\int_{a}^{b}f(x)dx(无闭式解)

  • 插值interpolation 拉格朗日插值多项式
    • n+1个不同的点(x_i,y_i),可确定n阶多项式函数的全部系数
    • A(x)=\sum_{i=0}^{n-1}f(x_i)\prod_{j\neq i}\frac{(x-x_j)}{(x_i-x_j)}
  • 梯形法即为一次插值(但是近似程度不够
  • 优化一:利用二次插值(利用a,m=(a+b)/2,b三个点)
    • P(x)=f(a)\frac{(x-m)(x-b)}{(a-m)(a-b)}+f(m)\frac{(x-a)(x-b)}{(m-a)(m-b)}+f(b)\frac{(x-a)(x-m)}{(b-a)(b-m)}
    • 数值积分转化为下式(即辛普森积分法
    • \int_{a}^{b}f(x)dx\approx \int_{a}^{b}P(x)dx=\frac{b-a}{6}(f(a)+4f(\frac{a+b}{2})+f(b))

一维数值积分

给出一维函数f(x),计算\int_{a}^{b}f(x)dx(无闭式解)

  • 梯形近似法:将区间等分n份,求n个梯形的面积和
  • 思考:分到多细为止?等分是否有问题?

 

  • 显然,每个区间不必分解到相同粒度,但分解后结果要使得积分值在误差范围\varepsilon
  • 优化二:自适应辛普森算法----自适应算法也是一类算法框架
    • AdaSimp(a,b,S(a,b,f)),其中S()为辛普森积分函数
    1. m\leftarrow \frac{a+b}{2}
    2. 计算S(a,m,f)和S(m,b,f)
    3. \left | S(a,m,f)+S(m,b,f)-S(a,b,f) \right |<\varepsilon,则返回S(a,b,f)
    4. 否则,返回AdaSimp(a,m,S(a,m,f))+AdaSimp(m,b,S(m,b,f))
  • 时间复杂度?取决于f(x)的性质和\varepsilon的大小
  • 思考:高维积分这种方法还可行吗?不可行的话该怎么办?

多项式算法

n阶多项式A(x)的定义为

A(x)=\sum_{i=0}^{n-1}a_ix^i=a_0+a_1x+a_2x^2+\cdot \cdot \cdot +a_nx^n

给出两个n阶多项式A(x),B(x),计算C(x)=A(x)✖B(x)

  • 多项式乘法:D(x)=A(x)+B(x)=\sum_{i=0}^{n}d_ix^i,\ where\ d_i=a_i+b_i时间复杂度:O(n)
  • 多项式乘法:C(x)=A(x)\times B(x)=\sum_{i=0}^{2n}c_ix^i,\ where\ c_i=\sum_{k=0}^{i}a_kb_{i-k}时间复杂度:O(n^2)

多项式的表示法

以下对n-1阶多项式A(x)=\sum_{i=0}^{n-1}a_ix^i讨论

  • 系数表示法Coefficient representation
    • 即系数向量\mathbf{a}=(a_0,a_1,\cdot \cdot \cdot ,a_{n-1})即可表示一个多项式
    • 加法时间复杂度O(n),乘法时间复杂度O(n^2)
  • 点值表示法Point-value representation
    • 即使用n个点\left \{ (x_0,y_0),(x_1,y_1),\cdot \cdot \cdot ,(x_n,y_n) \right \}即可表示n-1阶多项式,其中要求y_k=A(x_k),k=0,1,\cdot \cdot \cdot ,n
    • 若对于A(x)的点值表示\left \{ (x_0,y_0),(x_1,y_1),\cdot \cdot \cdot ,(x_n,y_n) \right \}和B(x)的点值表示\left \{ (x_0,y^{'}_0),(x_1,y^{'}_1),\cdot \cdot \cdot ,(x_n,y^{'}_n) \right \}
      • 加法结果为\left \{ (x_0,y_0+y^{'}_0),(x_1,y_1+y^{'}_1),\cdot \cdot \cdot ,(x_n,y_n+y^{'}_n) \right \}复杂度O(n)
      • 乘法结果注意为2n-2阶多项式,需要2n-1个点确定,即为\left \{ (x_0,y_0y^{'}_0),(x_1,y_1y^{'}_1),,\cdot \cdot \cdot (x_{2n-1},y_{2n-1}y^{'}_{2n-1}) \right \}复杂度O(n)

多项式表示法的转换

以下对n-1阶多项式A(x)=\sum_{i=0}^{n-1}a_ix^i讨论

  • 求值Evaluation:系数表示➡点值表示
    • 秦九韶算法/Horner's rule:y_k4=A(x_k)=a_0+x_k(a_1+x_k(a_2+\cdot \cdot \cdot ))
    • 对n个点进行计算,总共需要O(n^2)的时间
  • 插值Interpolation:点值表示➡插值表示
    • ​​​​​​​多项式插值唯一性:范德蒙德矩阵非奇异
    • 拉格朗日插值,时间复杂度O(n^2)
      • ​​​​​​​​​​​​​​A(x)=\sum_{i=0}^{n-1}f(x_i)\prod_{j\neq i}\frac{(x-x_j)}{(x_i-x_j)}

于是,计算乘积C(x)=A(x)\times B(x)可以通过

  • A(x),B(x)系数表示➡系数乘法 O(n^2)➡C(x)系数表示
  • A(x),B(x)系数表示➡求值 O(n^2)➡A(x),B(x)点值表示➡点值乘法 O(n)➡C(x)点值表示➡插值 O(n^2)➡C(x)系数表示

快速傅里叶变换Fast Fourier Transform,FFT

选n个特殊点(x_k,y_k),用分治优化求值和插值O(n logn)

  • A(x),B(x)系数表示➡FFT求值 O(n log n)➡A(x),B(x)点值表示➡点值乘法 O(n)➡C(x)点值表示➡FFT插值 O(n log n)➡C(x)系数表示

特殊的点x_k为第k个n次单位根

背景知识:复变函数 z=a+bi

  • 复数的四则运算:加减乘除
  • 复数的几何表示:复平面,模长r和辐角\theta
  • 运算的几何意义:加:四边形法则;乘:模长乘,辐角加
  • 欧拉公式:e^{\theta i}=\cos \theta+i\sin \theta
  • 单位根:复数域上满足x^n=1所有解:共有n个根,记作
    • w_n^k=e^{\frac{2\pi k}{n}i},\ for\ k=0,1,\cdot \cdot \cdot ,n-1
  • n个单位根构成了循环群,生成元(本源根)w_n^1
  • 模n加/乘法群,w_n^jw_n^k=w_n^{(j+k)\ mod\ n}(w_n^k)^i=w_n^{(ki)\ mod\ n}
  • 折半引理:w_{2n}^{2k}=w_n^k
  • 消去引理:w_n^{k+\frac{n}{2}}=-w_n^k
  • 求和引理:\sum_{i=0}^{n-1}(w_n^k)^i=0

离散傅里叶变换Discrete Fourier Transform,DFT

即对n-1阶多项式A(x)在n次单位根上的求值过程

  • 给出A(x)的系数表示,即向量\mathbf{a}=(a_0,a_1,\cdot \cdot \cdot ,a_{n-1}),计算其在n次单位根w_n^k上的值y_k​​​​​​​,即
    • y_k=A(w_n^k)=\sum_{i=0}^{n-1}a_i(w_n^k)^i,for\ k=0,1,\cdot \cdot \cdot ,n-1
  • 在这种情况下,向量\mathbf{y}=(y_0,y_1,\cdot \cdot \cdot ,y_{n-1})即为系数向量\mathbf{a}=(a_0,a_1,\cdot \cdot \cdot ,a_{n-1})离散傅里叶变换,记作
    • \mathbf{y}=DFT(\mathbf{a})或者\mathbf{a}=DFT^{-1}(\mathbf{y})
  • 由单位根w_n^k的特殊性质,DFT可用分治快速完成➡FFT

快速傅里叶变化(求值部分)

  • 首先,假定A(x)的阶数n为2的整数次幂(通过引入0系数高阶项可以永远做到)
  • 分别通过A(x)的奇数次项和偶数次项系数定义新多项式
    • A^{'}(x)=a_0+a_2x+a_4x^2+\cdot \cdot \cdot +a_{n-2}x^{\frac{n}{2}-1} 
    • A^{''}(x)=a_1+a_3x+a_5x^2+\cdot \cdot \cdot +a_{n-1}x^{\frac{n}{2}-1}
  • 由此可得分治算法的关键:A(x)=A^{'}(x^2)+xA^{''}(x^2)
  • 因此,计算A(x)在w_n^0,w_n^1,\cdot \cdot \cdot ,w_n^{n-1}上的值,可以转化为:

     

    • 先计算A^{'}(x)A^{''}(x)(w_n^0)^2,(w_n^1)^2,\cdot \cdot \cdot ,(w_n^{n-1})^2上的值(注意根据折半引理和消去引理可以使计算点的个数减半
    • 再根据A(x)=A^{'}(x^2)+xA^{''}(x^2)合并结果
  • 具体来说,计算A(w_n^0),A(w_n^1),\cdot \cdot \cdot ,A(w_n^{n-1}),可以转化为:
    • 对于k=0,1,\cdot \cdot \cdot ,\frac{n}{2}-1前n/2个点
    • A(w_n^k)=A^{'}((w_n^k)^2)+w_n^kA^{''}((w_n^k)^2)=A^{'}(w_{\frac{n}{2}}^k)+w_n^kA^{''}(w_{\frac{n}{2}}^k)
    • 对于k+\frac{n}{2}=\frac{n}{2},\frac{n}{2}+1,\cdot \cdot \cdot ,n-1(后n/2个点,但k=0,1,\cdot \cdot \cdot ,\frac{n}{2}-1)
    • A(w_n^{k+\frac{n}{2}})=A^{'}(w_n^{2k+n})+w_n^{k+\frac{n}{2}}A^{''}(w_n^{2k+n})=A^{'}(w_{\frac{n}{2}}^k)-w_n^kA^{''}(w_{\frac{n}{2}}^k)
  •  总结来说,A(x)上的n次单位根的值,只需分别计算一遍A^{'}(x)A^{''}(x)n/2次单位根值即可构造出来
  • 时间复杂度?T(n)=2T(\frac{n}{2})+O(n)=O(n\log n)

快速傅里叶变换(插值部分)

  • 注意,离散傅里叶变换 \mathbf{y}=DFT(\mathbf{a})其实可以写成矩阵形式\mathbf{y}=V_n\mathbf{a},其中V_n范德蒙德矩阵,第k行第j列元素为w_n^{kj}
  • 于是,\mathbf{a}=DFT^{-1}(\mathbf{y})也就是\mathbf{a}=V_n^{-1}\mathbf{y},由范德蒙德矩阵的求逆公式,第k行第j列元素为\frac{w_n^{-kj}}{n}
  • 因此,插值部分即将\mathbf{y}=(y_0,y_1,\cdot \cdot \cdot ,y_{n-1})视作系数向量,构成新多项式,计算其在w_n^0,w_n^{-1},\cdot \cdot \cdot ,w_n^{-(n-1)}上的值,所得向量结果除上n即得到答案,此过程同理亦可分治

分治算法小结

  • 难点:综合所有子问题的解构造问题的解的过程
  • 特点:时间复杂度通常使用主定理计算
  • 思维特点:
  1. 子问题规模要成倍缩小;(分解
  2. 假设子问题完成,如何得出原问题的答案;(合并
  • 更多:平面最近点对、CDQ分治、并行分布式的Map-Reduce

 

 

 

 

这篇关于南开大学软件学院2021年秋季学期研究生算法课程(复习)贪心分治之分治(包含快速傅里叶变换)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/269929

相关文章

C++变换迭代器使用方法小结

《C++变换迭代器使用方法小结》本文主要介绍了C++变换迭代器使用方法小结,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧... 目录1、源码2、代码解析代码解析:transform_iterator1. transform_iterat

使用Python实现快速搭建本地HTTP服务器

《使用Python实现快速搭建本地HTTP服务器》:本文主要介绍如何使用Python快速搭建本地HTTP服务器,轻松实现一键HTTP文件共享,同时结合二维码技术,让访问更简单,感兴趣的小伙伴可以了... 目录1. 概述2. 快速搭建 HTTP 文件共享服务2.1 核心思路2.2 代码实现2.3 代码解读3.

springboot security快速使用示例详解

《springbootsecurity快速使用示例详解》:本文主要介绍springbootsecurity快速使用示例,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝... 目录创www.chinasem.cn建spring boot项目生成脚手架配置依赖接口示例代码项目结构启用s

SpringBoot实现MD5加盐算法的示例代码

《SpringBoot实现MD5加盐算法的示例代码》加盐算法是一种用于增强密码安全性的技术,本文主要介绍了SpringBoot实现MD5加盐算法的示例代码,文中通过示例代码介绍的非常详细,对大家的学习... 目录一、什么是加盐算法二、如何实现加盐算法2.1 加盐算法代码实现2.2 注册页面中进行密码加盐2.

Java时间轮调度算法的代码实现

《Java时间轮调度算法的代码实现》时间轮是一种高效的定时调度算法,主要用于管理延时任务或周期性任务,它通过一个环形数组(时间轮)和指针来实现,将大量定时任务分摊到固定的时间槽中,极大地降低了时间复杂... 目录1、简述2、时间轮的原理3. 时间轮的实现步骤3.1 定义时间槽3.2 定义时间轮3.3 使用时

C++快速排序超详细讲解

《C++快速排序超详细讲解》快速排序是一种高效的排序算法,通过分治法将数组划分为两部分,递归排序,直到整个数组有序,通过代码解析和示例,详细解释了快速排序的工作原理和实现过程,需要的朋友可以参考下... 目录一、快速排序原理二、快速排序标准代码三、代码解析四、使用while循环的快速排序1.代码代码1.由快

Win32下C++实现快速获取硬盘分区信息

《Win32下C++实现快速获取硬盘分区信息》这篇文章主要为大家详细介绍了Win32下C++如何实现快速获取硬盘分区信息,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 实现代码CDiskDriveUtils.h#pragma once #include <wtypesbase

如何通过Golang的container/list实现LRU缓存算法

《如何通过Golang的container/list实现LRU缓存算法》文章介绍了Go语言中container/list包实现的双向链表,并探讨了如何使用链表实现LRU缓存,LRU缓存通过维护一个双向... 目录力扣:146. LRU 缓存主要结构 List 和 Element常用方法1. 初始化链表2.

Spring AI与DeepSeek实战一之快速打造智能对话应用

《SpringAI与DeepSeek实战一之快速打造智能对话应用》本文详细介绍了如何通过SpringAI框架集成DeepSeek大模型,实现普通对话和流式对话功能,步骤包括申请API-KEY、项目搭... 目录一、概述二、申请DeepSeek的API-KEY三、项目搭建3.1. 开发环境要求3.2. mav

Python如何快速下载依赖

《Python如何快速下载依赖》本文介绍了四种在Python中快速下载依赖的方法,包括使用国内镜像源、开启pip并发下载功能、使用pipreqs批量下载项目依赖以及使用conda管理依赖,通过这些方法... 目录python快速下载依赖1. 使用国内镜像源临时使用镜像源永久配置镜像源2. 使用 pip 的并