前段时间我有一次有趣的面试经历。问题一开始很简单:

Q1:我们有一个袋子,里面有数字1,2,3,…,100。每个数字恰好出现一次,所以有100个数字。现在从袋子里随机抽取一个数字。找到丢失的号码。

当然,我以前听过这个面试问题,所以我很快就回答了这个问题:

A1:嗯,1 + 2 + 3 +…+ N的和是(N+1)(N/2)(参见维基百科:等差级数的和)。当N = 100时,和是5050。 因此,如果所有的数字都在袋子里,总和将恰好是5050。因为少了一个数,总和就会小于这个数,差的就是这个数。所以我们可以在O(N)时间和O(1)空间中找到这个缺失的数。

在这一点上,我认为我做得很好,但突然间,问题发生了意想不到的转变:

这是正确的,但是如果少了两个数字,你会怎么做?

我以前从未见过/听过/考虑过这种变化,所以我很恐慌,无法回答这个问题。面试官坚持要知道我的思考过程,所以我提到,也许我们可以通过与预期产品进行比较来获得更多信息,或者在从第一次传递中收集到一些信息后再进行第二次传递,等等,但我真的只是在黑暗中拍摄,而不是真正有一个明确的解决方案的路径。

面试官试图鼓励我说,有第二个方程确实是解决问题的一种方法。在这一点上,我有点不安(因为事先不知道答案),并问这是一种通用的(阅读:“有用的”)编程技术,还是只是一个技巧/答案。

面试官的回答让我惊讶:你可以把这个技巧概括为3个缺失的数字。事实上,你可以推广它来找到k个缺失的数。

Qk:如果袋子里少了k个数字,你如何有效地找到它?

这是几个月前的事了,我还不明白这个技巧是什么。显然有一个Ω(N)的时间下限,因为我们必须扫描所有的数字至少一次,但面试官坚持认为,解决技术的时间和空间复杂度(减去O(N)次输入扫描)定义为k而不是N。

所以问题很简单:

如何解决Q2? 你会如何解决Q3? 如何求解Qk?


澄清

Generally there are N numbers from 1..N, not just 1..100. I'm not looking for the obvious set-based solution, e.g. using a bit set, encoding the presence/absence each number by the value of a designated bit, therefore using O(N) bits in additional space. We can't afford any additional space proportional to N. I'm also not looking for the obvious sort-first approach. This and the set-based approach are worth mentioning in an interview (they are easy to implement, and depending on N, can be very practical). I'm looking for the Holy Grail solution (which may or may not be practical to implement, but has the desired asymptotic characteristics nevertheless).

当然,你必须以O(N)为单位扫描输入,但你只能捕获少量的信息(用k而不是N定义),然后必须以某种方式找到k个缺失的数字。


当前回答

谢谢你这个有趣的问题:

因为你让我想起了牛顿的工作,它真的可以解决这个问题

请参考牛顿恒等式

As变量的数量=方程的数量(必须为一致性)

我认为,对于这个问题,我们应该提高袋数的幂,以便创建不同的方程。

我不知道,但是,我相信如果有一个函数,比如f,我们要加上f(xi)

x1+x2+…+ xk = z1

x12 + x22 + ... + xk2 = z2

............

............

............

x1k + x2k + ... + xkk = XP

休息是一个不确定时间和空间复杂性的数学工作,但牛顿恒等式肯定会发挥重要作用。

我们不能用集合理论吗 .difference_update()或在这个问题方法中是否有线性代数的机会?

其他回答

有一个通用的方法来解决这样的流问题。 我们的想法是使用一些随机化,希望将k个元素“分散”到独立的子问题中,在那里我们的原始算法为我们解决了问题。该技术用于稀疏信号重建等。

创建一个大小为u = k^2的数组a。 选取任意通用哈希函数h:{1,…,n} ->{1,…,u}。(如multiply-shift) 对于1中的每一个i,…, n增加a[h(i)] += i 对于输入流中的每个数字x,减去a[h(x)] -= x。

