python评估算法性能

一.原理分析

1. 时间复杂度

这是最重要的评估指标之一,它描述了算法的运行时间如何随着输入规模的增加而变化

核心思想 :我们并不关心具体的运行时间(比如多少毫秒),而是关心增长的趋势。当输入规模n变得非常大时,常数因子和低阶项的影响会变得微不足道,起主导作用的是最高阶项。

大O表示法:用来描述算法运行时间的上界,即最坏情况下的性能。

常见复杂度(从优到劣):

O(1): 常数时间。无论输入多大,运行时间都不变。
python 复制代码
def constant_time(arr):
    return arr[0] if arr else None  # 无论数组多长,只做一次操作

def access_dict(dictionary, key):
    return dictionary.get(key)      # 字典查找平均O(1)

# 示例:数组索引、字典查找、算术运算
O(log n): 对数时间。运行时间随输入规模呈对数增长,效率非常高(如二分查找)。
python 复制代码
def binary_search(arr, target):
    left, right = 0, len(arr) - 1
    
    while left <= right:            # 每次循环范围减半
        mid = (left + right) // 2
        if arr[mid] == target:
            return mid
        elif arr[mid] < target:
            left = mid + 1
        else:
            right = mid - 1
    return -1

# 输入规模n,循环次数:log₂n
O(n): 线性时间。运行时间与输入规模成正比。
python 复制代码
def linear_search(arr, target):
    for i, item in enumerate(arr):  # 遍历整个数组
        if item == target:
            return i
    return -1

def sum_array(arr):
    total = 0
    for num in arr:                 # 遍历每个元素
        total += num
    return total

# 输入规模n,操作次数:n
O(n log n): 线性对数时间。许多高效排序算法的复杂度(如归并排序、快速排序的平均情况)。
python 复制代码
def merge_sort(arr):
    if len(arr) <= 1:
        return arr
    
    mid = len(arr) // 2
    left = merge_sort(arr[:mid])    # O(log n) 层递归
    right = merge_sort(arr[mid:])
    
    return merge(left, right)       # 每层合并 O(n)

def merge(left, right):
    result = []
    i = j = 0
    while i < len(left) and j < len(right):
        if left[i] <= right[j]:
            result.append(left[i])
            i += 1
        else:
            result.append(right[j])
            j += 1
    result.extend(left[i:])
    result.extend(right[j:])
    return result

# 典型算法:归并排序、快速排序(平均情况)、堆排序
O(n²): 平方时间。常见于简单的双重循环(如冒泡排序)。
python 复制代码
def bubble_sort(arr):
    n = len(arr)
    for i in range(n):              # n 次循环
        for j in range(0, n-i-1):   # 平均 n/2 次循环
            if arr[j] > arr[j+1]:
                arr[j], arr[j+1] = arr[j+1], arr[j]
    return arr

def find_pairs(arr):
    pairs = []
    for i in range(len(arr)):       # n 次循环
        for j in range(i+1, len(arr)):  # n-i-1 次循环
            pairs.append((arr[i], arr[j]))
    return pairs

# 总操作次数:n × (n-1)/2 ≈ n²/2 = O(n²)
O(2^n): 指数时间。通常出现在暴力求解问题中,当n较大时几乎不可用。
python 复制代码
def fibonacci_naive(n):
    if n <= 1:
        return n
    return fibonacci_naive(n-1) + fibonacci_naive(n-2)  # 两次递归调用

# 递归树:每个节点分裂为两个子节点
# 时间复杂度:O(2^n)
O(n!): 阶乘时间。性能最差,通常用于解决旅行商等问题。
python 复制代码
def generate_permutations(arr):
    if len(arr) <= 1:
        return [arr]
    
    permutations = []
    for i in range(len(arr)):
        rest = arr[:i] + arr[i+1:]
        for p in generate_permutations(rest):  # 递归调用 (n-1)! 次
            permutations.append([arr[i]] + p)
    
    return permutations

# n个元素的排列数:n!,时间复杂度:O(n!)

2. 空间复杂度

空间复杂度 衡量的是算法在运行过程中临时占用的存储空间大小随输入规模增长的变化趋势

简单来说,它回答的问题是:"当我的输入数据量翻倍时,这个算法需要多少额外的内存?"

O(1) - 常数空间

算法所需的额外空间不随输入规模n变化。

python 复制代码
def constant_space(n):
    total = 0           # O(1) - 1个变量
    for i in range(n):  # i 也是 O(1) - 每次循环复用同一个变量
        total += i
    return total

# 无论n多大,只用了固定数量的变量

O(n) - 线性空间

算法所需的额外空间与输入规模n成正比。

python 复制代码
def linear_space(n):
    result = []              # O(n)
    for i in range(n):
        result.append(i)     # 列表长度随n线性增长
    return result

