正弦波的峰值查找算法

我在做公司的一个项目时遇到一个问题,我用示波器抓取到了正弦波数据,在我的python程序中是一个数组,长度很长,有20M个数据。我需要得到这组正弦波数据的峰峰值,我已经用过了直接求最大最小值的方法,但我对此算法并不满意,因为不够高大上。即我需要一个合适的峰值查找算法,要求这个算法具有较低的时间复杂度(如果时间复杂度确实无法降下来,也行,我可以用多线程加快计算时间,但最好能有较低的时间复杂度),如果您知道的话,告诉我算法的名字,如果能加上算法的代码和思想更好。

需要的是峰值搜索算法(Peak Search Algorithm),以下是一个简单的实现:

def find_peak_valley(arr):  
    if len(arr) < 3:  
        return arr  
  
    # Initialize variables  
    max_val = max(arr)  
    min_val = min(arr)  
    peak_val = None  
    valley_val = None  
  
    # Binary search for peak and valley values  
    mid = len(arr) // 2  
    while mid > 0:  
        if arr[mid-1] < arr[mid] < arr[mid+1]:  
            peak_val = arr[mid]  
            valley_val = arr[mid-1] if arr[mid-1] < arr[mid] else arr[mid+1]  
        elif arr[mid-1] < arr[mid] and arr[mid] < arr[mid+1]:  
            peak_val = arr[mid]  
            valley_val = arr[mid+1] if arr[mid+1] < arr[mid] else arr[mid-1]  
        elif arr[mid-1] > arr[mid] and arr[mid] > arr[mid+1]:  
            valley_val = arr[mid]  
            peak_val = arr[mid-1] if arr[mid-1] > arr[mid] else arr[mid+1]  
        elif arr[mid-1] > arr[mid] and arr[mid] < arr[mid+1]:  
            valley_val = arr[mid]  
            peak_val = arr[mid+1] if arr[mid+1] > arr[mid] else arr[mid-1]  
  
        max_val = max(arr[0:mid], max_val) if peak_val <= max_val else peak_val  
        min_val = min(arr[0:mid], min_val) if valley_val >= min_val else valley_val  
  
        mid = len(arr) // 2 - mid + 1 if mid > 0 else len(arr)  
  
    return [peak_val, valley_val, max_val, min_val]

如果只是想找峰峰值的话,又不想直接找最大值,那遗传算法或者差分进化算法应该可以的。

可以使用基于分治思想和二分查找的峰值查找算法。该算法的基本思想是使用二分查找的方法,将数据分成两半,比较中间位置及其相邻位置的大小,如果中间位置较大,则在左边继续查找;如果中间位置较小,则在右边继续查找;如果中间位置是峰值,则直接返回该值。重复执行以上步骤,直到找到峰值。

下面是该算法的 Python 实现:

def find_peak_value(data):
    n = len(data)
    # 左右边界
    left, right = 0, n - 1
    while left < right:
        mid = (left + right) // 2
        if data[mid] < data[mid + 1]:  # 右边更高
            left = mid + 1
        else:  # 左边更高
            right = mid
    return data[left]

该算法的时间复杂度为 O(log n),是一种较快的算法。同时,由于该算法只需要比较邻近元素,因此占用的空间较小,可适用于较大量数据的峰值查找。

你可以调用该函数来得到正弦波数据的峰峰值。

import numpy as np

# 生成正弦波数据
t = np.arange(0, 10, 0.1)
data = np.sin(t)

# 查找峰峰值
peak_value = find_peak_value(data)
min_value = np.min(data)
amplitude = (peak_value - min_value) * 2
print("Amplitude: ", amplitude)

初始化左边界 left 为 0,右边界 right 为数组的长度减 1。
在数组的中间位置找到中间元素 mid。
比较 mid 与其相邻元素的大小关系:
如果 mid 大于其左右相邻元素,那么 mid 就是一个峰值,算法结束。
如果 mid 小于其左侧元素,那么峰值必然在 mid 的左侧,将右边界更新为 mid - 1,并重复步骤 2。
如果 mid 小于其右侧元素,那么峰值必然在 mid 的右侧,将左边界更新为 mid + 1,并重复步骤 2

python波形峰值检测
可以参考下
https://blog.csdn.net/lm_is_dc/article/details/127585918

基于new bing的编写:
您可以使用基于分而治之思想的峰值查找算法(Peak Finding Algorithm),该算法的时间复杂度为O(log n)。

以下是算法的详细步骤:

  • 找到正弦波数据的中间点 mid。

  • 比较 mid 位置的值与它相邻位置的值,如果 mid 的左右两侧都比 mid 小,则 mid 就是一个峰值。

  • 如果 mid 左侧的位置比 mid 大,则在 mid 的左侧进行相同的比较。

  • 如果 mid 右侧的位置比 mid 大,则在 mid 的右侧进行相同的比较。

接下来是 Python 代码实现:

def peak_finding(arr, left, right):
    mid = (left + right) // 2
    if (mid == 0 or arr[mid-1] <= arr[mid]) and (mid == len(arr)-1 or arr[mid+1] <= arr[mid]):
        return mid
    elif mid > 0 and arr[mid-1] > arr[mid]:
        return peak_finding(arr, left, mid-1)
    else:
        return peak_finding(arr, mid+1, right)

# 输入正弦波数据,调用peak_finding函数得到峰值的位置
peak_index = peak_finding(sin_wave, 0, len(sin_wave)-1)
# 根据峰值位置得到峰峰值
peak_to_peak = max(sin_wave) - min(sin_wave)

