当前位置: 首页 > news >正文

Python 高手编程系列三千三百九十七:使用概率型数据结构

概率型数据结构是为存储集合值而设计的,可以让你在一定的时间内或资源约束内回
答某些特定的问题,这些问题使用其他数据结构难以处理。最重要的事实是,答案只可能
是正确的或是真实值的近似。并且,可以很容易地估计正确答案或其准确性的概率。所以,
尽管不总是给出正确的答案,如果我们接受一定程度的错误,仍然可以使用它。
有很多具有这样的概率性质的数据结构。每个结构都解决一些具体的问题,并且由于
它们的随机性,就不能在每种情况下都去使用。给一个实际的例子,让我们谈谈其中特别
受欢迎的一个—HyperLogLog。
HyperLogLog(参见 https://en.wikipedia.org/wiki/HyperLogLog)是一种估算多重集中不
同元素数量的算法。使用普通集合,你需要存储每个元素,这对于非常大的数据集可能是
非常不切实际的。HLL 不同于将集合实现作为编程数据结构的经典方式。没有深入到实现
细节,它只专注于提供集合基数的近似。因此,实际值从不被存储。它们无法检索、迭代
和测试成员资格。HyperLogLog 在内存中处理时间复杂度和大小的精度以及正确性。例如,
HLL 的 Redis 实现只需要 12k 字节,标准误差为 0.81%,对集合大小没有实际限制。
使用概率型数据结构是解决性能问题的一种非常有趣的方法。在大多数情况下,它是
关于某种精确度或正确性的权衡,以加快处理速度或更好地利用资源。但它并不总是用于
上述场景。概率型数据结构经常用于键/值存储系统中以加速键查找。在这种系统中使用的
流行技术之一被称为近似成员查询(AMQ)。Bloom 过滤器(参考 https://en.wikipedia.org/
wiki/Bloom_filter)就是一个用于此目的有趣的数据结构。
缓存
当你的应用程序中的某些函数需要很长时间计算时,可以考虑的有用的技术是缓存。缓存
只是保存一个返回值,以供将来参考。可以缓存运行成本高的函数或方法的结果,只要:
• 该函数是确定性的,并且每次给定相同的输入时,结果具有相同的值;
• 函数的返回值继续有用并且在一段时间内有效(非确定性)。
换句话说,对于同一组参数,确定性函数总是返回相同的结果,而非确定性函数可能返回随时间变化的结果。这种方法通常可以大大减少计算时间,并且还可以节省大量的计
算机资源。
任何缓存解决方案的最重要的必要条件是拥有一个存储器,你可以取回保存的值,这
通常比重新计算更快。通常,以下情况比较适合使用缓存:
• 查询数据库的可调用项的结果;
• 渲染为静态值的可调用项的结果,例如文件内容,Web 请求或 PDF 渲染;
• 执行复杂计算的确定性可调用对象的结果;
• 全局映射,用于跟踪到期时间的值,例如 Web 会话对象;
• 需要经常和快速访问的结果。
缓存的另一个重要的使用案例是保存通过 Web 服务获得的第三方 API 的结果。通过减
少网络延迟,这可以大大提高应用程序性能,如果你需要为每一个 API 请求付费,那么这
样还可以为你节省费用。
根据你的应用程序架构,可以有很多种实现缓存的方式,并且复杂程度也各不相同。
有许多方法提供缓存,复杂的应用程序可以在不同级别的应用程序架构堆栈中使用不同的方
法。有时,高速缓存可以像在进程空间中保存的单个全局数据结构(通常为 dict)一样简
单。在其他情况下,你可能需要设置一个专门的缓存服务,在精心设计的硬件上运行。本节
主要介绍最受欢迎的缓存方法的基本信息,并指导你了解常见的使用案例以及常见的陷阱。
确定性缓存
确定性函数是缓存中最简单并且最安全的使用案例。如果给定完全相同的输入,确定
性函数总是返回相同的值,因此通常可以无限期地存储它们的结果。唯一的限制是用于缓
存的存储的大小。缓存这样的结果的最简单的方法是将它们放入进程内存,因为这里通常
是检索数据最快的地方。这种技术通常被称为记忆化。
记忆化在优化递归函数时非常有用,这些函数会针对多次相同的输入进行计算。我们
已经在第 7 章中讨论了的斐波那契序列的递归实现。当时,我们试图用 C 和 Cython 提高我
们的程序的性能。现在我们将尝试通过更简单的手段实现相同的目标即在缓存的帮助下。在
我们使用缓存之前,让我们先回忆一下 fibonacci()函数的代码如下:
def fibonacci(n):
“”“递归计算返回斐波那契数列的第 n 项
“””
if n < 2:
return 1
else:
return fibonacci(n - 1) + fibonacci(n - 2)
正如我们所看到的,fibonacci()是一个递归函数,如果输入值大于 2,则调用自身
两次。这使得它非常低效。运行时间复杂性是 O(2n),它的执行创建了一个非常深且庞大的
调用树。对于较大的值,此函数执行起来需要很长时间,并且很有可能会快速超过 Python
解释器的最大递归限制。
简单的记忆化尝试可以在字典中存储先前运行的结果,并在它们可用时取回它们。
fibonacci()函数中的递归调用都包含在这一行代码中:
return fibonacci(n - 1) + fibonacci(n - 2)
我们知道 Python 是从左到右地计算指令。这意味着,在这种情况下,对具有较高参数
值的函数的调用将在调用具有较低参数的函数之前执行。因此,我们可以通过构造一个非
常简单的装饰器来实现记忆化,如下所示:
def memoize(function):
“”" 记忆单参数函数的调用
“”"
call_cache = {}
def memoized(argument):
try:
return call_cache[argument]
except KeyError:
return call_cache.setdefault(argument,
function(argument))
return memoized
@memoize
def fibonacci(n):
“”" 递归计算返回斐波那契数列的第 n 项
“”"
if n < 2:
return 1
else:
return fibonacci(n - 1) + fibonacci(n - 2)
我们使用 memoize()装饰器的闭包的字典作为缓存值的简单存储。对该数据结构中
值的存储和取回的平均复杂度为 O(1),因此这大大降低了被记忆函数的总体复杂性。每个
唯一的函数调用将只计算一次。在不进入数学证明的
情况下,我们可以从视觉上推断出在不改变 fibonacci()函数的核心的情况下,我们将
复杂度从非常昂贵的 O(2n)降到了线性 O(n)。

我们的 memoize()装饰器的实现,当然不是完美的。对于那个简单的例子,它可以
良好地运行,但它绝对不是一个可复用的软件。如果你需要记住有多个参数的函数,或者
想要限制缓存的大小,你需要更通用的东西。幸运的是,Python 标准库提供了一个非常简
单和可重用的实用程序,当你需要在内存中缓存确定性函数的结果时,大多数情况下可以
使用它。它是来自 functools 模块的 lru_cache(maxsize,typed)装饰器。名称来
自 LRU 缓存,它代表最近最少使用(least recently used)。附加的参数可以对记忆行为进行
更精细的控制。
• maxsize:设置高速缓存的空间上限。None 表示没有限制。
• typed:定义了不同类型的值是否应该被缓存为相同的结果。
lru_cache 在斐波那契序列示例中的用法如下:
@lru_cache(None)
def fibonacci(n):
“”" 递归计算返回斐波那契数列的第 n 项
“”"
if n < 2:
return 1
else:
return fibonacci(n - 1) + fibonacci(n - 2)

http://www.jsqmd.com/news/996716/

相关文章:

  • 2026年汽车清洗液市场口碑观察:哪些品牌与产品值得关注? - 优质品牌商家
  • 2026年腾讯邮箱服务公司,哪个口碑好 - myqiye
  • 2026年兰州装饰公司怎么选?本地装修公司、工作室与设计机构深度行业分析 - 优质品牌商家
  • 2026年靠谱的外墙保温/烟台外墙保温/烟台外墙保温隔热值得信赖公司 - 行业平台推荐
  • VRCX终极指南:VRChat社交管理的免费神器,轻松提升虚拟社交体验
  • 临床工作流嵌入式AI:大模型在癌症诊疗中的安全落地实践
  • 命令注入新思路:当Ping测试遇到黑名单,如何用BurpSuite配合%0a和nc优雅拿Shell?
  • Windows下Java调ZeroMQ的PUB/SUB通信演示工程(含DLL和可直接运行代码)
  • 如何安装Switch大气层系统:5个简单步骤打造完美自制系统环境
  • AI自省机制:让大模型实时感知并熔断幻觉输出
  • 机器学习系统性落地:从业务语义到工程部署的实战地图
  • Open UI5 源代码解析之1473:FilterableListContent.js
  • 从‘感觉’到‘精确’:OpticStudio里单模光纤耦合仿真的三种武器(近轴/单模/POP)深度对比
  • 机器学习入门书单:按认知断层点匹配的七段式学习路径
  • 大连欧式宫廷风婚礼场地靠谱推荐 - myqiye
  • 多租户Kafka生产者配置与Spring Kafka集成
  • GitHub年度回顾工具:用数据叙事重构开发者体验
  • 告别网页乱码困扰:Chrome-Charset 扩展让你轻松修复字符编码问题
  • AIP企业级数据操作系统:上下文感知与操作闭环实战
  • 2026年质量好的郑州展厅装修/郑州火锅店装修/郑州写字楼装修/装修用户推荐公司 - 品牌宣传支持者
  • OpenSpeedTest™:如何用纯HTML5打造企业级网络测速解决方案?
  • LangChain+Weaviate+Streamlit构建企业级法律问答机器人
  • C语言的概念和特点是什么
  • 2026年成都废旧物资回收公司怎么选?多维度实测与行业趋势分析 - 优质品牌商家
  • 3分钟学会用手机识别电阻值:Resistor Scanner让电子设计更简单
  • 华硕笔记本性能控制终极指南:G-Helper轻量级替代方案完全解析
  • 推荐下靠谱的南天湖假日酒店? - 工业品牌热点
  • AI论文核心主张如何做到可证伪、可验证、可复现
  • 从S19文件到ECU内存:深入拆解UDS刷写背后的36、37服务数据流
  • 微信读书笔记助手WeReader:一键导出高效笔记的完整解决方案