多项式算法初探:从 FFT 到 FWT(目前只有FFT)

多项式一向是算法竞赛中相当博大精深的东西,作为一个蒟蒻,我将会以最大的努力完成这篇记录,以防自己以后看不懂qwq。


FFT(快速傅里叶变换)

FFT 是一种可以在 \(O(n\log n)\) 的时间内完成多项式乘法的算法。这个算法的劣势在于精度。

我将会从复数、DFT、FFT 和 IFFT 四个部分完成对 FFT 的讲解。

复数

在日常的学习生活中(这是真的),我们常会遇到一个神奇的字母:\(i\)。我们规定 \(i\) 为虚数单位,他满足 \(i^2=-1\)

那么我们假如想要表示一个复数,我们就可以写为 \(a+bi\),其中 \(a,b\) 为实数。前半部分我们称之为实部,后半部分我们称之为虚部。

那我们就可以开始定义虚数的运算了:

\((a+bi)+(c+di)=(a+c)+(b+d)i\)
\((a+bi)-(c+di)=(a-c)+(b-d)i\)
\((a+bi)\times(c+di)=(ac-bd)+(ad+bc)i\)
\(\dfrac{(a+bi)}{(c+di)}=\dfrac{(ac+bd)}{(c^2+d^2)}+\dfrac{(bc-ad)}{(c^2+d^2)}i\)

假如我们建立一个平面直角坐标系,横轴表示实部,纵轴表示虚部,我们就可以建立一个复平面,复平面上的每一个点都可以表示一个复数。

我们连接复平面上的点 \((a,b)\)(实际上表示复数 \(z=a+bi\))和原点,这条线段的长度即为 \(\sqrt{a^2+b^2}\),我们称这条线段的长度为模,表示为 \(|z|\)

那么对于一个复数 \(z=a+bi\),我们称他的共轭复数 \(\overline{z}=a-bi\)。当 \(|z|=1\) 时,\(\overline{z}=\dfrac 1z\)。下给出证明:

\[z\times\overline{z}=(a+bi)\times(a-bi)=a^2+b^2=|z|=1 \]

在复数中,有一类数被称为单位根。若复数 \(\omega^n=1\),我们称 \(\omega\)\(n\) 次单位根。

