检查 10 亿个手机号码是不是重复

Posted

技术标签:

【中文标题】检查 10 亿个手机号码是不是重复【英文标题】:check 1 billion cell-phone numbers for duplicates检查 10 亿个手机号码是否重复 【发布时间】:2011-12-03 22:05:58 【问题描述】:

这是一道面试题:

有 10 亿个有 11 位数字的手机号码,它们随机存储在一个文件中,用于 例如 12345678910,第一个数字必须是 1。通过这些数字看是否有 一个有重复的,只看是否存在重复,如果发现重复, 返回 True,或返回 False。 仅允许 10 MB 内存。

这是我的解决方案:

使用 hash(num)%1000 将所有这些数字散列到 1000 个文件中,然后重复的应该落入同一个文件中。

经过哈希处理后,我得到了 1000 个小文件,每个小文件都包含 1 million 数字 at most,对吗?我不确定这个,我只是这样做1 billion / 1000 = 1 million

然后为每个文件构建一个哈希表来存储每个数字和一个flag 代表它的出现。

我猜,用5 B代表数字,4 B代表较低的8 digits1 B代表较高的3 digits;实际上1 bit 就足够了flag,因为我只需要找出重复是否存在,只需要多少次。但是如何将1 bit 标志应用于每个数字?我迷路了,所以我选择bool 作为标志,1 B 被占用。 所以最后,哈希表中的每个数字都将采用5B<for number> + 1B<for flag> + 4B<for the next-pointer> = 10B,然后每个文件将采用10M作为哈希表。

这是我的愚蠢解决方案,请给我一个更好的解决方案。

谢谢。

跟进:

如果这 10 亿个电话号码中有 no duplicates,给定一个 电话号码,如何找出给定的一个is or is not in这些1 十亿数字? 使用尽可能少的内存

我想出了 2 个解决方案,

    电话号码可以用我上面说的5B表示,扫描文件,一次读一个号码,xor the given number with the one read from the file,如果结果是0,那么给定的在文件,这需要O(n) 时间,对吧?

    Partition这些数字根据leading bit变成2 small files,也就是说,带有leading 1-bit的数字进入一个文件,leading 0-bit进入另一个文件,同时计算有多少个数字在每个文件中,如果给定的数字落入1-bit文件,并且1-bit文件的countnot full,则again partition根据secondary leading-bit的1-bit文件,并检查给定的数字递归地;如果 1 位文件 is full,那么给定的数字必须在文件中,这需要 O(logn) 时间,对吧?

【问题讨论】:

什么是十亿? 1E9 ? @wildplasser,是的,10^9。 从您的帖子的其余部分可以明显看出,但美国人似乎有一个奇怪的习惯,即混淆 xxxillions ;-) 这是不久前的特色问题之一(每周通讯)。愚蠢的是,我想在阅读答案之前自己考虑一下... :) 既然你以某种方式“拥有”了输入,你是不是已经使用了超过 10MB 的空间?如果那部分不算在内,一个简单的 O(n^2) 算法将仅使用 O(1) 额外 【参考方案1】:

经过哈希处理后,我得到了 1000 个小文件,每个小文件包含 1 最多百万,对吧

不正确,在极端情况下,一个文件可能包含所有数字。

根据数字的前 x 位或后 x 位创建文件(忽略开头的 1)。创建这些文件时,您实际上可以删除这些数字,因为它们在文件中是相等的。这比散列好很多,因为虽然所有数字仍然可以在一个文件中结束,但现在这些数字的范围是有限的,所以你可以将其放入 10MB。

每个数字都可以用一个简单的位来表示,因为您需要的唯一信息是该数字是否以前出现过。您不必存储实际数字,位的 地址 就是数字。在 10MB 中,您可以存储 80M 位,因此您需要 1G/80M = 12.5 个文件,但请记住,这些数字必须不同,因此您实际上需要 100 个文件 (x=2)。

最后,您不必创建这些文件,也可以多次扫描整个文件。在这种情况下,您可以在内存中有多个位图,因为一个不占用 10MB。

