3

我已经比较了 python 3 中一些内置排序函数的性能和一些我知道它们的性能的算法,我看到了一些意想不到的结果。我想澄清这些问题。

我使用“perfplot”库来比较可视化算法的复杂性。

import numpy as np
import perfplot

# algorithms:


def sort(x):
    np.sort(x)
    return 0


def sort_c(x):
    np.sort_complex(x)
    np.sort_complex(x)
    return 0


def builtin(x):
    sorted(x)
    return 0


def c_linear_inplace(x):
    for i in range(len(x) - 1):
        if x[i] > x[i + 1]:
            x[i] = x[i] + x[i + 1]
            x[i + 1] = x[i] - x[i + 1]
            x[i] = x[i] - x[i + 1]
    return 0


def c_linear_outplace(x):
    a = x.copy()
    for i in range(len(x) - 1):
        if x[i] > x[i + 1]:
            a[i] = x[i + 1]
            a[i + 1] = x[i]
    x = a.copy()
    return 0

def c_nlogn(x):
    logn = int(np.ceil(np.log2(len(x))))
    for i in range(len(x)-1):
        for j in range(logn):
            x[i] = 0
    return 0

#comprasion:
perfplot.show(
    setup=np.random.rand,  # function to generate input for kernel by n
    kernels=[
        sort,
        sort_c,
        builtin,
        c_linear_inplace,
        c_linear_outplace,
        c_nlogn,
    ],
    labels=["sort", "sort_c", "builtin", "check: linear inplace", "check: linear not in place", "check: nlogn"],
    n_range=[2 ** k for k in range(15)],  # list of sizes of inputs, i"setup" function will be called with those values
    xlabel="len(a)",
)

视觉比较

我希望所有排序函数都接近 nlogn() 函数或至少比线性函数效率低,并且我希望“c_linear_inplace”会比“c_linear_outplace”快。但所有内置排序函数都比线性函数快得多,并且“c_linear_outplace”函数比“c_linear_inplace”函数慢。

编辑: 正如我所见,这些是具有常量的函数的复杂性:
sort,sort_c,builtin:c nlog 2 (n) for c>=1
check:linear inplace:6n
check:linear not in place:7n
check: nlogn : 2n + 3n
对数2 n

我将任何 for 循环计算为 2*(迭代次数)以进行检查和增量。并且任何“if”都为 O(1),任何赋值 (=) 为 O(1),这似乎很奇怪,甚至占用更多内存的“check:linear not in place”比“check:linear inplace”的性能要好得多并且仍然比任何 numpy 的类型都差

4

2 回答 2

2

您想在“对数”图上显示结果。渐近/大 O 表示法通过忽略低阶因素来近似运行时间,这可能对实际运行时间产生重大影响,因此您会看到差异。

如果你做了一个对数图,你应该得到近似直线,这些直线在高度上被这些低阶常数偏移。另请注意,从单个元素开始往往会突出调用函数的开销,而不是任何渐近性能。

例如,这是我在运行时得到的:

    n_range=[2 ** k for k in range(10, 17)],
    xlabel="len(a)", logx=True, logy=True,

演示

现在更明显的是,高度差异只是性能上的差异

于 2019-09-09T17:23:48.247 回答
1

渐近地更快并不意味着它总是运行得更快。它只是意味着在一定的值之后n,它将开始获胜。这n甚至可能超出您的实际问题域,因此您永远不会看到渐近更快的函数按时间顺序更快的问题。你的情节就是这种情况。

如果您从 0-16,000 ( range(15)) 缩小到 0-67,000,000 ( range(27)),情况就不同了。特别builtin是,比check: linear inplace最多 40M 更快,但随后开始丢失:

随着大小的增加,显示排序比线性慢的图

与 C 直接访问数组相比,执行 Python->C FFI 访问数组值的开销是例如 50 倍的开销,这并非不合理。在这种情况下,n log2 n不会比50*nuntil更快n > 2^50,因此您可能永远不会看到sortlinear inplace今天的硬件慢。

于 2019-09-09T18:56:13.403 回答