所以我给了一个大集合(大约200k)的列表。每个都包含数字0到27的子集。我想返回两个列表,其中长度的乘积大于任何其他列表对的长度的乘积。还有另一个条件,即列表没有共同的数字。
我找到了一个算法(无法记住来源,道具的非特异性道歉),它利用了0到27号码的总子集数量少于那里的事实。是字典中的单词。
我做的第一件事就是循环遍历所有列表,找到构成它的整数的唯一子集,并将其索引为0到1之间的数字<&lt; 28。如下:
def index_lists(lists):
index_hash = {}
for raw_list in lists:
length = len(raw_list)
if length > index_hash.get(index,{}).get("length"):
index = find_index(raw_list)
index_hash[index] = {"list": raw_list, "length": length}
return index_hash
这给了我最长的列表以及每个子集的列表长度,这些列表实际包含在给定的列表集合中。当然,并非必须包括从0到(1 <28)-1的所有子集,因为不保证所提供的集合具有包含每个唯一子集的列表。
我想要的是,对于每个子集0到1&lt;&lt; 28(这次都是所有这些),是包含至多该子集的最长列表。这是杀死我的部分。在高级别,对于每个子集,它应该首先检查该子集是否包含在index_hash中。然后,它应该将散列中的条目的长度(如果它存在)与先前存储在当前散列中的当前子集的长度减去一个数字(这是内部循环27强)进行比较。其中最大的存储在外部循环的当前子集的新哈希中。现在的代码如下所示:
def at_most_hash(index_hash):
most_hash = {}
for i in xrange(1<<28): # pretty sure this is a bad idea
max_entry = index_hash.get(i)
if max_entry:
max_length = max_entry["length"]
max_word = max_entry["list"]
else:
max_length = 0
max_word = []
for j in xrange(28): # again, probably not great
subset_index = i & ~(1<<j) # gets us a pre-computed subset
at_most_entry = most_hash.get(subset_index, {})
at_most_length = at_most_entry.get("length",0)
if at_most_length > max_length:
max_length = at_most_length
max_list = at_most_entry["list"]
most_hash[i] = {"length": max_length, "list": max_list}
return most_hash
这个循环显然需要几个推理才能完成。我觉得我对python有足够的新意,我选择如何迭代以及使用什么数据结构可能是完全灾难性的。更不用说试图填写字典时的前瞻记忆问题了。是否有更好的结构或包可用作数据结构?或者更好的方法来设置迭代?或者我可以更稀疏地做到这一点?
算法的下一部分只是遍历我们给出的所有列表,并通过在at_most_hash中查找它们来获取子集的max_length和互补子集的最大长度的乘积,取最大值那些。
这里有什么建议吗?我很感激耐心地趟过我啰嗦的问题,而不是尝试编写这个问题。
理论上,这仍然比单独使用列表集合更好的方法,因为该方法大致为o(200k ^ 2),而且这个方法大致为o(28 * 2 ^ 28 + 200k),但我的实现抱着我。
答案 0 :(得分:2)
鉴于您的索引只是整数,您可以使用列表而不是dicts来节省一些时间和空间。我会进一步引入NumPy数组。它们提供紧凑的存储表示和高效的操作,使您可以在C中隐式执行重复性工作,从而绕过大量的解释器开销。
我们首先构建一个NumPy数组,而不是index_hash
,其中index_array[i]
是最长列表的长度,其元素集由i
或0
表示如果没有这样的清单:
import numpy
index_array = numpy.zeros(1<<28, dtype=int) # We could probably get away with dtype=int8.
for raw_list in lists:
i = find_index(raw_list)
index_array[i] = max(index_array[i], len(raw_list))
然后我们使用NumPy操作在C中冒泡长度而不是解释Python。事情可能会从这里变得混乱:
for bit_index in xrange(28):
index_array = index_array.reshape([1<<(28-bit_index), 1<<bit_index])
numpy.maximum(index_array[::2], index_array[1::2], out=index_array[1::2])
index_array = index_array.reshape([1<<28])
每个reshape
调用都会获取数组的新视图,其中偶数行中的数据对应于bit_index
清除位的集合,而奇数行中的数据对应于具有在bit_index
设置位。然后numpy.maximum
调用执行该位的冒泡操作。最后,index_array[i]
的每个单元格index_array
代表最长列表的长度,其中元素是集合i
的子集。
然后,我们计算互补指数的长度乘积:
products = index_array * index_array[::-1] # We'd probably have to adjust this part
# if we picked dtype=int8 earlier.
找到最佳产品的位置:
best_product_index = products.argmax()
和最长的列表,其元素是由best_product_index
及其补充表示的集合的子集,是我们想要的列表。
答案 1 :(得分:1)
评论时间太长,所以我会将其作为答案发布。将子集索引为整数的另一种直接方法是使用“位集”,其中二进制表示中的每个位对应于其中一个数字。
例如,集合{0,2,3}将由2 0 + 2 2 + 2 3 = 13表示{4,5}表示为2 4 + 2 5 = 48
这将允许您使用简单列表而不是字典和Python的通用散列函数。