FFT算法(快速傅里叶变换)
FFT(快速傅里叶变换)是一种用于将时域信号转换为频域信号的算法。它是傅里叶变换的一种高效实现方法,能够快速计算出离散傅里叶变换(DFT)。
FFT算法的主要思想是将长度为N的DFT分解为多个长度为N/2的DFT,并重复进行这种分解,直到长度为1的DFT,即得到信号的频域表示。通过利用DFT的对称性质和周期性特点,FFT算法大大减少了计算量,使得基于FFT的频域分析及滤波等操作变得非常高效。
具体实现时,FFT算法采用分治法,将长度为N的DFT分解为两个长度为N/2的DFT,然后再将这两个子问题进一步分解,直到达到最小规模。最后,通过将这些子问题的计算结果合并,得到原始信号的频域表示。
python实现
import numpy as np
def fft(x):
N = len(x)
if N <= 1:
return x
even = fft(x[0::2])
odd = fft(x[1::2])
factor = np.exp(-2j * np.pi * np.arange(N) / N)
return np.concatenate([even + factor[:N // 2] * odd, even + factor[N // 2:] * odd])
# 示例输入
x = np.array([0, 1, 2, 3, 4, 5, 6, 7])
# 输出结果
print(fft(x))
上述代码中, 函数接受一个输入信号 x,并返回其频谱结果。频谱结果是一个复数数组,表示输入信号的离散傅里叶变换结果。
Java实现
class Main {
public static Complex[] fft(Complex[] x)
{
int N = x.length;
// 用于处理长度为1的输入情况
if (N == 1)
{
return new Complex[]{x[0]};
}
// 计算偶数和奇数下标的DFT分量
Complex[] even = new Complex[N/2];
Complex[] odd = new Complex[N/2];
for (int k = 0; k < N/2; k++)
{
even[k] = x[2*k];
odd[k] = x[2*k + 1];
}
// 递归计算偶数和奇数分量的DFT
Complex[] evenResult = fft(even);
Complex[] oddResult = fft(odd);
// 合并偶数和奇数分量的DFT结果
Complex[] y = new Complex[N];
for (int k = 0; k < N/2; k++)
{
double kth = -2 * k * Math.PI / N;
Complex wk = new Complex(Math.cos(kth), Math.sin(kth));
y[k] = evenResult[k].plus(wk.times(oddResult[k]));
y[k + N/2] = evenResult[k].minus(wk.times(oddResult[k]));
}
return y;
}
public static void main(String[] args)
{
Complex[] x =
{
new Complex(1, 0),
new Complex(2, 0),
new Complex(3, 0),
new Complex(4, 0)
};
Complex[] y = fft(x);
System.out.println(Arrays.toString(y));
}
}
class Complex
{
private final double real;
private final double imag;
public Complex(double real, double imag)
{
this.real = real;
this.imag = imag;
}
public Complex plus(Complex other)
{
double real = this.real + other.real;
double imag = this.imag + other.imag;
return new Complex(real, imag);
}
public Complex minus(Complex other)
{
double real = this.real - other.real;
double imag = this.imag - other.imag;
return new Complex(real, imag);
}
public Complex times(Complex other)
{
double real = this.real * other.real - this.imag * other.imag;
double imag = this.real * other.imag + this.imag * other.real;
return new Complex(real, imag);
}
@Override
public String toString()
{
return String.format("(%f, %f)", real, imag);
}
}
程序的输入是一个复数数组x
,数组x
的长度应当是2的幂次。
程序的输出是经过FFT变换后的频域信号,表示为一个复数数组y
。数组y
的长度与输入数组x
相同,并且对应于不同频率分量的振幅和相位信息。
在实现的fft
方法中,程序首先检查输入数组x
的长度,如果长度为1,则直接返回。否则,程序将输入数组x
分成偶数下标和奇数下标的两个子数组even
和odd
。
然后,递归地对子数组even
和odd
分别进行FFT变换,并得到它们的结果evenResult
和oddResult
。
最后,程序合并并计算子问题的结果。对于每个下标k
,程序计算出旋转因子wk
,然后根据计算公式将子问题的结果合并成频域结果y
。
平面点集的凸包
平面点集的凸包是指包含所有点的最小凸多边形。凸多边形是指多边形内部的任意两点之间的连线都完全位于多边形内部或边界上。凸包可以看作是将点集用一个弹性绳子紧紧地拴住的形状。
求解凸包的方法——Graham扫描算法:
-
首先找到点集中的最下边界点(如果有多个,选取最左边的那个),将该点作为凸包的起始点。
-
将其他所有点按照与起始点的极角进行排序(如果同角度有多个点,按照距离起始点的距离排序)。
-
从第二个点开始,依次将每个点加入凸包中,检查是否满足凸包的性质:即加入当前点后,前面已加入的点和当前点构成的路径是右拐(即逆时针方向)。
-
如果不满足凸包性质,移除前一个点,重复检查直到满足性质为止。
-
重复上述步骤直到所有点都加入了凸包中。
python实现:
import numpy as np
from scipy.spatial import ConvexHull
def compute_convex_hull(points):
# 将点集转换为numpy数组
points = np.array(points)
# 计算凸包
hull = ConvexHull(points)
# 获取凸包的顶点索引
vertices = hull.vertices
# 获取凸包的顶点坐标
convex_hull_points = points[vertices]
return convex_hull_points
# 示例点集
points = [[0, 0], [1, 1], [1, 0], [0, 1], [0.5, 0.5]]
# 计算凸包
print(compute_convex_hull(points))
快速选择算法
快速选择算法的基本思想类似于快速排序,通过选择一个基准元素,将列表划分为两个部分,并根据基准元素的位置进行递归处理,直到找到目标元素。
实例:选择第k小的数
python实现:
def partition(arr, low, high):
pivot = arr[high]
i = low - 1
for j in range(low, high):
if arr[j] <= pivot:
i += 1
arr[i], arr[j] = arr[j], arr[i]
arr[i+1], arr[high] = arr[high], arr[i+1]
return i+1
def quick_select(arr, low, high, k):
if low == high:
return arr[low]
pivot_idx = partition(arr, low, high)
if k == pivot_idx:
return arr[k]
elif k < pivot_idx:
return quick_select(arr, low, pivot_idx - 1, k)
else:
return quick_select(arr, pivot_idx + 1, high, k)
def find_kth_smallest(arr, k):
if k < 1 or k > len(arr):
return None
return quick_select(arr, 0, len(arr) - 1, k - 1)
Java实现:
public static int partition(int[] arr, int low, int high)
{
int pivot = arr[high];
int i = low - 1;
for (int j = low; j < high; j++)
{
if (arr[j] <= pivot)
{
i++;
int temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
}
int temp = arr[i + 1];
arr[i + 1] = arr[high];
arr[high] = temp;
return i + 1;
}
public static int quickSelect(int[] arr, int low, int high, int k)
{
if (low == high)
{
return arr[low];
}
int pivotIdx = partition(arr, low, high);
if (k == pivotIdx)
{
return arr[k];
}
else if (k < pivotIdx)
{
return quickSelect(arr, low, pivotIdx - 1, k);
}
else
{
return quickSelect(arr, pivotIdx + 1, high, k);
}
}
public static Integer findKthSmallest(int[] arr, int k)
{
if (k < 1 || k > arr.length)
{
return null;
}
return quickSelect(arr, 0, arr.length - 1, k - 1);
}
本文仅为学习记录,如有错误欢迎指出