Warning: file_get_contents(/data/phpspider/zhask/data//catemap/8/python-3.x/17.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 如果使用很长的字符串作为键,在Dict中搜索的时间复杂度是多少?_Python_Python 3.x_Dictionary_Hash_Hashtable - Fatal编程技术网

Python 如果使用很长的字符串作为键,在Dict中搜索的时间复杂度是多少?

Python 如果使用很长的字符串作为键,在Dict中搜索的时间复杂度是多少?,python,python-3.x,dictionary,hash,hashtable,Python,Python 3.x,Dictionary,Hash,Hashtable,我从python3文档中读到,python使用dict()的哈希表。因此,搜索时间复杂度应为O(1),O(N)为最坏情况。然而,最近我上了一门课,老师说只有当你用int作为键时才会发生这种情况。如果使用长度为L的字符串作为键,则搜索时间复杂度为O(L) 我写了一段代码来测试他的诚实 随机导入 导入字符串 从时间导入时间 将matplotlib.pyplot作为plt导入 def随机字符串(stringLength=10): “”“生成固定长度的随机字符串”“” 字母=字符串。ascii_小写 返

我从python3文档中读到,python使用dict()的哈希表。因此,搜索时间复杂度应为O(1),O(N)为最坏情况。然而,最近我上了一门课,老师说只有当你用int作为键时才会发生这种情况。如果使用长度为L的字符串作为键,则搜索时间复杂度为O(L)

我写了一段代码来测试他的诚实

随机导入
导入字符串
从时间导入时间
将matplotlib.pyplot作为plt导入
def随机字符串(stringLength=10):
“”“生成固定长度的随机字符串”“”
字母=字符串。ascii_小写
返回“”。在范围(stringLength)内为i连接(随机选择(字母))
def测试(L):
#L:键的整数长度
N=1000#钥匙数量
d=dict()
对于范围(N)中的i:
d[随机字符串(L)]=无
tic=时间()
对于d.keys()中的键:
d[钥匙]
toc=时间()-tic
tic=时间()
对于d.keys()中的键:
通过
t_idle=time()-tic
t_总计=toc-t_空闲
返回t_总数
L=[i*10000表示范围(5,15)内的i]
ans=[测试(l)中的l]
plt.图()
plt.绘图(L,ans)
plt.show()
结果非常有趣。如您所见,x轴是用作键的字符串的长度,y轴是查询字典中所有1000个键的总时间

有人能解释这个结果吗


请对我温柔一点。如您所见,如果我问这个基本问题,这意味着我没有能力阅读python源代码或相当复杂的内部文档

由于字典是哈希表,在哈希表中查找密钥需要计算密钥的哈希,因此在字典中查找密钥的时间复杂度不能小于哈希函数的时间复杂度

在当前版本的CPython中,如果是第一次对特定字符串对象进行哈希运算,则长度为L的字符串需要O(1)个时间来计算哈希值,如果已计算该字符串对象的哈希值(因为哈希值已存储),则需要O(1)个时间:

>>>从timeit导入timeit
>>>s='b'*(10**9)#长度为10亿的字符串
>>>timeit(lambda:hash,number=1)
0.48574538500002973#半秒
>>>timeit(lambda:hash,number=1)
5.301000044255488e-06#5微秒
这也是在字典中查找关键字所需的时间:

>s='c'*(10**9)#长度为10亿的字符串
>>>d=dict()
>>>timeit(λ:s在d中,数字=1)
0.485215068999167#半秒
>>>timeit(λ:s在d中,数字=1)
4.491000026973779e-06#5微秒
您还需要注意,字典中的键不仅仅是通过其散列查找的:当散列匹配时,它仍然需要测试您查找的键是否等于字典中使用的键,以防散列匹配为假阳性。在最坏的情况下,测试字符串的相等性需要O(L)时间:

s1='a'*(10**9) >>>s2=‘a’*(10**9) >>>timeit(lambda:s1==s2,数字=1) 0.2006020820001595 对于长度为L的键和长度为n的字典:

  • 如果该密钥不在字典中,并且其哈希已被缓存,则需要0(1)个平均时间来确认该密钥不存在
  • 如果密钥不存在且其散列未被缓存,则由于计算散列,需要O(L)个平均时间
  • 如果密钥存在,则由于等式测试,无论是否需要计算哈希,都需要O(L)个平均时间来确认它是否存在
  • 最坏的情况总是O(nL),因为如果每个哈希冲突,并且字符串除了最后几位都相等,那么必须执行n次缓慢的相等测试
仅当使用int作为键时。如果使用长度为L的字符串作为键,则搜索时间复杂度为O(L)

只是为了解决kaya3答案中没有涉及的一点

为什么人们经常说哈希表插入、查找或擦除是一个O(1)操作

对于散列表的许多实际应用程序,无论存储多少个键,键的典型长度都不会增加。例如,如果您制作了一个哈希集来存储电话簿中的姓名,那么前100人的平均姓名长度可能非常接近每个人的平均姓名长度。因此,当您有一组1000万个名称时,与最初的100个名称相比,查找名称所花费的时间也不会更糟(这种分析通常会忽略CPU缓存大小以及程序开始交换时RAM与磁盘速度对性能的影响)。您可以在不考虑名称长度的情况下对程序进行推理:例如,插入一百万个名称可能需要比插入一千个名称大约长一千倍的时间


其他时候,应用程序有一个哈希表,其中的键可能会有很大的变化。想象一下,假设有一个散列集,其中键是二进制数据编码视频:一个数据集是旧的标准定义24fps视频剪辑,另一个是8k UHD 60fps电影。插入这些密钥集所需的时间不仅仅是这些密钥数量的比率,因为在密钥散列和比较中涉及的工作量大不相同。在这种情况下,如果您想对不同大小的键的插入时间进行推理,如果没有相关因素,big-O性能分析将是无用的。您仍然可以描述具有相似大小键的数据集的相对性能,只考虑正常的哈希表性能特征。当密钥散列时间可能成为问题时,你可能想考虑你的应用程序设计是否仍然是一个好主意,或者是否你可以使用一组说的文件名而不是原始的视频数据。

而不是强迫每个人读代码,试着理解你正在做什么,然后预言什么。