我强烈建议阅读这本书,它以一个几乎相同的示例开头:http://www.amazon.co.uk/Programming-Pearls-ACM-Press-Bentley/dp/0201657880

【讨论】:

一件事我不太明白,如果我现在有100 文件,但每个文件中的数字仍然在0000 0000 - 9999 9999 的范围内,对吧?无论如何,我仍然不能用80M bits 代表他们。 根据问题,第一个数字始终为 1,因此您也可以丢弃它,留下 7 个数字,这意味着您只需要一个超过 1 兆字节的位图来表示每个文件。【参考方案2】:

不需要hash,10M = 83886080 bits,把每个数字放入[0, 83886080), [83886080, 83886080 * 2) ... [xx, 9999999999) (不考虑第一个数字), 大约999999999 / 83886080 = 120个文件,然后构建bit set,总共需要O(n)。

【讨论】:

@Dialectus 这只是你的理解,问题中没有提到。 @Dialectus 由于 OP 提出的解决方案还依赖于额外的磁盘存储,我认为可以肯定地说它是允许的,并且限制仅在 RAM 上。 我正在删除downvote,但我仍然认为限制是:您在磁盘上有一个大文件,除了读取该文件之外,您不能将磁盘用于其他任何事情。您还可以使用 10MB 的 RAM。如果这与 OP 的观点相冲突,那么我认为 OP 弄错了。 由于这是一个假设问题,因此允许磁盘交换是没有意义的,因为它只是一种较慢的 RAM 形式。很明显,辩证法是对的。【参考方案3】:

面试问题只对使用的内存施加限制,而不是对提供答案所需的时间施加限制。

因此,这样实现这个问题是合理的:

take the first number
compare it to all numbers following it
take the second number
compare it to all numbers following it
...

这需要大量时间来处理十亿个数字 (O(n^2)),但不会占用超过 10MB 的内存空间。

【讨论】:

据了解,候选人将采用表现最佳的解决方案。对于 10 亿 O(n2) 的输入,面试官可能会寻找最糟糕的解决方案。不要忘记你会做大量的 IO 来匹配你的解决方案。【参考方案4】:

最快的解决方案(在程序员开销方面也是如此:)

# Generate some 'phones'
yes 1 | perl -wne 'chomp; ++$a; print $_."$a\n";' > phones.txt

# Split phones.txt in 10MB chunks
split -C 10000000 phones.txt

# Sort each 10MB chunk with 10MB of memory
for i in x??; do sort -S 10M $i > $i.srt; echo -ne "$i.srt\0" >> merge.txt; done

# Merge the shorted chunks with 10MB of memory
sort -S 10M --files0-from=merge.txt -m > sorted.txt

# See if there is any duplicates
test -z $(uniq -d merge.txt)

检查内存使用限制是否满足 pmap $(pidof sort) 例如:

【讨论】:

upvoted.. 但请注意 split 看起来很可疑,它会拆分数字,这不是很酷:) 此外,100 个文件的合并部分是相当大的开销,这绝对不是最快的解决方案(就运行时间而言)。 @yi_H 是的,也给你投票,分裂是假的,应该是行号。我在拆分中将 -b 更改为 -C。 @piotr 那是什么语言?我无法从这些符号中得到任何东西【参考方案5】:

您可以使用包含 m 位数组并使用 k 个哈希函数的布隆过滤器。 虽然我不确定您可能需要多少个哈希函数。

【讨论】:

【参考方案6】:

您可以遵循 bitset 技术。参考这个问答:Find an integer not among four billion given ones

【讨论】:

以上是关于检查 10 亿个手机号码是不是重复的主要内容,如果未能解决你的问题,请参考以下文章

如何生成1亿个手机号码?Python生成随机数的22种方法,random函数太强了

检查互联网数据是不是接收(Android)[重复]

腾讯三面:40亿个QQ号码如何去重?

腾讯三面:40 亿个 QQ 号码如何去重?

如何使用正则表达式检查不应包含重复数字的手机号码

如何在android中检查电话号码是不是有效