对于输入序列长度为
N
N
N 的FFT计算,它的运算复杂度可以看做是
O
(
N
log
N
)
O(N\log N)
O(NlogN),其中
log
N
\log N
logN是计算FFT的迭代次数。当输入序列中存在大量为零的元素时,我们可以通过以下两种方式来计算FFT的运算复杂度:
从输入序列的角度考虑。如果输入序列中的大多数元素都为零,我们可以用稀疏矩阵的概念来描述这个序列,即将这个序列视为一个矩阵中的一行,其中大部分元素都是零。在这种情况下,我们可以使用基于稀疏矩阵的FFT算法来计算FFT,这样可以将计算FFT的时间复杂度降到
O
(
N
log
2
N
)
O(N\log^2 N)
O(Nlog2N) 级别。但是需要注意,基于稀疏矩阵的FFT算法只对空间复杂度有要求,并不一定能够提高时间复杂度。
从FFT算法的角度考虑。如果输入序列中存在很多为零的元素,那么在计算FFT的过程中,有一些计算是可以优化的,因为一个数的乘积是零时,整个和式也一定是零。因此,我们可以在计算FFT时,特判输入序列中的零元素,将它们在计算过程中直接忽略掉,这样可以将计算FFT的时间复杂度降到
O
(
N
log
M
)
O(N\log M)
O(NlogM) 级别,其中
M
M
M 是输入序列中非零元素的个数。但是需要注意,这种优化在实践中并不一定会提高计算速度。因为在实际应用中,输入序列中存在大量为零的元素的情况并不是很常见,而在处理稀疏数据时,往往需要用到一些专门的算法,比如 COO 格式、CSR 格式等,这些算法和FFT计算的时间复杂度没有相互关系。
3、这里的时间复杂度
O
(
N
log
M
)
O(N\log M)
O(NlogM) 是如何推导出来的?
在计算FFT的过程中,我们对输入序列进行了重排,将位置对应的元素分到了不同的组中进行计算。在计算过程中,如果一个位置对应的元素为零,那么在该位置所在的组中计算的加法和乘法都可以被直接忽略掉。因此,如果输入序列中有
M
M
M 个非零元素,那么在计算FFT时,我们只需要对这
M
M
M 个非零元素进行计算就可以了,其余的元素直接跳过即可。
设
N
N
N 为输入序列的长度,
M
M
M 为非零元素的个数。在采用上述优化后,计算FFT的时间复杂度可以分为两个部分:
1.重排序列的时间复杂度为
O
(
N
)
O(N)
O(N)。
2.计算FFT的时间复杂度为
O
(
M
log
M
)
O(M\log M)
O(MlogM)。
因此,总的时间复杂度为
O
(
N
+
M
log
M
)
O(N+M\log M)
O(N+MlogM),但是由于
M
≤
N
M\leq N
M≤N,因此该算法的时间复杂度可以简化为
O
(
N
log
M
)
O(N\log M)
O(NlogM)。值得注意的是,
M
M
M 的大小并不是常数,取决于输入序列中有多少个非零元素,因此这种优化算法的最差时间复杂度仍然为
O
(
N
log
N
)
O(N\log N)
O(NlogN)。
4、
O
(
N
+
M
log
M
)
O(N+M\log M)
O(N+MlogM)复杂度可以简化成
O
(
N
log
M
)
O(N\log M)
O(NlogM)吗,数量关系是怎么来的?
这个简化的复杂度分析是有问题的,可能是存在一些混淆概念。具体来说,
N
N
N 和
M
M
M 在这里应该指的是什么是不清楚的。以下是一种可能的分析方法。
假设输入序列的长度为
N
N
N,其中非零元素的个数为
M
M
M,且
M
M
M 是
N
N
N 的一个较小的常数倍。在这种情况下,如果我们采取特判输入序列中的零元素的策略,那么计算FFT的时间复杂度可以分为两个部分:
重排序列的时间复杂度为
O
(
N
)
O(N)
O(N)。
计算FFT的时间复杂度为
O
(
M
log
M
)
O(M\log M)
O(MlogM)。
因此,总的时间复杂度为
O
(
N
+
M
log
M
)
O(N+M\log M)
O(N+MlogM)。
不过需要注意的是,
N
N
N 和
M
M
M 都在考虑中,因此将其简化成
O
(
N
log
M
)
O(N\log M)
O(NlogM) 是不严谨的。而且即便存在
M
=
O
(
1
)
M = O(1)
M=O(1) 的情况,此时算法的时间复杂度还是
O
(
N
)
O(N)
O(N),因为重排序的时间复杂度仍然与输入序列长度成线性关系。
因此,是否能够将
O
(
N
+
M
log
M
)
O(N+M\log M)
O(N+MlogM) 的复杂度简化为
O
(
N
log
M
)
O(N\log M)
O(NlogM),取决于我们定义
N
N
N 和
M
M
M 的具体含义。如果
M
M
M 只表示输入序列中非零元素的个数,那么
N
N
N 和
M
M
M 的比例可能非常大,这种简化是不合适的。但是如果
M
M
M 是
N
N
N 的一个较小的常数倍,那么
O
(
N
+
M
log
M
)
O(N+M\log M)
O(N+MlogM) 的复杂度确实可以用
O
(
N
log
M
)
O(N\log M)
O(NlogM) 来简化。
5、大部分信号点为0的情况下,fft的运算复杂度,列出fft的复数乘法运算次数和复数加法运算次数。
假设输入序列的长度为
N
N
N,其中非零元素的个数为
M
M
M。在大部分信号点为零的情况下,FFT计算的时间复杂度可以优化为
O
(
N
log
M
)
O(N\log M)
O(NlogM)。下面分别列出复数乘法的运算次数和复数加法的运算次数:
复数乘法运算次数: 每个迭代步骤需要进行
N
/
2
N/2
N/2 次复数乘法运算,一共有
log
2
N
\log_2 N
log2N 个迭代步骤。即总复杂度为
O
(
N
log
N
)
O(N\log N)
O(NlogN) 的FFT算法需要进行
N
log
N
N\log N
NlogN 次复数乘法运算。在大部分信号点为零的情况下,所需计算的非零乘积个数为
M
log
2
M
M\log_2 M
Mlog2M,因此,采用特判输入序列中的零元素的优化方法后,FFT算法的总复数乘法运算次数为
O
(
M
log
M
)
O(M\log M)
O(MlogM)。
复数加法运算次数: 每个迭代步骤需要进行
N
/
2
N/2
N/2 次复数加法运算,一共有
log
2
N
\log_2 N
log2N 个迭代步骤。因此,总复杂度为
O
(
N
log
N
)
O(N\log N)
O(NlogN) 的FFT算法需要进行
N
log
N
N\log N
NlogN 次复数加法运算。在大部分信号点为零的情况下,输入序列中非零元素的个数为
M
M
M,则采用特判输入序列中的零元素的优化方法后,FFT算法的总复数加法运算次数最多为
2
M
log
2
M
2M\log_2 M
2Mlog2M,因为一次加法有可能被用了两次。
假设在FFT计算过程中,输入序列中非零元素的个数依次为
M
1
,
M
2
,
…
,
M
N
M_1, M_2, \ldots, M_N
M1,M2,…,MN,其中
M
1
=
1
M_1=1
M1=1,
M
N
=
M
M_N=M
MN=M,
M
M
M表示输入序列中非零元素的总个数。并且假设每个非零元素都参与了所有迭代步骤的计算。
在这种情况下,FFT的计算复杂度为
O
(
N
M
log
M
)
O(NM\log M)
O(NMlogM),其中
M
log
M
M\log M
MlogM 表示计算
M
M
M 个非零元素的FFT的复杂度,
N
M
NM
NM 表示总共需要迭代
N
N
N 次,每次迭代中非零元素的个数为
M
i
M_i
Mi,需要进行
M
i
M_i
Mi 次复杂度为
O
(
log
M
i
)
O(\log M_i)
O(logMi) 的FFT计算。
复数乘法的运算次数可以用相同的方式计算出来。在计算
M
i
M_i
Mi 个非零元素的FFT过程中,每个非零元素都需要参与
N
N
N 次复数乘法的计算,因此总复数乘法运算次数为
O
(
N
M
log
M
)
O(NM\log M)
O(NMlogM)。
复数加法的运算次数与复数乘法的运算次数类似,也可以用相同的方式计算出来。在计算
M
i
M_i
Mi 个非零元素的FFT过程中,每个非零元素都需要参与
N
N
N 次复数加法的计算,因此总复数加法运算次数也为
O
(
N
M
log
M
)
O(NM\log M)
O(NMlogM)。
需要注意的是,由于每个非零元素不一定在所有位置都参与计算,因此实际的复杂度可能低于
O
(
N
M
log
M
)
O(NM\log M)
O(NMlogM)。如果存在一些特殊的输入序列,其非零元素的位置和值的分布比较规则,那么可能会出现一些优化算法,可以进一步降低FFT计算的复杂度。
假设在FFT计算过程中,输入序列中非零元素的个数依次为
M
1
,
M
2
,
…
,
M
N
M_1, M_2, \ldots, M_N
M1,M2,…,MN,其中
M
1
=
1
M_1=1
M1=1,
M
N
=
N
M_N=N
MN=N。并且假设每个非零元素都参与了所有迭代步骤的计算。
在这种情况下,FFT的计算复杂度为
O
(
N
2
log
N
)
O(N^2\log N)
O(N2logN),其中
N
2
N^2
N2 表示总共需要迭代
N
N
N 次,每次迭代中非零元素的个数为
M
i
=
i
M_i=i
Mi=i,需要进行
i
i
i 次复杂度为
O
(
log
i
)
O(\log i)
O(logi) 的FFT计算。另外,由于
N
N
N 个非零元素都在最终的结果中参与计算,因此最后还需要进行一次
N
N
N 个元素的FFT计算。
复数乘法的运算次数可以用相同的方式计算出来。在计算
M
i
=
i
M_i=i
Mi=i 个非零元素的FFT过程中,每个非零元素都需要参与
N
N
N 次复数乘法的计算,因此总复数乘法运算次数为
O
(
N
2
log
N
)
O(N^2\log N)
O(N2logN)。
复数加法的运算次数与复数乘法的运算次数类似,也可以用相同的方式计算出来。在计算
M
i
=
i
M_i=i
Mi=i 个非零元素的FFT过程中,每个非零元素都需要参与
N
N
N 次复数加法的计算,因此总复数加法运算次数也为
O
(
N
2
log
N
)
O(N^2\log N)
O(N2logN)。
9、假设,第一个迭代的计算次数为
O
(
1
log
1
)
O(1\log 1)
O(1log1),第二次迭代的计算次数为
O
(
2
log
2
)
O(2\log 2)
O(2log2),以此类推,第
N
N
N次的计算系数是
O
(
N
log
N
)
O(N\log N)
O(NlogN),那么总的计算复杂度应该是
O
(
1
log
1
+
2
log
2
+
…
+
N
log
N
)
O(1\log 1 + 2\log 2 + … + N\log N)
O(1log1+2log2+…+NlogN),这个求和的式子不才是总计算复杂度吗?
您的求和式子是正确的,它表示FFT算法总的计算复杂度。但是根据
O
O
O符号定义的惯例,我们通常省略常数因子和低阶项,只考虑高阶项。这是因为,在实际问题中,常数和低阶项在问题规模足够大的时候往往变得微不足道,因此对算法的实际效率影响不大。因此,FFT算法的总的计算复杂度通常表示为
O
(
N
log
N
)
O(N\log N)
O(NlogN)。
另外,您的求和式子可进一步转化为
∑
i
=
1
N
i
log
i
=
N
log
N
+
O
(
N
)
\sum_{i=1}^N i\log i=N\log N + O(N)
∑i=1Nilogi=NlogN+O(N),其中
O
(
N
)
O(N)
O(N)可以忽略不计。因此,FFT算法的总的计算复杂度不仅是
O
(
N
log
N
)
O(N\log N)
O(NlogN),而且系数是
N
N
N,也就是说,比起一般情况下的
O
(
N
log
N
)
O(N\log N)
O(NlogN),FFT算法的系数较小,实际上是一个非常高效的算法。
10、
∑
i
=
1
N
i
log
i
\sum_{i=1}^N i\log i
∑i=1Nilogi 这个式子求和最终的结果是什么?
∑
i
=
1
N
i
log
i
\sum_{i=1}^N i\log i
∑i=1Nilogi 的求和结果是
N
log
N
−
N
+
O
(
log
N
)
N\log N - N + O(\log N)
NlogN−N+O(logN)。这个结论可以通过分部求和和斯特林公式得到。
首先,我们可以对
∑
i
=
1
N
i
log
i
\sum_{i=1}^N i\log i
∑i=1Nilogi 进行分部求和,得到:
∑
i
=
1
N
i
log
i
=
∑
i
=
1
N
log
i
⋅
∑
j
=
1
i
j
=
∑
i
=
1
N
log
i
⋅
i
(
i
+
1
)
2
=
1
2
∑
i
=
1
N
(
i
2
+
i
)
log
i
=
1
2
(
∑
i
=
1
N
i
2
log
i
+
∑
i
=
1
N
i
log
i
)
\begin{aligned} \sum_{i=1}^N i\log i &= \sum_{i=1}^N \log i \cdot \sum_{j=1}^i j \\ &= \sum_{i=1}^N \log i \cdot \frac{i(i+1)}{2} \\ &= \frac{1}{2} \sum_{i=1}^{N} (i^2+i)\log i \\ &= \frac{1}{2} \left(\sum_{i=1}^{N} i^2\log i + \sum_{i=1}^{N} i\log i\right) \end{aligned}
i=1∑Nilogi=i=1∑Nlogi⋅j=1∑ij=i=1∑Nlogi⋅2i(i+1)=21i=1∑N(i2+i)logi=21(i=1∑Ni2logi+i=1∑Nilogi)
接下来,我们可以使用斯特林公式
n
!
≈
2
π
n
⋅
(
n
/
e
)
n
n! \approx \sqrt{2\pi n} \cdot (n/e)^n
n!≈2πn⋅(n/e)n,将
log
n
!
\log n!
logn! 近似为
1
2
log
2
π
n
+
n
log
n
−
n
+
O
(
log
n
)
\frac{1}{2}\log 2\pi n + n\log n - n + O(\log n)
21log2πn+nlogn−n+O(logn),进而计算
∑
i
=
1
N
i
log
i
\sum_{i=1}^N i\log i
∑i=1Nilogi。具体如下:
∑
i
=
1
N
i
2
log
i
=
∑
i
=
1
N
log
(
i
i
2
)
=
log
∏
i
=
1
N
i
i
2
=
log
(
∏
i
=
1
N
i
i
)
2
=
2
log
N
!
≈
2
(
1
2
log
(
2
π
N
)
+
N
log
N
−
N
+
O
(
log
N
)
)
=
N
log
N
+
O
(
N
log
N
)
\begin{aligned} \sum_{i=1}^N i^2\log i &= \sum_{i=1}^N \log (i^{i^2}) \\ &= \log\prod_{i=1}^N i^{i^2} \\ &= \log\left(\prod_{i=1}^N i^i\right)^2 \\ &= 2\log N! \\ &\approx 2\left(\frac{1}{2}\log(2\pi N) + N\log N - N + O(\log N)\right) \\ &= N\log N + O(N\log N) \end{aligned}
i=1∑Ni2logi=i=1∑Nlog(ii2)=logi=1∏Nii2=log(i=1∏Nii)2=2logN!≈2(21log(2πN)+NlogN−N+O(logN))=NlogN+O(NlogN)
∑
i
=
1
N
i
log
i
=
∑
i
=
1
N
log
i
i
=
log
∏
i
=
1
N
i
i
=
log
N
!
2
≈
2
(
1
2
log
(
2
π
N
)
+
N
log
N
−
N
+
O
(
log
N
)
)
=
N
log
N
−
N
+
O
(
log
N
)
\begin{aligned} \sum_{i=1}^N i\log i &= \sum_{i=1}^N \log i^i \\ &= \log \prod_{i=1}^N i^i \\ &= \log N!^2 \\ &\approx 2\left(\frac{1}{2}\log(2\pi N) + N\log N - N + O(\log N)\right) \\ &= N\log N - N + O(\log N) \end{aligned}
i=1∑Nilogi=i=1∑Nlogii=logi=1∏Nii=logN!2≈2(21log(2πN)+NlogN−N+O(logN))=NlogN−N+O(logN)
因此,
∑
i
=
1
N
i
log
i
=
1
2
(
∑
i
=
1
N
i
2
log
i
+
∑
i
=
1
N
i
log
i
)
=
N
log
N
−
N
+
O
(
log
N
)
\sum_{i=1}^N i\log i = \frac{1}{2}(\sum_{i=1}^N i^2\log i + \sum_{i=1}^N i\log i) = N\log N - N + O(\log N)
∑i=1Nilogi=21(∑i=1Ni2logi+∑i=1Nilogi)=NlogN−N+O(logN)。
11、
(
∏
i
=
1
N
i
i
)
\left(\prod_{i=1}^N i^i\right)
(∏i=1Nii)是如何变成
N
!
N!
N!的?
考虑
(
∏
i
=
1
N
i
i
)
\left(\prod_{i=1}^N i^i\right)
(∏i=1Nii) 中的每一项作为一个因子:
(
∏
i
=
1
N
i
i
)
=
1
1
⋅
2
2
⋅
3
3
⋅
⋯
⋅
N
N
\left(\prod_{i=1}^N i^i\right)=1^1 \cdot 2^2 \cdot 3^3 \cdot \cdots \cdot N^N
(i=1∏Nii)=11⋅22⋅33⋅⋯⋅NN
根据数学中乘法的结合律和交换律,上式中的因子可以改写为:
(
∏
i
=
1
N
i
i
)
=
1
⋅
2
2
⋅
3
3
⋅
⋯
⋅
(
N
−
1
)
N
−
1
⋅
N
N
\left(\prod_{i=1}^N i^i\right)=1 \cdot 2^2 \cdot 3^3 \cdot \cdots \cdot (N-1)^{N-1}\cdot N^N
(i=1∏Nii)=1⋅22⋅33⋅⋯⋅(N−1)N−1⋅NN
接下来,我们考虑
(
N
−
1
)
N
−
1
(N-1)^{N-1}
(N−1)N−1 这个因子。根据指数运算法则,
(
N
−
1
)
N
−
1
=
(
(
N
−
1
)
!
)
N
−
1
(N-1)^{N-1}=((N-1)!)^{N-1}
(N−1)N−1=((N−1)!)N−1. 再次利用乘法的交换律,将
(
N
−
1
)
N
−
1
(N-1)^{N-1}
(N−1)N−1 和
N
N
N^N
NN 放在一起:
(
∏
i
=
1
N
i
i
)
=
1
⋅
2
2
⋅
3
3
⋅
⋯
⋅
(
(
N
−
2
)
!
N
−
2
⋅
(
N
−
1
)
N
−
1
)
⋅
N
N
=
(
N
!
)
2
=
N
!
⋅
N
!
\begin{aligned} \left(\prod_{i=1}^N i^i\right)&=1 \cdot 2^2 \cdot 3^3 \cdot \cdots \cdot ((N-2)!^{N-2} \cdot (N-1)^{N-1})\cdot N^N\\ &=(N!)^2\\ &=N! \cdot N! \end{aligned}
(i=1∏Nii)=1⋅22⋅33⋅⋯⋅((N−2)!N−2⋅(N−1)N−1)⋅NN=(N!)2=N!⋅N!
因此,
(
∏
i
=
1
N
i
i
)
=
N
!
\left(\prod_{i=1}^N i^i\right)=N!
(∏i=1Nii)=N!。
12、根据指数运算法则,
(
N
−
1
)
N
−
1
=
(
(
N
−
1
)
!
)
N
−
1
(N-1)^{N-1}=((N-1)!)^{N-1}
(N−1)N−1=((N−1)!)N−1,这左右两边明显不相等吧
非常抱歉,我的回答有误。
(
N
−
1
)
N
−
1
(N-1)^{N-1}
(N−1)N−1 并不等于
(
(
N
−
1
)
!
)
N
−
1
((N-1)!)^{N-1}
((N−1)!)N−1。感谢您的纠正。
下面给出一个正确的证明:
∏
i
=
1
N
i
i
=
1
1
⋅
2
2
⋅
3
3
⋅
⋯
⋅
(
N
−
1
)
N
−
1
⋅
N
N
=
(
1
⋅
2
⋅
3
⋯
N
)
(
1
+
2
+
⋯
+
N
)
(
1
!
⋅
2
!
⋅
3
!
⋯
N
!
)
N
−
1
⋅
N
N
=
N
!
N
−
1
⋅
N
N
(
1
!
⋅
2
!
⋅
3
!
⋯
N
!
)
N
−
1
=
(
N
!
)
N
N
!
(
N
−
1
)
!
⋯
2
!
1
!
=
N
!
\begin{aligned} \prod_{i=1}^N i^i &= 1^1 \cdot 2^2 \cdot 3^3 \cdot \cdots \cdot (N-1)^{N-1} \cdot N^N \\ &= \frac{(1 \cdot 2 \cdot 3 \cdots N)^{(1+2+\cdots+N)}}{(1! \cdot 2! \cdot 3! \cdots N!)^{N-1}} \cdot N^N \\ &= \frac{N!^{N-1} \cdot N^N}{(1! \cdot 2! \cdot 3! \cdots N!)^{N-1}} \\ &= \frac{(N!)^N}{N!(N-1)! \cdots 2!1!} \\ &= N! \\ \end{aligned}
i=1∏Nii=11⋅22⋅33⋅⋯⋅(N−1)N−1⋅NN=(1!⋅2!⋅3!⋯N!)N−1(1⋅2⋅3⋯N)(1+2+⋯+N)⋅NN=(1!⋅2!⋅3!⋯N!)N−1N!N−1⋅NN=N!(N−1)!⋯2!1!(N!)N=N!
因此,
∏
i
=
1
N
i
i
=
N
!
\prod_{i=1}^N i^i = N!
∏i=1Nii=N!。注意,上述证明利用了乘法的交换律和结合律,以及
∑
i
=
1
N
i
=
1
2
N
(
N
+
1
)
\sum_{i=1}^N i = \frac{1}{2}N(N+1)
∑i=1Ni=21N(N+1) 和
∑
i
=
1
N
i
2
=
1
6
N
(
N
+
1
)
(
2
N
+
1
)
\sum_{i=1}^N i^2 = \frac{1}{6}N(N+1)(2N+1)
∑i=1Ni2=61N(N+1)(2N+1)。