def another_linear(n):
    hash_map = {}           # O(n)
    for i in range(n):
        hash_map[i] = i * 2  # 字典大小随n线性增长
    return hash_map

O(n²) - 平方空间

算法所需的额外空间与输入规模n的平方成正比。

python 复制代码
def quadratic_space(n):
    matrix = []                    # O(n²)
    for i in range(n):
        row = []                   # 每行 O(n)
        for j in range(n):
            row.append(i * j)      # 总共 n * n = n² 个元素
        matrix.append(row)
    return matrix

O(log n) - 对数空间

通常出现在递归算法中。

python 复制代码
def recursive_function(n):
    if n <= 1:
        return n
    # 递归深度为 O(log n),调用栈空间也是 O(log n)
    return recursive_function(n // 2) + recursive_function(n // 2)

二.Python性能评估的实践工具

1. 时间测量工具

timeit 模块 - 首选工具
python 复制代码
import timeit

# 方法1:使用timeit.timeit()
def test_function():
    return sum(range(1000))

# 重复执行10000次,计算总时间
time_taken = timeit.timeit(test_function, number=10000)
print(f"平均时间: {time_taken/10000:.6f}秒")

# 方法2:使用timeit.repeat()
times = timeit.repeat(test_function, number=1000, repeat=5)
print(f"最佳时间: {min(times)/1000:.6f}秒")
print(f"最差时间: {max(times)/1000:.6f}秒")
print(f"平均时间: {sum(times)/len(times)/1000:.6f}秒")

2. 内存分析工具

memory_profiler
python 复制代码
from memory_profiler import profile

@profile
def memory_intensive_function():
    # 创建大量数据
    big_list = [i for i in range(100000)]
    big_dict = {i: str(i) for i in range(100000)}
    return big_list, big_dict

if __name__ == "__main__":
    memory_intensive_function()

三、完整的性能评估实践示例

让我们通过一个具体的例子来展示完整的评估流程。

python 复制代码
import timeit
import random
from collections import defaultdict
import sys

def linear_search(arr, target):
    """线性查找 - O(n)"""
    for i, item in enumerate(arr):
        if item == target:
            return i
    return -1

def binary_search(arr, target):
    """二分查找 - O(log n)"""
    low, high = 0, len(arr) - 1
    while low <= high:
        mid = (low + high) // 2
        if arr[mid] == target:
            return mid
        elif arr[mid] < target:
            low = mid + 1
        else:
            high = mid - 1
    return -1

def performance_comparison():
    """完整的性能比较函数"""
    # 准备测试数据
    sizes = [100, 1000, 10000, 100000]
    results = defaultdict(dict)
    
    for size in sizes:
        # 生成有序数据(二分查找需要有序数据)
        sorted_data = list(range(size))
        target = random.choice(sorted_data)  # 确保目标存在
        
        # 测试线性查找
        linear_time = timeit.timeit(
            lambda: linear_search(sorted_data, target),
            number=1000
        )
        
        # 测试二分查找
        binary_time = timeit.timeit(
            lambda: binary_search(sorted_data, target),
            number=1000
        )
        
        results[size] = {
            'linear_search': linear_time,
            'binary_search': binary_time,
            'ratio': linear_time / binary_time
        }
    
    # 输出结果
    print("性能比较结果:")
    print("数据规模\t线性查找\t二分查找\t速度比")
    print("-" * 50)
    for size in sizes:
        result = results[size]
        print(f"{size}\t\t{result['linear_search']:.6f}\t"
              f"{result['binary_search']:.6f}\t"
              f"{result['ratio']:.1f}x")

if __name__ == "__main__":
    performance_comparison()
相关推荐
儒雅芝士2 小时前
BIT*算法
python
蒋星熠2 小时前
用 CodeBuddy CLI + Prompt,从零到可运行:前后端混合管理系统的高效实战
人工智能·python·机器学习·prompt·codebuddy code·无界生成力·ai cli
Nina_7172 小时前
第一章——了解prompt以及一些基础技巧方法
人工智能·python
JJJJ_iii2 小时前
【深度学习04】PyTorch:损失函数、优化器、模型微调、保存与加载
人工智能·pytorch·笔记·python·深度学习·机器学习
小熊出擊3 小时前
【pytest】使用 marker 向 fixture 传递数据
python·pytest
Dest1ny-安全3 小时前
Java代码审计-Servlet基础(1)
java·python·servlet
好开心啊没烦恼3 小时前
Python数据分析:使用爬虫从网页、社交媒体平台、论坛等公开资源提取中文和英文人名。
开发语言·爬虫·python·数据挖掘·数据分析
雨夜的星光3 小时前
Python环境管理工具全景对比:Virtualenv, Pipenv, Poetry 与 Conda
python·pycharm·conda·virtualenv
如意猴3 小时前
数据结构初阶(第六讲)单链表的功能实现
数据结构