`
wcb2003
  • 浏览: 59297 次
  • 性别: Icon_minigender_1
  • 来自: zhengzhou
最近访客 更多访客>>
文章分类
社区版块
存档分类
最新评论

十五、多项式乘法与快速傅里叶变换

 
阅读更多

十五、多项式乘法与快速傅里叶变换

前言

经典算法研究系列,已经写到第十五章了,本章,咱们来介绍多项式的乘法以及快速傅里叶变换算法。本博客之前也已详细介绍过离散傅里叶变换(请参考:十、从头到尾彻底理解傅里叶变换算法、上,及十、从头到尾彻底理解傅里叶变换算法、下),这次咱们从多项式乘法开始,然后介绍FFT算法的原理与实现。同时,本文虽涉及到不少数学公式和定理(当然,我会尽量舍去一些与本文咱们要介绍的中心内容无关的定理或证明,只为保证能让读者易于接受或理解),但尽量保证通俗易懂,以让读者能看个明白。

有朋友建议,算法专一种,就ok,没必要各个都学习。但个人实在抑制不住自己的兴趣,就是想写,当没法做到强迫自己不写时,这个经典算法研究系列便一直这么写下来了。

第一节、多项式乘法

我们知道,有两种表示多项式的方法,即系数表示法和点值表示法。什么是系数表示法?所谓的系数表示法,举个例子如下图所示,A(x)=6x^3 + 7x^2 - 10x + 9,B(x)=-2x^3+4x-5,则C(x)=A(x)*B(x)就是普通的多项式相乘的算法,系数与系数相乘,这就是所谓的系数表示法。

那么,何谓点值表示法?。一个次数为n次的多项式A(x)的点值表示就是n个点值所形成的集合:{(x0,y0), (x1,y1),..., (xn-1,yn-1)}。其中所有xk各不相同,且当k=0,1,……,n-1时,有y(k)=A(xk)。

一个多项式可以由很多不同的点值表示,这是由于任意n个相异点x0,x1,...,xn-1组成的集合,都可以看做是这种点值法的表示方法。对于一个用系数形式表示的多项式来说,在原则上计算其点值表示是简单易行的,我们只需要选取n个相异点x0,x1,...,xn-1,然后对k=0,1,...,n-1,求出A(xk),然后根据霍纳法则,求出这n个点的值所需要的时间为O(n^2)。

然,稍后,你将看到,如果巧妙的选取xk的话,适当的利用点值表示可以使多项式的乘法可以在线性时间内完成。所以,如果我们能恰到好处的利用系数表示法与点值表示法的相互转化,那么我们可以加速多项式乘法(下面,将详细阐述这个过程),达到O(n*logn)的最佳时间复杂度。

前面说了,当用系数表示法,即用一般的算法表示多项式时,两个n次多项式的乘法需要用O(n^2)的时间才能完成。但采用点值表示法时,多项式乘法的运行时间仅为O(n)。接下来,咱们要做的是,如何利用系数表示法与点值表示法的相互转化来实现多项式系数表示法的O(n*logn)的快速乘法

第二节、多项式的快速乘法

在此之前,我们得明白两个概念,求值与插值。通俗的讲,所谓求值(系数->点值),是指系数形式的多项式转换为点值形式的表示方式。而插值(点值->系数)正好是求值的逆过程,即反过来,它是已知点值表示法,而要你转换其为多项式的系数表示法(用n个点值对也可以唯一确定一个不超过n-1次多项式,这个过程称之为插值)。而这个系数表示法与点值表示法的相互转化,即无论是从系数表示法转化到点值表示法所谓的求值,还是从点值表示法转化到系数表示法所谓的插值,求值和插值两种过程的时间复杂度都是O(n*logn)。

前面,我们已经说了,在多项式乘法中,如果用系数表示法表示多项式,那么多项式乘法的复杂度将为O(n^2),而用点值表示法表示的多项式,那么多项式的乘法的复杂度将是线性复杂度为O(n),即: 适当的利用点值表示可以使多项式的乘法可以在线性时间内完成。此时读者可以发挥你的想象,假设我们以下面这样一种过程来计算多项式的乘法(不过在此之前,你得先把两个要相乘的多项式A(x)和B(x)扩充为次数或者说系数为2n次的多项式),我们将会得到我们想要的结果

  1. 系数表示法转化为点值表示法。先用系数表示法表示一个多项式,然后对这个多项式进行求值操作,即多项式从系数表示法变成了点值表示法,时间复杂度为O(n*logn);
  2. 点值表示法计算多项式乘法。用点值表示法表示多项式后,计算多项式的乘法,线性复杂度为O(n);
  3. 点值表示法转化为系数表示法。最终再次将点值表示法表示的多项式转变为系数表示法表示的多项式,这一过程,为O(n*logn)。

那么,综上上述三项操作,系数表示法表示的多项式乘法总的时间复杂度已被我们降到了O(n*logn+n+n*logn)=O(N*logN)。

如下图所示,从左至右,看过去,这个过程即是完成多项式乘法的计算过程。不过,完成这个过程有两种方法,一种就是前面第一节中所说的普通方法,即直接对系数表示法表示的多项式进行乘法运算,复杂度为O(n^2),它体现在下图中得Ordinary multiplication过程。还有一种就是本节上文处所述的三个步骤:1、将多项式由系数表示法转化为点值表示法(点值过程);2、利用点值表示法完成多项式乘法;3、将点值表示法再转化为系数表示法(插值过程)。三个步骤下来,总的时间复杂度为O(N*logN)。它体现在下图中的Evaluation + Pointwise multiplication + Interpolation 三个合过程。

由上图中,你也已经看到了。我们巧妙的利用了关于点值形式的多项式的线性时间乘法算法,来加快了系数形式表示的多项式乘法的运算速度。在此过程中,我们的工作最关键的就在于以O(n*logn)的时间快速把一个多项式从系数形式转换为点值形式(求值,我们即称之为FFT),然后再以O(n*logn)的时间从点值形式转换为系数形式(插值,我们即称之为FFT逆)。最终达到了我们以最终的系数形式表示的多项式的快速乘法为O(N*logN)的时间复杂度。好不令人心生快意。

对上图,有一点必须说明,项w2n是2n次单位复根。且不容忽视的是,在上述两种表示法即系数表示法和点值表示法相互转换的过程中, 都使用了单位复根,才得以在O(n*logn)的时间内完成求值与插值运算(至于何谓单位复根,请参考相关资料。因为为了保证本文的通俗易懂性,无意引出一大堆公式或定理)。

第三节、FFT

注:本节有相当部分文字来自算法导论中文版第二版以及维基百科。

在具体介绍FFT之前,我们得熟悉知道折半定理是怎么一回事儿:如果n>0且n为偶数,那么,n个n次单位复根的平方等于n/2个n/2个单位复根。我们已经知道,通过使用一种称为快速傅里叶变换(FFT)的方法,可以在O(n*logn)的时间内计算出DFTn(a),而若采用直接的方法复杂度为O(n^2)。FFT就是利用了单位复根的特殊性质。

你将看到,FFT方法运用的策略为分治策略,所谓分治,即分而治之。两个多项式A(x)与B(x)相乘的过程中,FFT用A(x)中偶数下标的系数与奇数下标的系数,分别定义了两个新的次数界为n/2的多项式A[0](x)和A[1](x):

A[0](x) =a0 +a2x +a4x2 +··· +an-2xn/2-1,

A[1](x) =a1 +a3x +a5x2 +··· +an-1xn/2-1.

注意,A[0]包含了A中所有偶数下标的系数(下标的相应二进制数的最后一位为0),而A[1]包含A中所有奇数下标的系数(下标的相应二进制数的最后一位为1)。有下式:

这样,求A(x)在处的问题就转换为:

1)求次数界为n/2的多项式A[0]与A[1]在点的值,然后

2)将上述结果进行组合。

下面,我们用N次单位根WN来表示

WN的性质:

  1. 周期性WN具有周期N。
  2. 对称性

为了简单起见,我们下面设待变换序列长度n = 2r。 根据上面单位根的对称性,求级数时,可以将求和区间分为两部分:

Fodd(k)Feven(k)是两个分别关于序列奇数号和偶数号序列N/2点变换。由此式只能计算出yk的前N/2个点,对于后N/2个点,注意Fodd(k)Feven(k) 都是周期为N/2的函数,由单位根的对称性,于是有以下变换公式:

这样,一个N点变换就分解成了两个N/2点变换,照这样可继续分解下去。这就是库利-图基快速傅里叶变换算法的基本原理。此时,我们已经不难分析出此时算法的时间复杂度将为O(NlogN)。

ok,没想到,本文之中还是出现了这么多的公式(没办法,搞这个FFT就是个纯数学活儿。之前买的一本小波与傅里叶分析基础也是如此,就是一本数学公式和定理的聚集书。不过,能看懂更好,实在无法弄懂也只权当做个稍稍了解)。

好了,下面,咱们来简单理解下FFT中的蝶形算法,本文将告结束。如下图所示:

有人这样解释蝶形算法:对于N(2的x次方)点的离散信号,把它按索引位置分成两个序列,分别为0,2,4,....,2K(记为A)和1,3,5,....,2K-1(记为B),由傅立叶变换可以推出N点的傅立叶变换前半部分的结果为A+B*旋转因子,后半部分的结果为A-B*旋转因子。于是求N点的傅立叶变换就变成分别求两个N/2点序列的傅立叶变换,对每一个N/2点的序列,递归前面的步骤,直到只有两点的序列,就只变成简单的加减关系了。把这些点的加减关系用线连接,看上去就是个蝶形。ok,更多可参考算法导论第30章。

举一个例子,我们知道,4X4的矩阵运算如果按常规算法的话仍要进行64次乘法运算和48次加法,这将耗费较多的时间,于是在H.264中,有一种改进的算法(蝶形算法)可以减少运算的次数。这种矩阵运算算法构造非常巧妙,利用构造的矩阵的整数性质和对称性,可完全将乘法运算转化为加法运算。如下图所示:

下面的代码来自一本数字图像处理的书上的源代码:

VOID WINAPI FFT(complex<double> * TD, complex<double> * FD, int r)
{
	// 付立叶变换点数
	LONG count;

	// 循环变量
	int i,j,k;

	// 中间变量
	int bfsize,p;

	// 角度
	double angle;

	complex<double> *W,*X1,*X2,*X;

	// 计算付立叶变换点数
	count = 1 << r;

	// 分配运算所需存储器
	W = new complex<double>[count / 2];
	X1 = new complex<double>[count];
	X2 = new complex<double>[count];

	// 计算加权系数
	for(i = 0; i < count / 2; i++)
	{
		angle = -i * PI * 2 / count;
		W[i] = complex<double> (cos(angle), sin(angle));
	}

	// 将时域点写入X1
	memcpy(X1, TD, sizeof(complex<double>) * count);

	// 采用蝶形算法进行快速付立叶变换
	for(k = 0; k < r; k++)
	{
		for(j = 0; j < 1 << k; j++)
		{
			bfsize = 1 << (r-k);
			for(i = 0; i < bfsize / 2; i++)
			{
				p = j * bfsize;
				X2[i + p] = X1[i + p] + X1[i + p + bfsize / 2];
				X2[i + p + bfsize / 2] = (X1[i + p] - X1[i + p + bfsize / 2]) * W[i * (1<<k)];
			}
		}
		X = X1;
		X1 = X2;
		X2 = X;
	}

	// 重新排序
	for(j = 0; j < count; j++)
	{
		p = 0;
		for(i = 0; i < r; i++)
		{
			if (j&(1<<i))
			{
				p+=1<<(r-i-1);
			}
		}
		FD[j]=X1[p];
	}

	// 释放内存
	delete W;
	delete X1;
	delete X2;
}

待我日后继续斟酌,补充。完。

updated:关于快速傅立叶变换(FFT)的C++实现与Matlab实验,这里有一篇不错的文章,读者可以看看:http://blog.csdn.net/rappy/article/details/17008292012.03.03

分享到:
评论

相关推荐

    用快速傅里叶变换实现多项式相乘

    VC++6.0编写的用快速傅里叶变换实现多项式相乘,其中变量值用的是整数,不存在精度丢失的问题。

    fft快速傅里叶变换

    此后,在这思想基础上又开发了高基和分裂基等快速算法,随着数字技术的高速发展,1976年出现建立在数论和多项式理论基础上的维诺格勒傅里叶变换算法(WFTA)和素因子傅里叶变换算法。它们的共同特点是,当N是素数时,...

    DFT的matlab源代码-dft:在Haskell中快速,肮脏地实现离散傅立叶变换(DFT)。经过多项式乘法测试

    离散傅立叶变换(DFT) 执行 在Haskell中快速而肮脏的实现。 该算法是使用的简化版本实现的。 实现是。 测验 通过简单地检查逆dft和dft的组合是否产生与提供的输入相同的结果,即可测试DFT,逆DFT和基础FFT算法。 ...

    多项式乘法

    详细介绍了快速傅里叶变换算法,是快速傅里叶变换算法入门的经典!

    十五个经典算法研究与总结、目录+索引(定稿版)

    前言: 本人的原创作品经典算法研究系列,自从10年12月末至11年12月,写了近一年。可以这么说,开博头俩个月一直在整理微软等公司的面试题,而后的四个月至今,则断断续续...十五、多项式乘法与快速傅里叶变换

    克莱姆法则MATLAB代码-Numerical-Analysis-Examples:多种语言的数值分析实现

    克莱姆法则MATLAB代码数值分析示例 注意:您可以用任何语言实现贡献。 1 方程解 1.1 迭代方法 ...快速傅立叶变换 9 插值 9.1 拉格朗日多项式插值 拉格朗日插值算法 内维尔插值算法 9.2 三次样条插值

    algorithm-swift:Udacity课程以Swift编写的算法-研究生算法简介

    Swift中的算法第1课-斐波那契最长的...几何级数之和第4课-多项式乘法快速傅立叶变换-FFT第5课-使用FFT的多项式乘法第1课-深度优先搜索(DFS) 广度优先搜索(BFS) 拓扑排序通过DFS查找强连接的组件(SCC)第2课-2-可

    libfqfft-windows:libfqfft,但可以git克隆到Windows

    libfqfft有限域中FFT的C ++库libfqfft是一个C ++库,用于通过多线程支持(通过OpenMP)在有限字段中进行快速傅立叶变换(FFT )。 该库由及其贡献者开发(请参阅文件),并根据MIT许可证发行(请参阅文件)。 该库...

    OpenSAL1.0

    代数算法:霍纳法则计算多项式和、矩阵乘法(2种)、方阵的LUP分解、解线性方程组(2种)、矩阵求逆(2种)、求伪逆矩阵(2种)、解正态方程组(2种)、最小二乘估计(2种)、多元最小二乘估计*、快速傅里叶变换、...

    OpenSAL1.1

    代数算法:方幂和、霍纳法则计算多项式和、矩阵乘法(2种)、方阵的LUP分解、解线性方程组(2种)、矩阵求逆(2种)、求伪逆矩阵(2种)、解正态方程组(2种)、最小二乘估计(2种)、快速傅里叶变换、快速傅里叶逆...

    C++开源算法库OpenSAL1.1(Open Standardized Algorithm Library) ——静态链接库

    代数算法:霍纳法则计算多项式和、矩阵乘法(2种)、方阵的LUP分解、解线性方程组(2种)、矩阵求逆(2种)、求伪逆矩阵(2种)、解正态方程组(2种)、最小二乘估计(2种)、多元最小二乘估计*、快速傅里叶变换、...

    C++开源算法库OpenSAL1.1(Open Standardized Algorithm Library)——动态链接库

    代数算法:霍纳法则计算多项式和、矩阵乘法(2种)、方阵的LUP分解、解线性方程组(2种)、矩阵求逆(2种)、求伪逆矩阵(2种)、解正态方程组(2种)、最小二乘估计(2种)、多元最小二乘估计*、快速傅里叶变换、...

    论文研究-基于MEA算法的RS(255,223)码的译码软件实现.pdf

    遵循有限域上多项式的运算规则,使用MATLAB软件设计了GF(28)上的加法、乘法、求逆运算模块,并以这些模块为基础,采用修正的欧几里德算法(MEA)与有限域上快速傅立叶变换算法相结合的思想,实现了RS(255,223)...

    OpenSAL1.1算法导论开源算法库

    代数算法:霍纳法则计算多项式和、矩阵乘法(2种)、方阵的LUP分解、解线性方程组(2种)、矩阵求逆(2种)、求伪逆矩阵(2种)、解正态方程组(2种)、最小二乘估计(2种)、多元最小二乘估计*、快速傅里叶变换、...

    常用算法程序集(C语言描述)(第三版)+完整源代码

    13.2 快速傅立叶变换 13.3 快速袄什变换 13.4 五点三次平滑 13.5 离散随机线性系统的卡尔曼滤波 13.6 α-β-γ滤波 第14章 特殊函数的计算 14.1 伽马函数 14.2 不完全伽马函数 14.3 误差函数 14.4 第一类整数阶...

    应用数值线性代数 英文版 Applied Numerical Linear Algebra / James W. Demmel

     6.7.4 计算快速傅里叶变换  6.8 块循环约化  6.9 多重网格法  6.9.1 二维泊松方程多重网格法概述  6.9.2 一维泊松方程的多重网格法详述  6.10 区域分解法  6.10.1 无交叠方法  6.10.2 交叠方法  ...

    算法引论:一种创造性方法.[美]Udi Manber(带详细书签).pdf

    9.4 多项式乘法 9.5 矩阵乘法 9.5.1 Winograd算法 9.5.2 Strassen算法 9.5.3 布尔矩阵 9.6 快速傅里叶变换 9.7 小结 第10章 归约 10.1 引言 10.2 归约的例子 10.2.1 简单字符串匹配问题 10.2.2 特殊代表...

    组合数学(第4版) 卢开澄 卢华明

    8.7 快速傅里叶变换 8.7.1 问题的提出 8.7.2 预备定理 8.7.3 快速算法 8.7.4 复杂性分析 8.8 dfs算法 8.9 bfs算法 8.10 αβ剪技术 8.11 状态与图 8.12 分支定界法 8.12.1 tsm问题 8.12.2 任务安排问题 ...

    算法导论中文版

    第30章 多项式与快速傅里叶变换  30.1 多项式的表示  30.2 DFT与FFT  30.3 高效FFT实现  思考题  本章注记 第31章 数论算法  31.1 基础数论概念  31.2 最大公约数  31.3 模运算  31.4 求解模线性...

    GPS信号模拟器中sine存储表的设计和实现 (2005年)

    用快速傅里叶变换来分析杂散水平,利用matlab计算工具对整个算法进行了建模、优化和验证,仿真表明映射表采用该算法设计的DDS数字载波部分的最大杂散约为-90dB,且有良好的压缩效率。该算法实现结构简单,没有乘法...

Global site tag (gtag.js) - Google Analytics