假设我有一个可能较大的32位数字数组,可能有几百万个条目......有没有办法有效地选择一些在数组中没有出现的相同位宽?天真地,我可以选择一些适当宽度的随机数,然后检查数组,如果它出现在数组中,返回并选择另一个,但由于数组中元素的数量,可能重新扫描数组的成本反复令人担忧。在实践中,我不确定这会有多大的问题,因为永远不会超过大约2000万个条目,而唯一值的数量是几十亿,所以可能需要重新扫描的总体可能性阵列很少发生,这不是问题。尽管如此,这样的算法可能会多次重复地重新扫描阵列这一事实对我来说很麻烦,如果可以找到的话,我理想地想要一个更好的解决方案。从技术上讲,这个数字甚至不必是随机的......确定性值是可以接受的,唯一的要求是产生的数字必须是唯一的,而不是已经出现在列表中。
那么...是否有一种运行时有效的方法来生成一个唯一的数字,或者我上面描述的随机数方法是唯一可行的方法吗?关于时间/空间权衡,我对速度更感兴趣,所以保证O(n)算法是理想的,但我可能不希望任何额外的空间要求大于约O(n log n)。 / p>
这最终将在C中实现,但是可以接受任何语言中性术语中的算法描述。
答案 0 :(得分:2)
扫描大型阵列的大部分成本都在内存访问中。您可以通过选择一小组候选随机数来大幅降低重新扫描的风险。
在扫描期间,将该组的每个成员与当前数组元素进行比较。如果匹配,则删除set成员。如果这使得集合为空,则必须返回并使用新集合重新开始。如果你使用非空候选集到达最后,请挑选任何幸存的成员。
答案 1 :(得分:2)
Bloom Filter符合您的需求。它可以让您对百万元素数组进行简明扼要的总结,并提供快速的成员资格测试。它允许误报,但没有误报,适合您的应用,不需要完美的随机性。
# python-style-psuedo-code
# build concise searchable summary of the known members
members = BloomFilter(data)
# choose 1000 values known not to be in the members
for i in range(1000):
candidate = random.randrange(2 ** 32)
while candidate in members:
candidate = random.randrange(2 ** 32)
print candidate
答案 2 :(得分:1)
一个好的解决方案就是按照你的说法(挑选并重新选择碰撞),但将数字保存在哈希表中。
如果您排除的数字数组本身分布均匀,您甚至不需要哈希函数。
确保按排序顺序存储碰撞,以减少约1/2的工作量。理想情况下,在二叉树中放入桶冲突,但有序链表可能与您的数字一起使用。
它很好,因为通过调整存储桶的数量可以调整它。
一旦你构建了哈希表,它就是O(N)。它是渐近平均O(N ^ 2),如果你试图选择“没有替换”的话。并将您找到的那个添加到'排除'列出每一步。然而,N ^ 2的常数在您的规模上可能会很小。
请注意,选择一个随机的32位值大约有1:2000的“击中”值。 a'排除'清单2,000,000。
如果排除列表更密集(K~2 ^ 32-1),您最终确定范围(0,2 ^ 31-1-K)中的随机数,然后计数到正确的间隙。 但是你的数字肯定会通过任何测试,因为排除量与游泳池大小相比较小。
如果你不太关心统计准确性,你只需要跳进去,然后点击+1,如果你点击排除'。
如果您要在某些模拟或加密应用程序中生成准确的统计信息,请不要执行+1 bodge。如果您正在进行游戏编程或只是在(比如说)自动测试套件中寻找健康的传播,我希望它没问题。请注意' clumping'是排除'排除'密度
答案 3 :(得分:1)
此解决方案具有O(n)插入时间,最大为O(n) - 但可能更少 - 找到唯一编号的时间。
创建一个2n位的附加结构,其中每两位表示数组元素(元素+ 1)和(元素-1)是否存在于列表中。
要查找唯一编号,请遍历位结构,直到遇到设置为零的位。
在数组中插入新数字时,请更新相应的位。例如,插入元素2,表示数组中任何3和1的位将被更新,以显示3-1中(在3的情况下)和1 + 1(在1的情况下)现在存在于阵列。
通过将每个元素的指针添加到具有相同整数的下一个元素,可以减少插入/删除时间。
(改编自我在这里的回答Efficiently choose an integer distinct from all elements of a list)