我很高兴其他答案证实了我的怀疑,这是一个 NP 完全问题。目前没有已知的解决方法,在最坏的情况下,尝试所有可能的键子集。
这是我的算法,它在O(n^2*2^k) 时间和O(nk^2+2^k) 空间中运行,其中n 是列表中的项目数,k 是每个项目的属性数。
只要2^k n^2,这将在大致多项式时间内运行。
def get_unique_key_values(objs):
key = get_unique_key(objs)
return [{ k: obj[k] for k in key } for obj in objs ]
def get_unique_key(objs):
return get_unique_key_set(objs, { k for obj in objs for k in obj }, [])
def get_unique_key_set(objs, keys, tested_keys):
if len(keys) == 0 or not all_unique(objs):
# keys is either the empty set, or this subset of keys
# does not guarantee uniqueness
return False
# the smallest number of keys required for a unique key
best_key_set = set(keys)
# delete each key one at a time and check if the list of
# items are still unique
for del_key in keys:
tmp_keys = set(keys)
tmp_keys.remove(del_key)
# if we've already tested this subset, skip it and all its children
if tmp_keys in tested_keys:
continue
# keep track of subsets we've tested so we don't retest them--significant trimming
tested_keys.append(tmp_keys)
# generate a list of objects with only the current set of keys
tmp_objs = [{ k: obj[k] for k in tmp_keys } for obj in objs]
# continue to delete keys from the current subset until we find a subset
# of size 1, or the current tmp_keys is optimal
tmp_key_set = get_unique_key_set(tmp_objs, tmp_keys, tested_keys)
if tmp_key_set == False:
continue
if len(tmp_key_set) < len(best_key_set):
best_key_set = tmp_key_set
return best_key_set
# O(n^2) algorithm for checking that every element in the list is unique
def all_unique(objs):
for i in range(len(objs) - 1):
for j in range(i + 1, len(objs)):
if objs[i] == objs[j]:
return False
return True
objects = [
{ 'a': 1, 'b': 2, 'c': 2 },
{ 'a': 1, 'b': 3, 'c': 2 },
{ 'a': 1, 'b': 3, 'c': 3 }
]
print(get_unique_key(objects))
# prints set([ 'b', 'c' ])
objects = [
{ 'a': 1, 'b': 2, 'c': 2 },
{ 'a': 2, 'b': 3, 'c': 2 },
{ 'a': 3, 'b': 3, 'c': 3 }
]
print(get_unique_key(objects))
# prints set([ 'a' ])
我在编写这个脚本时做了一些假设,例如所有对象都具有相同的属性集。如果某些属性只存在于某些对象上,您可能需要对脚本进行一些更改。
您可以通过将tested_keys 更改为一个集合并为键数组创建一个散列函数并将散列存储在集合中来加快这一速度。
为对象字典创建哈希函数可以将all_unique 转换为O(n) 算法,从而将总运行时间减少到O(n2^k)。具有讽刺意味的是,在显着减少运行时间的同时,它增加了它将成为指数时间算法的可能性,因为2^k < n 更难以满足。
请参阅 this answer 了解如何创建这些哈希。