我们如何找单位根呢?这就需要用到复平面了。我们以原点为圆心,画一个半径为 \(1\) 的圆,在满足实部正半轴是其中一条平分线的情况下将整个圆分成 \(n\) 份,每条平分线在圆上的端点所表示的复数,就是一个 \(n\) 次单位根。我们称圆与实部正半轴的交点所表示的复数(其实就是 \(1\))为 \(\omega^0_n\),从 \(\omega^0_n\) 逆时针方向数,依次是 \(\omega^1_n,\omega^2_n,\dots,\omega^{n-1}_n\)。可以结合下面这张图理解(图片来源:https://blog.csdn.net/Flag_z/article/details/99163939)。

满足如下几条性质:

  1. \(\omega^k_n=(\omega^1_n)^k\)
  2. \(\omega^k_n=\omega^{2k}_{2n}\)
  3. \(\omega^0_n=\omega^n_n\)
  4. \(\omega^k_n=-\omega^{k+\frac n2}_n\)
  5. \(\omega^k_n=(\cos(\frac{2\pi k}n)+(\sin(\frac{2\pi k}n))i\)
  6. \(\sum\limits_{i=0}^{n-1}\omega^i_n=0\)

这些公式看上面这张图,应该都很容易推出来。

好的,复数的前置知识到此为止,接下来,我们就将进入正题。

DFT(离散傅里叶变换)

对于一个多项式 \(f(x)=\sum\limits_{i=0}^n a_ix^i\),我们可以用 \(n\) 个不同的在这张函数图像上的点来表示。如我们选取 \(x_1,x_2,\dots,x_n\) 带入 \(f(x)\) 中,就会得到点 \((x_1,f(x_1)),(x_2,f(x_2)),\dots,(x_n,f(x_n))\),而这些点就会对应且仅对应 \(f(x)\) 这个函数。这被称为函数的点值表示法。而从正常的多项式表达形式转化为点值表示法,时间复杂度是 \(O(n^2)\) 的。

那么,点值表示法有什么性质呢?

容易发现,对于 \(x_0\),有 \(f(x_0)\times g(x_0)=(f\times g)(x_0)\)。这也就意味着,只要我们将两个多项式 \(f(x),g(x)\) 带入相同的 \(n\)\(x\) 值,再将对应点值的 \(y\) 值相乘,再将点值表示法转化为普通形式(应该是可以拉格朗日插值做到 \(O(n^2)\) 的),我们就完成了多项式乘法。

有什么用呢?常数甚至更大了……

此时,伟大的数学家傅里叶先生提出了离散傅里叶变换。他使用了 \(\omega^k_n\) 作为 \(x\) 值进行带入。虽然时间复杂度还是 \(n^2\),而且常数更大了,但也为我们建立 FFT 奠定基础。

FFT(快速傅里叶变换)

我们考虑将 \(f(x)=\sum\limits_{i=0}^{n-1} a_ix^i\) 分成奇偶两个部分 \(f_0(x),f_1(x)\)(不妨设 \(n\bmod 2=0\)),满足 \(f(x)=f_0(x)+x\times f_1(x)\),那么这两个函数就长成下面这个样子:

\[f_0(x)=\sum_{i=0}^{\frac n2-1}a_{2i}x^{2i},f_1(x)=\sum_{i=0}^{\frac n2-1}a_{2i+1}x^{2i+1} \]

我们就把问题分成了两个部分。相当于我们求出了 \(\omega^1_{\frac 2n},\omega^2_{\frac 2n},\dots,\omega^{\frac 2n-1}_{\frac 2n}\) 的点值,现在要推导到 \(\omega^1_n,\omega^2_n,\dots,\omega^{n-1}_n\) 的点值。

那我们来推一推式子:

\[f(\omega^k_n)=f_0(\omega^{2k}_n)+\omega^k_n\times f_1(\omega^{2k}_n) \]

\[=f_0(\omega^k_{\frac n2})+\omega^k_n\times f_1(\omega^k_{\frac n2}) \]

\[f(\omega^{k+\frac n2}_n)=f_0(\omega^{2k+n}_n)+\omega^{k+\frac n2}_n\times f_1(\omega^{2k+n}_n) \]

\[=f_0(\omega^k_{\frac n2})-\omega^k_n\times f_1(\omega^k_{\frac n2}) \]

那这样就可以在 \(O(n)\) 的时间复杂度内推导了。这个操作有一个好听的名字,叫做蝴蝶变换。

我们使用递归分治的方法,每一层的总时间复杂度为 \(O(n)\),一共有 \(O(\log n)\) 层,时间复杂度即为 \(O(n\log n)\)

当然这里还有一个注意事项:考虑到每一层的 \(n\) 都得是偶数,相当于 \(n\) 必须要能表示为 \(2^x\)。考虑在前面补零即可。时间复杂度不变。

但是递归时间复杂度超大,我们难以承受。考虑采取迭代法。即我们先将单位元按照最终位置进行放置,然后从下层向上层迭代,这样常数可以小很多。

举个例子,当 \(n=8\) 时,最终形态为:

\[\omega^0_n\ \omega^4_n\ \omega^2_n\ \omega^6_n\ \omega^ 1_n\ \omega^5_n\ \omega^3_n\ \omega^7_n\]

我们先把最终形态摆出来,然后再依次向上合并。

至于说具体过程,可以根据奇偶分组进行模拟。

IFFT(快速傅里叶逆变换)

我们刚才说了拉格朗日插值可以 \(O(n^2)\) 求解,但是这个思路大没前途,所以考虑深入挖掘 \(\omega\) 的性质:

当我们将 \(f(x)\) 进行 FFT 后的结果作为 \(g(x)\) 的系数,将单位根取倒数,也就是 \(\omega^0_n,\omega^{-1}_n,\dots,\omega^{1-n}_n\)。再将这些数带入 \(g(x)\) 中,进行一次快速傅里叶变换,再将所有数 \(\times\frac 1n\),得到的就是 \(f(x)\) 的各项系数。

假如上述性质成立,那么我们只需要再做一次 FFT,就可以完成 IFFT。

下给出证明:

\((y_0,y_1,\dots,y_{n-1})\) 为多项式 \(f(x)=\sum\limits_{i=0}^{n-1}a_ix^i\) 的 DFT,设多项式 \(g(x)=\sum\limits_{i=0}^{n-1}y_ix^i\) 带入 \((\omega^0_n,\omega^{-1}_n,\dots,\omega^{1-n}_n)\) 的 DFT 为 \((z_0,z_1,\dots,z_{n-1})\),则有:

\[z_k=\sum_{i=0}^{n-1}y_i(\omega^{-k}_n)^i \]

\[=\sum_{i=0}^{n-1}(\sum_{j=0}^{n-1}a_j\times(\omega^i_n)^j)(\omega^{-k}_n)^i \]

\[=\sum_{j=0}^{n-1}a_j\times(\sum_{i=0}^{n-1}(\omega^i_n)^{j-k}) \]

\(j-k=0\) 时,易得 \(\sum_{i=0}^{n-1}(\omega^i_n)^{j-k}=n\),否则,设 \(d=\gcd(j-k,n)\)

\[\sum_{i=0}^{n-1}(\omega^i_n)^{j-k}=\sum_{i=0}^{n-1}\omega^{(j-k)i}_n=d\sum_{i=0}^{\frac nd-1}\omega^i_{\frac nd}=0 \]

所以,\(z_k=na_k,a_k=\frac{z_k}n\)

由于单位根的模都为 \(1\),所以单位根的模就是他的共轭复数。

下给出模板题代码:

#include<bits/stdc++.h>
using namespace std;
const int N=2097153;
const double pi=acos(-1);
int n,m,rev[N];
struct comn{double a,b;}f[N],g[N];
comn operator+(comn x,comn y){return {x.a+y.a,x.b+y.b};
}comn operator-(comn x,comn y){return {x.a-y.a,x.b-y.b};
}comn operator*(comn x,comn y){return {x.a*y.a-x.b*y.b,x.b*y.a+x.a*y.b};
}void operator+=(comn &x,comn y){x=x+y;}
void operator-=(comn &x,comn y){x=x-y;}
void operator*=(comn &x,comn y){x=x*y;}
void init(int k,int len){for(int i=0;i<len;i++)rev[i]=(rev[i>>1]>>1)|((i&1)<<(k-1));
}void fft(comn *a,int n,int fl){for(int i=0;i<n;i++)if(i<rev[i]) swap(a[i],a[rev[i]]);comn om={cos(pi),fl*sin(pi)},w={1,0};for(int i=1;i<n;i*=2,om={cos(pi/i),fl*sin(pi/i)})for(int j=0;j<n;j+=i*2,w={1,0})for(int k=j;k<j+i;k++){comn x=a[k],y=w*a[k+i];a[k]+=y,a[k+i]=x-y,w*=om;}
}int main(){ios::sync_with_stdio(0);cin.tie(0),cout.tie(0);cin>>n>>m;int k=0,mx=1;while(mx<=n+m) mx*=2,k++;for(int i=0;i<=n;i++) cin>>f[i].a;for(int i=0;i<=m;i++) cin>>g[i].a;init(k,mx),fft(f,mx,1),fft(g,mx,1);for(int i=0;i<mx;i++) f[i]*=g[i];fft(f,mx,-1);for(int i=0;i<=n+m;i++)cout<<(int)(f[i].a/mx+0.5)<<" ";return 0;
}//fast fourier transform

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/871173.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

漏洞预警 | WordPress Plugin Radio Player SSRF漏洞

0x00 漏洞编号CVE-2024-543850x01 危险等级高危0x02 漏洞概述 WordPress插件Radio Player是一种简单而有效的解决方案,用于将实时流媒体音频添加到您的WordPress网站。0x03 漏洞详情CVE-2024-54385漏洞类型:SSRF 影响:获取敏感信息 简述:Radio Player的/wp-admin/admin-aja…

从零开始的PHP原生反序列化漏洞

1、写在前面 OK 兄弟们,这几天一直在面试,发现很多 HR 喜欢问反序列化相关的内容,今天咱们就从最简单的 PHP 原生反序列化入手,带大家入门反序列化 2、PHP 序列化 在 PHP 中,有反序列化,就有序列化,我们先来解释一下序列化。 所谓序列化,就是将 PHP 的一个对象,序列化…

简单讲一下免杀的一个思想

这几天一直在忙,没时间学新东西,也不知道写什么,正好今天有人跟我要免杀,cs 二开的东西,这里就水一篇文章,带各位入门免杀,建立一个免杀思路。 1、什么是免杀? 首先各位需要了解一下,免杀的基本概念免杀,全称为反杀毒技术,用来使木马病毒程序逃过杀毒软件的检测免杀…

读量子霸权07光合作用

光合作用转化CO2、阳光、水为糖和氧气,对地球生命至关重要。量子计算机或能揭秘光合作用,助力高效光伏电池和作物增产。人工光合作用和“人工树叶”或对抗全球变暖,量子计算机或加速其进展。1. 光合作用 1.1. 生命起源的一幕主要戏剧便是光合作用,一个看似简单的过程1.1.1.…

关于网传微信聊天记录提取工具留痕盗取个人信息的分析

今天早上看到一篇文章,是关于一个微信聊天记录提取工具泄露个人信息的内容,于是我就好奇,看了一下作者的 github,然后也是自己小小的分析了一下 1、官方地址 Github: https://github.com/LC044/WeChatMsg 2、作者自证 url:https://github.com/LC044/WeChatMsg/issues/4923…

2024 11~12 月 做题记录(待更新)

普通和理所当然是什么呢CF2047D Move Back at a Cost 要使字典序最大,每次都要找到最小的数,把它前面的数都后移. 因为可以钦定后移的顺序使得后移的数按升序排列,所以每个数最多被移位一次. 定序后开两个队列模拟即可. CCPC2024 上海F 羁绊大师 将羁绊相同的英雄相连,因为…

看完这章你也会黑盒edu通杀

负责声明: 请勿利用文章内的相关技术从事非法测试,由于传播、利用此文所提供的信息或者工具而造成的任何直接或者间接的后果及损失,均由使用者本人负责,所产生的一切不良后果与文章作者无关。该文章仅供学习用途使用 信息收集: 在一个阳光明媚的中午一位刚吃完午饭的安服仔…

记一次对某学校挖矿木马的应急响应

本来今天高高兴兴,期待着明天的疯狂星期四,但客户那边突然有一台主机需要应急,那就上去看一眼。 1、事件背景 这次设备上有报警,发现是挖矿木马,并且也捕捉到了外联 IOC,那这问题就不大了,直接上机开搞!恶意 IOC:217.160.36.1592、上机处置 首先对这台主机的外联情况查…

Prometheus +VictoriaMetrics+Granafa安装部署

测试环境 prometheus-2.54.1.linux-amd64.tar.gz 下载地址: https://www.prometheus.io/download/ https://github.com/prometheus/prometheus/releases/download/v2.54.1/prometheus-2.54.1.linux-amd64.tar.gz node_exporter-1.8.2.linux-amd64.tar.gz 下载地址: https://g…

服务器被攻击,为什么硬防不起作用?

当服务器遭受攻击时,即使配备了硬件防护设备(如 100G 硬防),仍然可能出现性能下降或无法访问的情况。以下是详细的解释和解决方案:理解硬件防护的作用:硬件防护设备主要用于抵御大流量攻击(如 DDoS 攻击),通过清洗恶意流量来保护服务器。然而,它并不能完全消除所有类…

【ESP 乐鑫相关】ESP32-S3启动流程

转载自:https://blog.itpub.net/70040860/viewspace-3053923/ ESP32-S3启动流程本文将会介绍ESP32-S3从上电到运行app_main函数中间所经历的步骤(即启动流程)。从宏观上,该启动流程可分为如下3个步骤。①:一级引导程序,它被固化在ESP32-S3内部的ROM中,它会从flash的0x00…

80端口对外网访问受限,如何解除限制?

您好!当您发现配置了80端口的Nginx服务虽然可以在本地正常访问,但从外部网络却无法访问时,这通常是由于防火墙规则、安全组策略或其他网络配置不当所造成的。以下是详细的排查步骤和解决方案,帮助您解除80端口的访问限制:检查服务器防火墙设置: 首先,确认服务器上的防火…