首先,您的实现中有一个错误。想象一下mydict = {'AB': 1, 'A': 1},并且项目是按书面顺序读取的:
结论:mydict = {'AB': 1, 'A': 1} 但应该是{'AB': 1}。
要解决此问题,您必须通过增加 len 对键进行排序,以确保丢弃较小的键:
def original_func(mydict):
newdict = {}
# sort by inc key len
items = sorted(mydict.items(), key=lambda i: len(i[0]))
for key1, value1 in items:
for key2, value2 in items:
if key1 in key2 and value1 == value2:
if key1 in newdict:
del newdict[key1]
newdict[key2] = value2
return newdict
其次,算法的时间复杂度为O(n^2 * K),其中K是最长key的大小。我假设您在实际用例中有更大的字典。您可以通过以下方式改进:
- 只比较具有相同值的键;
- 仅将键与之前的超级键进行比较,而不是所有键;
如果您有 V 不同的值和 S < n/V 相同值的平均超级键,则平均时间复杂度将大致为 O(V * S^2 * K)。除非您处于退化的情况下,否则这会更快。
为了测试这一点,我们创建了一个巨大的字典:
from random import shuffle, randint
mydict = {}
s = list("ABCDEFGHIJ")
for i in range(1000):
shuffle(s)
key = "".join(s[:randint(1, 7)])
mydict[key] = randint(1, 7)
然后是函数:
def new_func(mydict):
# group the keys by value
keys_by_value = {}
for k, v in mydict.items():
keys_by_value.setdefault(v, []).append(k)
newdict = {}
for v, ks in keys_by_value.items():
# for each value, find the superkeys
for sk in find_superkeys(ks):
# and add the mapping superkey -> value
newdict[sk] = v
return newdict
当然,程序的核心是find_superkeys 函数。这里没有魔术,但是您可以将键分组len,因为同一 len 的两个键要么相等要么不相交(也许有人知道更快的方法?):
def find_superkeys(ks):
# group keys by len (-len to sort by decreasing len)
keys_by_neg_len = {}
for k in ks:
keys_by_neg_len.setdefault(-len(k), []).append(k)
superkeys = []
for _, ks in sorted(keys_by_neg_len.items()):
cur = []
for k in ks:
# keep k if k not in any of superkeys
# by using a decreasing len, we avoid useless tests like 'AB' in 'A'
if all(k not in k2 for k2 in superkeys):
cur.append(k)
# here, cur contains superkeys of a given len
superkeys += cur
return superkeys
还有基准:
import timeit
assert original_func(mydict) == new_func(mydict)
print(timeit.timeit(lambda: original_func(mydict), number=100))
# 1.6872997790005684
print(timeit.timeit(lambda: new_func(mydict), number=100))
# 0.15774182000041037