前一段时间我有一次有趣的面试经历。这个问题开始很容易:
Q1 :我们有包含数字的袋子1,2,3,…, 100。每个数字仅出现一次,因此有100个数字。现在,从袋子中随机抽取一个号码。查找丢失的号码。
1
2
3
100
当然,我之前曾听过这个面试问题,所以我很快按照以下方式回答:
A1 :好吧,数字的总和1 + 2 + 3 + … + N是(N+1)(N/2)(请参阅Wikipedia:算术级数的总和)。因为N= 100,总和是5050。 因此,如果袋子中所有数字都存在,则总和为5050。由于缺少一个数字,所以总和小于该数字,而差就是那个数字。因此,我们可以找到O(N)时间和O(1)空间上缺少的数字。
A1 :好吧,数字的总和1 + 2 + 3 + … + N是(N+1)(N/2)(请参阅Wikipedia:算术级数的总和)。因为N= 100,总和是5050。
1 + 2 + 3 + … + N
(N+1)(N/2)
N= 100
5050
因此,如果袋子中所有数字都存在,则总和为5050。由于缺少一个数字,所以总和小于该数字,而差就是那个数字。因此,我们可以找到O(N)时间和O(1)空间上缺少的数字。
O(N)
O(1)
在这一点上,我认为我做得不错,但是突然之间,这个问题突然发生了变化:
Q2 :是的,但是现在如果缺少 两个 数字,您将如何处理?
我之前从未见过/听过/考虑过这种变化,所以我感到惊慌,无法回答这个问题。面试官坚持要了解我的思维过程,所以我提到也许我们可以通过与预期产品进行比较来获得更多信息,或者也许在从第一遍收集到一些信息之后再进行第二遍,等等,但是我真的只是在拍摄在黑暗中,而不是真正找到解决方案的清晰途径。
面试官的确通过说第二个方程式确实是解决问题的一种方式来鼓励我。在这一点上,我有点不高兴(因为事先不知道答案),问我这是否是一种通用的(读作:“有用的”)编程技术,还是仅仅是一个技巧/陷阱。
面试官的回答让我感到惊讶:您可以推广该技术以找到3个缺失的数字。实际上,您可以将其概括以找到 k个 缺失数字。
Qk :如果袋子中恰好缺少 k个 数字,您将如何有效地找到它?
这是几个月前,但我仍然不知道这种技术是什么。显然存在Ω(N)时间下限,因为我们必须至少扫描一次所有数字,但访问员坚持认为求解技术的 TIME 和 SPACE 复杂度(减去O(N)时间输入扫描)以 k 而非 N 定义。
Ω(N)
所以这里的问题很简单:
因此,当然,您必须再次扫描中的输入O(N),但是您只能捕获少量信息(用 k 而不是 N 定义),然后必须以某种方式找到 k个 缺失的数字。
这是Dimitris Andreou的链接的摘要。
记住第i次幂的总和,其中i = 1,2,..,k。这减少了求解方程组的问题
a 1 + a 2 + … + a k = b 1
a 1 2 + a 2 2 + … + a k 2 = b 2
…
a 1 k + a 2 k + … + a k k = b k
使用牛顿的身份,知道b 我可以计算
c 1 = a 1 + a 2 + … a k
c 2 = a 1 a 2 + a 1 a 3 + … + a k-1 a k
c k = a 1 a 2 … a k
如果展开多项式(xa 1)…(xa k),则系数将恰好为c 1,…,c k-参见Viète公式。由于每个多项式因子都是唯一的(多项式的环是欧几里德域),因此这意味着a i是唯一确定的,直到置换。
这证明了记忆力足以恢复数字。对于常数k,这是一个好方法。
然而,当k变化时,直接计算c 1,…,c k的方法是非常昂贵的,因为例如,c k是所有缺失数的乘积n!/(nk)!。为了克服这个问题,请在Zq字段中执行计算,其中q是质数,使得n<= q<2n-根据Bertrand的假设存在。由于公式仍然成立,并且多项式的因式分解仍然是唯一的,因此无需更改证明。您还需要一种用于对有限域进行因子分解的算法,例如Berlekamp或Cantor-Zassenhaus提出的算法。
常数k的高级伪代码:
对于变化的k,使用例如Miller-Rabin求素数n <= q <2n,并执行所有以q为模的数字简化的步骤。
编辑:此答案的先前版本指出,代替Z q,其中q是质数,可以使用特征2(q = 2 ^(log n))的有限域。事实并非如此,因为牛顿公式需要除以不超过k的数字。