从内存的角度来看,最有效的方法是存储100万个电话号码?
显然,这是Google的面试问题,请提出您的想法。
从内存的角度来看,最有效的方法是存储100万个电话号码?
显然,这是Google的面试问题,请提出您的想法。
Apparently this is an interview question at Google, although this seems like its a bit too easy.
。对我而言,艰难并不是一件容易的事
Answers:
如果内存是我们最大的考虑因素,那么我们根本不需要存储数字,只需存储i和i + 1之间的差值即可。
现在,如果电话号码的范围在200 0000-999 9999之间,则可能有7,999,999个电话号码。由于我们有100万个数字,并且假设它们是均匀分布的,那么在连续数字n_i和n_i + 1之间的期望距离为E = n_i + 1-n_i〜8(3位)。因此,对于32位int,我们最多可以存储10个连续偏移量(〜400kb最佳总内存占用量),但是在某些情况下,我们可能需要大于8的偏移量(也许有400个偏移量,或者1500 ??)。在这种情况下,我们可以简单地将int的前2位保留为标头,以告知我们用于读取其存储的位的帧大小。例如,也许我们使用:00 = 3x10、01 = 5x6、10 = 7x4、11 = 1 * 30。
"1"x10^10
(所有100亿位均为1)。所有从0开始的交替数字都是"01"x(10^10)/2
(重复字符串“ 01” 50亿次)。如果您获得大约五亿个数字的随机分布,则该方法将失败,其中编码大小可能超过100亿位。
一个可能的解决方案是
Delta频率分布将高度偏斜。
我使用7 + 3 + 3 + ...位编码,使用简单的类似BER的打包方法对增量进行了实验。编码功能原为
def delta_write(x, b1, b2):
lim = 1 << (b1 - 1)
if x < lim:
bit_write(x, b1)
else:
bit_write(lim + (x & (lim - 1)), b1)
delta_write(x >> (b1 - 1), b2, b2)
(两个参数7和3是通过实验确定的)
通过这种方法,我得到了一百万个10位数字,其中前5位是从一千个随机前缀中选择的,每个数字平均为8.83位(打包大小为1104188)。
首先,我观察到它们从不以0开头,因为0开头被用作转义字符。因此,我可以简单地将电话号码视为整数。如果不是这种情况,我只需在数字前加上“ 1”,然后将其转换为整数即可。这不会显着影响编码效率(可能是几个字节不变的开销)。如果电话号码内的10位数字之外还有其他字符,则只能使用大于10的基数进行编码。
我会按尺寸升序排序。然后计算差异。然后使用protobuf作为打包的重复字段序列化差异。
该方法类似于RexKerr的方法,除了我在霍夫曼编码器上使用protobuf的惰性解决方案。可能更大一点,因为protobuf整数编码是通用的,并且没有考虑电话号码的概率分布。但是,由于我只需要使用现有的protobuf序列化程序,因此编写代码要容易得多。一旦超出UInt64的大小,这将成为问题,即电话号码长于19位数字。该文件格式仍然支持它,但是大多数实现均不支持。
没有索引的访问时间将是非常糟糕的,但是它应该相当紧凑。
作为特殊Trie数据结构的三元搜索树将提高存储效率,并且仍将(作为Trie)启用部分匹配。
如果查看“北美编号计划”的数据字段表示形式,您将得出结论:每个区号中每个电话号码字段中存储的美国电话号码1+ NPA + NXX + xxxx少于22位。添加区号,代表任何美国(加上加拿大)电话号码的数据都可以轻松容纳32位。这是位字段表示形式,而不是int形式。
但是,您对此的思考不应以美国为中心。当然,问题不仅仅在于将一百万个电话号码压缩成尽可能少的位数。
美国电话号码可以短至3位数字(内部PBX拨号计划)到最长22位数字(1 + NPA + NXX + xxxx + 11位内部PBX拨号计划)。如果电话号码仅限于ITU指定的号码格式,则您最多可以输入15位数字,外加1位的“ +”号。
然后,您可能应该定义一个介于3位和22位之间(对于ITU为15位)的任何电话号码的可变位域表示,每个位域都有一个X比特头域,以指示该域的格式。
然后将这些位字段放入压缩的位数组中。可能可以使用trie或某些其他方法来索引该位数组。
效率的高低取决于一百万个电话号码的格式,您要多快地访问它们以及将来以不同格式将数据结构用于更多电话号码的灵活性。它不仅在为“正确”答案恕我直言计算位数。
我认为我们可以在这里使用大小为100万的位向量。
Java示例:
private BitSet dir = new BitSet(1000000);
public void addTelephoneNumber(int number)
{
dir.set(number);
}
public void removeTelephoneNumber(int number)
{
if (dir.get(number))
{
dir.flip(number);
}
}
public boolean isNumberPresent(int number)
{
return dir.get(number);
}
我猜一个未签名的Int32或国际数字一个未签名的Int64
使用将为4MB的32位无符号整数
Int
或时髦的包装才能消除可能未使用的0-999999999值(这是您的34位空间的一半!)。
这确实取决于您要在存储的数据库上运行哪些操作。
普通的方法是使用无符号整数,如果您只需要存储它们,则使用字典对原始文本表示进行一些压缩可能会更小。
在求职面试中,这个问题的重点是扩大申请人的解决问题的能力。因为问题的重点是记忆效率,所以我认为正确的答案是问访调员:“电话号码是国际电话,还是仅限于一个国家?” 如果将号码限制在一个国家/地区,则每个国家/地区都有按州和市分配电话号码的简单规则,从而简化了最大化存储效率的任务。
8百万个数字,其中800万个数字之一为1(已使用)或0(可用)。
100 0000
900 0000
= 8 million phone numbers, bit 1 = 1000000 and bit 8 million = 9000000
/********************************************************************************
Filename: Phone_Numbers.c
Author: Paul Romsky
Company: Autoliv AEL
Date: 11 MAR 2013
Problem: What is the most efficient way, memory-wise, to store 1 million
phone numbers?
Caveats: There is no mention if the numbers are contiguous or not, so, to save
space the numbers should be contiguous. The problem (as a specification)
is rather vague and leaves a lot to interpretation, so many different
methods may be desired, but which one(s) desired is not surely known.
Are the phone numbers just the extension (last four digits), or do they
include the exchange (the leading 3 digits), or do they also include
area code and/or international numbers?
There is no mention of the first number, only the range of numbers, so
the first number 000-0000 is used. Although many numbers are not
normally used, they could in fact be used as valid number sequences
within the phone companies and are thus phone numbers nonetheless.
Solution: A simple algorithm. If the numbers are not contiguous a fractal algorithm
could be used.
A standard ANSI C compiler should pack this program into a very small
assembly module of only a few bytes.
Revisions:
Rev Date By Description
--- ----------- -------------------- -------------------------------------------
- 11 MAR 2013 P. Romsky Initial Coding
********************************************************************************/
/* Includes */
#include <stdio.h>
/* Functions */
/********************************************************************************
*
* Main Entry Point
*
********************************************************************************/
int main()
{
unsigned int Number;
/* 1,000,000 Phone Number Storage 000-0000 through 999-9999 */
for(Number = 0000000; Number < 10000000; Number++)
{
/* Retrieve Numbers */
printf("%07u\n", Number);
}
return 0;
}
/* End */