有没有一种方法可以更快地找到列表中的每个元素与该列表中的每个其他元素之间的区别?

时间:2019-11-08 15:51:01

标签: python performance data-structures time-complexity

我正在尝试获取列表中每个元素之间的拍频,(获取每个元素与每个其他元素之间的差的绝对值。

如果列表是

[a,b,c]

我要生成

[a,b,c,| a-b |,| a-c |,| b-c |]

但是我现在正在做的事情(简单地遍历列表中的每个元素然后再次进行遍历)对于我正在处理的元素数量来说确实很慢,是否还有其他我可以使用的数据结构或方法使此操作更快?

下面的代码是我现在正在做的。我是python的新手,没有从基础知识中学到许多数据结构,所以很抱歉,如果解决方案实际上是我从未想到过的真正明显的东西!

我与处理器进行了交谈,他提到了晶格,是纯粹的数学概念,还是在代码中有一些可以使用的实现方式,或者甚至对我的案例有用吗?

这是我当前正在运行的代码。它获取频率和幅度的列表,其中频率在[n]处的幅度为幅值[n]。一旦找到唯一的拍频,它将被添加到列表中,其幅度是输入频率的幅度的平均值,因此,如果在a = 1时为440hz,在a = 2时为110hz,则拍频将为abs (440-110)在a =((1 + 2)/ 2)。

def beatSet(frequencies, amplitudes, tol) :
        for index_x, x in enumerate(frequencies) :
                for index_y, y in enumerate(frequencies[index_x+1:]) :
                        beat_freq = abs(x - y)
                        if search(frequencies, beat_freq, tol) : #returns true if beat_freq isn't a duplicate, within a tolerance
                                frequencies.append(beat_freq)
                                amplitudes.append((amplitudes[index_y] + amplitudes[index_x])/2) 

和搜索功能如下:

def search(list_in, search_term, tolerance) :
        for i in list_in :
                if abs(search_term - i) <= tolerance :
                        return False
        return True

通常,一个输入列表将有大约10-50个元素,但是输出可能会变得很大,例如[440,441]会生成一个输出列表,如果没有公差,该列表将非常大,因此公差在某种程度上是输出列表大小的限制因素。通常,当输入列表中两个频率的差刚好在容差之上时,将生成最大的输出列表。

对不起,我希望我已经对所有内容进行了充分的解释!

1 个答案:

答案 0 :(得分:0)

您的方法遇到的一个问题是它可以适应原始方法

您可以对频率进行排序,然后使用bisect在排序列表中进行搜索:

import bisect
import itertools
def beat_set(frequencies, amplitudes, tolerance):
    sorted_frequencies = sorted(zip(frequencies, amplitudes))
    results = {}
    for (
        (index_x, (freq_x, ampl_x)),
        (index_y, (freq_y, ampl_y)),
    ) in itertools.combinations(enumerate(sorted_frequencies), 2):
        beat_frequency = abs(freq_x - freq_y)
        if not search_bisect(sorted_frequencies, beat_frequency, tolerance):
            # beat_frequency already in frequencies
            continue
        beat_amplitude = (ampl_x + ampl_y) / 2
        bisect.insort_left(sorted_frequencies, (beat_frequency, beat_amplitude))
    return sorted_frequencies
(beat_set(frequencies, amplitudes, tolerance))

def search_bisect(sorted_frequencies, beat_frequency, tolerance):
    insert_index = bisect.bisect_left(sorted_frequencies, (beat_frequency,))
    if insert_index == 0:
        return abs(sorted_frequencies[0][0] - beat_frequency) > tolerance
    return all(
        abs(sorted_frequencies[insert_index - i][0] - beat_frequency)
        > tolerance
        for i in (0, 1)
)

在找到的每个节拍上更新频率列表,但仅比较原始频率。在OP的解决方案中,第二个for循环使用了更新的频率。

frequencies = 440, 441
amplitudes = 2, 1
tolerance = 0.1

beat_set(frequencies, amplitudes, tolerance)
[(1, 1.5), (440, 2), (441, 1)]

-

收敛

def search_until_convergence(
    frequencies, amplitudes, tolerance, max_iterations=100
):
    result_new = beat_set(frequencies, amplitudes, tolerance)
    result_old = None
    for i in itertools.count():
        if i >= max_iterations:
            raise ValueError(f"No Convergence after {i} iterations")
        frequencies_new, amplitudes_new = zip(*result_new)
        if result_old == result_new:
            return {
                "frequencies": frequencies_new,
                "amplitudes": amplitudes_new,
                "iterations": i,
            }
        result_old = result_new
        result_new = beat_set(frequencies_new, amplitudes_new, tolerance)