确定数据流中缺少的号码


14

我们从集合接收到成对的不同数字流。{ 1 n }n1{1,,n}

如何通过一次读取流并仅使用位内存的算法来确定丢失的数字?O(log2n)

Answers:


7

您知道,因为可以用位,这可以在内存和一条路径中完成(只需找到,这是缺少的数字)。小号=ÑÑ+1i=1ni=n(n+1)2 Ó日志Ñø日志Ñš-çÙ- [R[RËÑ小号ùS=n(n+1)2O(log(n))O(logn)ScurrentSum

但是这个问题可以在一般情况下(对于常数)解决:我们有缺失的数字,找出它们全部。在这种情况下,不是仅计算总和,而是为所有计算的j'st幂的总和(我假设缺少数字,是输入数字):ķ ÿ X 1 Ĵ ķ X ÿ kkyixi1jkxiyi

i=1kxi=S1,i=1kxi2=S2,i=1kxik=Sk (1)

请记住,您可以简单地计算,因为,,...S 1 = S - y i S 2 = i 2 - y 2 iS1,...SkS1=SyiS2=i2yi2

现在要查找缺失的数字,您应该解决来找到所有。x (1)xi

您可以计算:

P 2 = Σ X X Ĵ P ķ = Π X 2 P1=xi,,...,。P2=xixjPk=xi (2)

为此,请记住 ,,...P 2 = S 2 1S 2P1=S1P2=S12S22

但是是系数,但是可以唯一地分解,因此您可以找到缺失的数字。 P = x x 1x x 2x x kPPiP=(xx1)(xx2)(xxk)P

这些不是我的想法;阅读这个


1
我不明白(2)。也许您添加了总和的详细信息?是否错过吗? ΣPk
拉斐尔

@ Raphael,是牛顿的身份,我想如果您看一下我引用的Wiki页面,就可以得出计算的主意,每个可以由先前的 s计算出来,请记住简单的公式:,您可以对所有能力应用类似的方法。同样,在我写是某些东西的总和,但是没有任何,因为只有一个。PiPiPSj2x1x2=(x1+x2)2(x12+x22)PiPkΣΠ

尽管如此,答案应该在合理范围内是独立的。您给出一些公式,那么为什么不使它们完整?
拉斐尔

11

从上面的评论:

处理流之前,分配位,在其中写X = Ñ = 1 b Ñb Ñ是的二进制表示是逐点exclusive-要么)。天真的,这需要On 时间。log2nx:=i=1nbin(i)bin(i)iO(n)

一旦处理流,每当一个读取数,计算X = X b ÑĴ 。让ķ是从单数{ 1 Ñ }不包含所述信息流中。在阅读整个流之后,我们有 X = Ñ = 1 b Ñķ bjx:=xbin(j)k{1,...n} 得到所需的结果。

x=(i=1nbin(i))(ikbin(i))=bin(k)ik(bin(i)bin(i))=bin(k),

因此,我们使用了空间,并且总体运行时间为On O(logn)O(n)


3
我可以建议一个简单的优化方法,使其成为真正的流式单遍算法:在时间步,xor x具有b i ni 且输入b i nj 已到达流中。这具有额外的好处,即使n提前未知,您也可以使其工作:只需从为x分配的单个位开始,然后根据需要“增长”分配的空间。ixb一世ñ一世bin(j)nx
Sasho Nikolov

0

HdM的解决方案有效。我用C ++进行了编码以对其进行测试。我不能将其限制value位,但是我敢肯定,您可以轻松地显示出实际上只设置了多少位。O(log2n)

对于那些想伪代码,使用一个简单的操作与异或():fold

Missing=fold(,{1,,N}InputStream)

手波浪卷发证明:甲从未需要比其输入更多的位,所以它遵循上面没有中间结果在需要超过该输入的最大比特(所以Ô 登录2 Ñ 位)。是可交换的,并且X X = 0,因此如果展开上述和一对关闭所有本你将只剩下一个未匹配的值,缺少的数量流中的数据。O(log2n)xx=0

#include <iostream>
#include <vector>
#include <cstdlib>
#include <algorithm>

using namespace std;

void find_missing( int const * stream, int len );

int main( int argc, char ** argv )
{
    if( argc < 2 )
    {
        cerr << "Syntax: " << argv[0] << " N" << endl;
        return 1;
    }
    int n = atoi( argv[1] );

    //construct sequence
    vector<int> seq;
    for( int i=1; i <= n; ++i )
        seq.push_back( i );

    //remove a number and remember it
    srand( unsigned(time(0)) );
    int remove = (rand() % n) + 1;
    seq.erase( seq.begin() + (remove - 1) );
    cout << "Removed: " << remove << endl;

    //give the stream a random order
    std::random_shuffle( seq.begin(), seq.end() );

    find_missing( &seq[0], int(seq.size()) );
}

//HdM's solution
void find_missing( int const * stream, int len )
{
    //create initial value of n sequence xor'ed (n == len+1)
    int value = 0;
    for( int i=0; i < (len+1); ++i )
        value = value ^ (i+1);

    //xor all items in stream
    for( int i=0; i < len; ++i, ++stream )
        value = value ^ *stream;

    //what's left is the missing number
    cout << "Found: " << value << endl;
}

3
请仅张贴该算法的可读(伪)代码(跳过主代码)。另外,应包括某种程度的正确性证明/论据。
拉斐尔

4
@ edA-qamort-ora-y您的答案假设读者知道C ++。对于不熟悉这种语言的人来说,没什么可看的:找到相关的段落并理解它的作用是一个挑战。可读的伪代码将使它成为一个更好的答案。C ++在计算机科学站点上并不是真正有用的。
吉尔(Gilles)'所以

3
如果我的回答被证明没有用,人们就无需投票。
edA-qa mort-ora-y 2012年

2
+1是实际花费时间编写C ++代码并对其进行测试。不幸的是,正如其他人指出的那样,事实并非如此。您仍然为此付出了努力!
朱利安·莱伯特

9
我不明白这个答案的意思:您采用了别人的解决方案,该解决方案非常简单,显然非常有效,然后对其进行“测试”。为什么需要测试?这就像测试计算机正确添加数字一样。而且您的代码也没有什么不平凡的。
Sasho Nikolov
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.