实现欧拉形式的三角插值

问题描述 投票:0回答:1

目前我正在努力正确实现欧拉的三角插值。为了指导您完成我已经完成的工作和收集的信息,我会将代码与数学定义配对。代码将用 python 编写,并使用 numpy 函数。 请注意,我不会使用快速傅立叶变换

首先,所有实验都将在以下数据集上进行

(x,y)


[('0.0', '0.0'),
 ('0.6283185307179586', '0.6427876096865393'),
 ('1.2566370614359172', '0.984807753012208'),
 ('1.8849555921538759', '0.8660254037844387'),
 ('2.5132741228718345', '0.3420201433256689'),
 ('3.141592653589793', '-0.34202014332566866'),
 ('3.7699111843077517', '-0.8660254037844384'),
 ('4.39822971502571', '-0.9848077530122081'),
 ('5.026548245743669', '-0.6427876096865396'),
 ('5.654866776461628', '-2.4492935982947064e-16')]

sine wave

我们定义离散傅里叶变换

definition discrete Fourier transform

所以我对这个功能的实现如下

import numpy as np #consider numpy as imported from here on

def F_n(Y):
    n = len(Y)
    Y_hat = []
    for k in range(len(Y)):
        transformed_k = 1/n * sum([y_l * np.exp(-2 * np.pi * 1j* k * l/n) for l, y_l in enumerate(Y) ])
        Y_hat.append(transformed_k)
    return Y_hat

因此,将得到的系数与使用

np.fft.fft
的系数进行比较,看起来系数几乎是正确的。它们的区别仅在于移动了一位数字,即

# F_n(y)
['(-1.33907057366955e-17+0j)',
 '(0.14283712054380923-0.439607454395199j)',
 '(-0.048591754799448425+0.06688081278992913j)',
 '(-0.039133572999081954+0.028432205056635337j)',
 '(-0.036913281031968816+0.01199385205986717j)',
 '(-0.036397023426620205-2.0058074207055733e-17j)',
 '(-0.03691328103196878-0.011993852059867215j)',
 '(-0.03913357299908168-0.028432205056635646j)',
 '(-0.04859175479944824-0.06688081278992904j)',
 '(0.1428371205438091+0.439607454395199j)']
# np.fft.fft(y)
['(-1.1102230246251565e-16+0j)',
 '(1.428371205438092-4.39607454395199j)',
 '(-0.4859175479944836+0.6688081278992911j)',
 '(-0.3913357299908192+0.2843220505663533j)',
 '(-0.36913281031968803+0.11993852059867194j)',
 '(-0.36397023426620184-1.1102230246251565e-16j)',
 '(-0.36913281031968803-0.11993852059867194j)',
 '(-0.3913357299908196-0.2843220505663534j)',
 '(-0.4859175479944836-0.6688081278992911j)',
 '(1.4283712054380922+4.39607454395199j)']

现在我想实现三角插值。这是定义

trigonometric polynomial theorem

我用这个实现来实现定理

def trig_interpolation(Y_hat, x_range, depth=1000):
    n = len(Y_hat)

    get_summand = lambda c_j,l,x: c_j*np.exp(2 * np.pi * 1j * l*x)

    y_intp = []
    x_intp = list((i/depth)*x_range for i in range(depth))
    if n%2==0:
        K = n//2 
        for x in x_intp:
            y_intp.append(sum([get_summand(c_j,l,x) for l,c_j in zip(range(-K+1,K+1), Y_hat)]))
    else:
        K = n//2+1
        for x in x_intp:
            y_intp.append(sum([get_summand(c_j,l,x) for l,c_j in zip(range(-K,K+1), Y_hat)]))

    return x_intp, y_intp

x_range = max(x)-min(x)
x_intp, y_intp = trig_interpolation(np.fft.fft(y), x_range)

其中

x_range
表示原始数据点集中
x
值的范围,
depth
表示插值的分辨率,第 4 行中
get_summand
函数表示项 $$ xp(2\pi j x)$$ 使求和过程更容易阅读。

当对 numpy 的 fft 给出的系数运行我的代码时,我得到

plt.plot(x_intp,np.real(y_intp))
plt.plot(x,y, 'o')

interpolation

虽然这些点与使用 numpy 的 fft 进行插值对齐,但我希望曲线看起来有所不同,表现得更接近实际的正弦曲线。

使用我的实现计算傅里叶系数给出了一条预期错误的曲线。

interpolation on wrong fourier coefficients


我要求指出我的错误,以便我可以根据我提出的数学特征正确地实现三角插值。

python fft interpolation
1个回答
0
投票

您所做的事情存在许多单独的问题。

最重要的是离散傅立叶变换不会产生您需要的频率顺序。顺序大致如下(参见 https://numpy.org/doc/stable/reference/routines.fft.html

freq[0] ....正频率...负频率(从非常正的频率映射)

所以你必须改变你的频率。有一个例程

numpy.fft.fftshift
但它并没有完全按照您想要的编号移动,所以我在这里使用
numpy.roll
来旋转数组。

您需要将 FFT 除以

n
才能获得所需形式的系数。

您的

x_range
是错误的 - 它基于隐含的 x_max,而不是数组的最后一个元素。

K
在一种情况下是错误的。

如果

x/x_range
不在 0 和 1 之间,则您希望在总和中包含
x

import numpy as np
import matplotlib.pyplot as plt

n = 10
x = np.linspace( 0.0, 2 * np.pi, n, endpoint=False )
y = np.sin( x )

def trig_interpolation(Y_hat, x_range, depth=1000):
    n = len(Y_hat)

    get_summand = lambda c_j, l, x: c_j * np.exp(2 * np.pi * 1j * l * x / x_range )

    x_intp = [(i / depth) * x_range for i in range(depth)]                     
    y_intp = []
    K = n // 2                                                       # Fix K for the odd/even cases
    if n%2==0:
        Y_hat = np.roll( Y_hat, K - 1 )                              # Rotate
        for x in x_intp:
            y_intp.append(sum([get_summand(c_j,l,x) for l,c_j in zip(range(-K+1,K+1), Y_hat)]))
    else:
        Y_hat = np.roll( Y_hat, K )                                  # Rotate
        for x in x_intp:
            y_intp.append(sum([get_summand(c_j,l,x) for l,c_j in zip(range(-K,K+1), Y_hat)]))
    return x_intp, y_intp


x_range = n * ( x[1] - x[0] )                                        # Fix the range: it ISN'T defined by last element of x
x_intp, y_intp = trig_interpolation( np.fft.fft(y) / n, x_range)     # Divide the FFT by n

plt.plot(x_intp,np.real(y_intp))
plt.plot(x,y, 'o')
plt.show()

enter image description here

© www.soinside.com 2019 - 2024. All rights reserved.