在以上代码中,sin_wave 是正弦波数据的数组,left 和 right 分别是正弦波数据的数组的左右两端的索引。

该算法的时间复杂度为 O(log n),比直接计算最大最小值的时间复杂度低,并且很容易实现。

该回答通过自己思路及引用到GPTᴼᴾᴱᴺᴬᴵ搜索,得到内容具体如下:
您可以尝试使用分治法来查找正弦波的峰值,这种算法的时间复杂度为O(log n),比直接求最大最小值的方法要快。

具体实现方法如下:

  1. 将正弦波数据分成两个部分,每个部分包含大约一半的数据。
  2. 比较两个部分的中间值,如果左边的中间值比右边的中间值大,则峰值在左半部分;反之,峰值在右半部分。
  3. 递归地对峰值所在的半部分进行查找,直到找到峰值。

以下是使用Python实现分治法查找正弦波峰值的示例代码:

def find_peak(data):
    size = len(data)
    if size == 1:
        return data[0]
    elif size == 2:
        return max(data[0], data[1])
    else:
        mid = size // 2
        if data[mid] > data[mid-1] and data[mid] > data[mid+1]:
            return data[mid]
        elif data[mid-1] > data[mid]:
            return find_peak(data[:mid])
        else:
            return find_peak(data[mid:])

在这个示例代码中,我们首先判断输入数据的大小,如果只有一个元素,直接返回该元素;如果有两个元素,返回它们中的较大值。否则,我们将数据分成两个部分,并比较它们的中间值。如果中间值是峰值,则返回该值;否则,递归地对峰值所在的半部分进行查找,直到找到峰值。

使用该算法查找正弦波的峰值时,只需要将正弦波数据传递给find_peak函数即可:

import numpy as np

# 生成一个正弦波数据
x = np.linspace(0, 2*np.pi, 20000000)
data = np.sin(x)

# 查找正弦波的峰值
peak = find_peak(data)
print("峰值为:", peak)

值得注意的是,这种算法只适用于单峰函数,如果正弦波数据有多个峰值,则需要对每个峰值分别进行查找。


如果以上回答对您有所帮助,点击一下采纳该答案~谢谢

来自GPT的回答:


```bash
您可以尝试使用峰值查找算法中的一种常见方法,即"峰值检测"Peak Detection)算法。该算法可以用于在一组数据中查找峰值点。

以下是一个简单的峰值检测算法的实现示例,基于Python编写:

python
Copy code
def find_peaks(data):
    peaks = []
    length = len(data)
    
    for i in range(1, length-1):
        if data[i] > data[i-1] and data[i] > data[i+1]:
            peaks.append(data[i])
    
    return peaks
这个算法的思想是,遍历数据数组,对于每个元素,检查其是否大于前一个元素和后一个元素。如果是,则将该元素添加到峰值列表中。

请注意,这个算法只能找到局部峰值,即在某个点上的值比其相邻的两个点都要大。如果您需要找到全局峰值(整个数据范围内的最大值和最小值),可以在找到所有局部峰值后,再从中选择最大和最小值。

这个算法的时间复杂度为O(n),其中n是数据的长度。这是一个相对较低的时间复杂度,可以处理您提到的20M个数据点。

当然,如果您希望进一步优化算法的性能,可以考虑使用并行计算(多线程或多进程)来加速峰值的查找过程。您可以将数据分成多个子集,在不同的线程或进程中并行查找峰值,然后合并结果。但请注意在实现并行计算时要确保数据的正确性和同步。

```

正弦峰值不是在T/4那里吗?

针对您的问题,您可以使用峰值查找算法中的"峭度峰值检测"(Kurtosis Peak Detection)算法来获取正弦波数据的峰峰值。这个算法具有较低的时间复杂度并且能够有效地找到波形的峰值。

以下是峭度峰值检测算法的思想和步骤:

  1. 首先,计算正弦波数据的峭度。峭度是描述数据分布形态陡缓程度的统计量,它可以通过以下公式计算: 峭度 = (1/N) * ∑((xi - x_mean)^4) / (σ^4) 其中,N表示数据的总数,xi表示数据点,x_mean表示数据的均值,σ表示数据的标准差。
  2. 对计算得到的峭度序列进行平滑处理。可以采用滑动窗口的方法,计算窗口内峭度的均值,并将均值作为窗口内所有数据点的峭度值。
  3. 寻找峭度序列中的峰值。通过比较当前峭度值与其相邻的峭度值,找到峭度序列中的局部最大值,即波形的峰值点。
  4. 根据峰值点的位置计算正弦波数据的峰峰值。峰峰值是指波形中最高峰值与最低峰值之间的差值,即振幅的两倍。

下面是用Python示例代码实现峭度峰值检测算法的基本思路:

import numpy as np
from scipy.stats import kurtosis

def find_peak_to_peak(data):
    # 计算数据的峭度
    kurt = kurtosis(data)
    
    # 平滑处理峭度序列
    smoothed_kurt = np.convolve(kurt, np.ones(10)/10, mode='same')
    
    # 寻找峭度序列中的峰值
    peaks, _ = find_peaks(smoothed_kurt)
    
    # 根据峰值点计算峰峰值
    peak_to_peak = 2 * (np.max(data[peaks]) - np.min(data[peaks]))
    
    return peak_to_peak

请注意,这只是一个简单的示例代码,具体实现可能需要根据您的数据结构和算法需求进行适当的修改和调整。

希望这个算法能够满足您的需求!如果您有任何进一步的问题,请随时提问。