如果所有缺失的数字都已散列到不同的bucket中,则数组的非零元素现在将包含缺失的数字。

根据通用哈希函数的定义,特定对被发送到同一桶的概率小于1/u。由于大约有k^2/2对,我们有错误概率不超过k^2/2/u=1/2。也就是说,我们成功的概率至少是50%,如果我们增加u,我们的机会就会增加。

注意,这个算法占用k^2 logn位的空间(每个数组桶需要logn位)。这与@Dimitris Andreou的答案所需要的空间相匹配(特别是多项式因式分解的空间要求,它碰巧也是随机的。) 该算法每次更新的时间也是常数,而不是幂和情况下的时间k。

事实上,通过使用评论中描述的技巧,我们甚至可以比幂和法更有效。

一种方法是对质数101取模。

计算并存储整数1到100的乘积,对该数字取101的模。小外显:结果是1。

计算并存储所有数字1到100的和,对结果对101进行模运算。小exo:结果是0。

现在假设袋子里的数字x和y都被拿走了。

求包里所有东西对101模的乘积和。这样我就知道的值

A = x+y和 b = x * y

modulo 101。

现在很容易求出x和y对101的模(求解含有101个元素的有限域上的二次多项式)。

现在你知道了x和y对101的模。但是因为你知道x和y都小于101,所以你知道它们的真实值。

You can motivate the solution by thinking about it in terms of symmetries (groups, in math language). No matter the order of the set of numbers, the answer should be the same. If you're going to use k functions to help determine the missing elements, you should be thinking about what functions have that property: symmetric. The function s_1(x) = x_1 + x_2 + ... + x_n is an example of a symmetric function, but there are others of higher degree. In particular, consider the elementary symmetric functions. The elementary symmetric function of degree 2 is s_2(x) = x_1 x_2 + x_1 x_3 + ... + x_1 x_n + x_2 x_3 + ... + x_(n-1) x_n, the sum of all products of two elements. Similarly for the elementary symmetric functions of degree 3 and higher. They are obviously symmetric. Furthermore, it turns out they are the building blocks for all symmetric functions.

你可以通过注意s_2(x,x_(n+1)) = s_2(x) + s_1(x)(x_(n+1))来构建初等对称函数。进一步思考应该会使您相信s_3(x,x_(n+1)) = s_3(x) + s_2(x)(x_(n+1))等等,因此它们可以在一次传递中计算。

我们如何知道数组中缺少了哪些项?考虑多项式(z-x_1) (z-x_2)……(z-x_n)。如果你输入任意一个数字x_i,它的值都是0。展开多项式,得到z^n-s_1(x)z^(n-1)+。+ (-1)^n s_n。初等对称函数也出现在这里,这并不奇怪,因为多项式应该保持不变,如果我们对根进行任何排列。

所以我们可以建立一个多项式,并尝试因式分解来找出哪些数不在集合中,就像其他人提到的那样。

Finally, if we are concerned about overflowing memory with large numbers (the nth symmetric polynomial will be of the order 100!), we can do these calculations mod p where p is a prime bigger than 100. In that case we evaluate the polynomial mod p and find that it again evaluates to 0 when the input is a number in the set, and it evaluates to a non-zero value when the input is a number not in the set. However, as others have pointed out, to get the values out of the polynomial in time that depends on k, not N, we have to factor the polynomial mod p.

这是个很简单的问题

void findMissing(){
    bool record[N] = {0};
    for(int i = 0; i < N; i++){
        record[bag[i]-1] = 1;
    }
    for(int i = 0; i < N; i++){
        if(!record[i]) cout << i+1 << endl;
    }
}

O(n)时间和空间复杂度

我还没有检查数学,但我怀疑在计算Σ(n)的同时计算Σ(n^2)将提供足够的信息来得到两个缺失的数字,如果有三个,也要计算Σ(n^3),等等。