กำหนดหมายเลขที่ขาดหายไปในสตรีมข้อมูล


14

เราได้รับกระแสของn1ตัวเลขที่แตกต่างจากจำนวนจากชุด{1,,n} }

ฉันจะกำหนดจำนวนที่ขาดหายไปด้วยอัลกอริทึมที่อ่านกระแสข้อมูลหนึ่งครั้งและใช้หน่วยความจำของบิตเท่านั้น( ล็อก2 n ) ได้O(log2n)อย่างไร

คำตอบ:


7

คุณรู้จักและเพราะS=n(n+1)i=1ni=n(n+1)2สามารถเขียนโค้ดในO(log(n))บิตนี้สามารถทำได้ในหน่วยความจำO(logn)และในเส้นทางเดียว (เพียงหาS-currentsumS=n(n+1)2O(log(n))O(logn)ScurrentSumนี้จะหายไปจำนวน)

แต่ปัญหานี้สามารถแก้ไขได้ในกรณีทั่วไป (สำหรับค่าคงที่ ): เรามีตัวเลขที่หายไปkให้หาพวกเขาทั้งหมด ในกรณีนี้แทนที่จะคำนวณเพียงผลรวมของy i , คำนวณผลรวมของ j'st power ของx iสำหรับทั้งหมด1 j k (ฉันสันนิษฐานว่าx iขาดตัวเลขและy iคือตัวเลขอินพุต):kkyixi1jkxiyi

ผม=1kxผม=S1,Σผม=1kxผม2=S2,Σผม=1kxผมk=Sk (1)

โปรดจำไว้ว่าคุณสามารถคำนวณเพียงเพราะว่าS 1 = S - y i , S 2 = i 2 - y 2 i , ...S1,...SkS1=SyiS2=i2yi2

ตอนนี้สำหรับการหาตัวเลขที่หายไปคุณควรจะแก้เพื่อค้นหาทุกxฉัน(1)xi

คุณสามารถคำนวณ:

, P 2 = x iP1=xi , ... , P k = x i ( 2 )P2=xixjPk=xi (2) )

สำหรับสิ่งนี้โปรดจำไว้ว่า , P 2 = S 2 1 - S 2P1=S1P2=S12S22 , ...

แต่คือสัมประสิทธิ์ของP = ( x - x 1 ) ( x - x 2 ) ( x - x k )แต่Pสามารถแยกตัวประกอบได้อย่างไม่ซ้ำใครดังนั้นคุณสามารถหาตัวเลขที่หายไปได้PiP=(xx1)(xx2)(xxk)P

นี่ไม่ใช่ความคิดของฉัน อ่านนี้


1
ฉันไม่ได้รับ (2) บางทีถ้าคุณเพิ่มรายละเอียดจำนวนเงิน? ไม่พลาดΣ ? Pk
Raphael

@Raphael, เป็นตัวตนของนิวตันผมคิดว่าถ้าคุณดูที่หน้าวิกิพีเดียอ้างอิงของฉันคุณจะได้รับความคิดของการคำนวณแต่ละP ฉันสามารถคำนวณได้โดยก่อนหน้านี้P S, S Jจำสูตรง่ายๆ: 2 x 1x 2 = ( x 1 + x 2 ) 2 - ( x 2 1 + x 2 2 )คุณสามารถใช้วิธีการที่คล้ายกันกับพลังทั้งหมด เช่นเดียวกับฉันเขียนP ฉันPiPiPSj2x1x2=(x1+x2)2(x12+x22)Piเป็น sigma ของบางอย่าง แต่ไม่มีΣใด ๆเพราะมีเพียงอันเดียวPkΣΠ Π

เป็นไปตามที่ควรคำตอบควรอยู่ในระดับที่เหมาะสม คุณให้สูตรบางอย่างแล้วทำไมไม่ทำให้เสร็จ?
Raphael

11

จากความคิดเห็นด้านบน:

ก่อนประมวลผลสตรีมจัดสรรบิตซึ่งคุณเขียนx : = n ฉัน= 1ฉันn ( i ) ( b ฉันn ( i )คือการแทนเลขฐานสองของiและเป็นค่าเฉพาะจุด - หรือ). อย่างไร้เดียงสานี่ใช้O ( n )log2nx:=i=1nbin(i)bin(i)iO(n)เวลา

Upon processing the stream, whenever one reads a number j, compute x:=xbin(j). Let k be the single number from {1,...n} that is not included in the stream. After having read the whole stream, we have

x=(i=1nbin(i))(ikbin(i))=bin(k)ik(bin(i)bin(i))=bin(k),
yielding the desired result.

Hence, we used O(logn) space, and have an overall runtime of O(n).


3
may I suggest an easy optimization that makes this a true streaming single-pass algorithm: at time step i, xor x with bin(i) and with the input bin(j) that has arrived on the stream. this has the added benefit that you can make it work even if n is not known ahead of time: just start with a single bit allocated for x and "grow" the allocated space as necessary.
Sasho Nikolov

0

HdM's solution works. I coded it in C++ to test it. I can't limit the value to O(log2n) bits, but I'm sure you can easily show how only that number of bits is actually set.

For those that want pseudo code, using a simple fold operation with exclusive or ():

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

Hand-wavey proof: A never requires more bits than its input, so it follows that no intermediate result in the above requires more than the maximum bits of the input (so O(log2n) bits). is commutative, and xx=0, thus if you expand the above and pair off all data present in the stream you'll be left only with a single un-matched value, the missing number.

#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
Please post readable (pseudo) code of only the algorithm instead (skip main). Also, a correctness proof/argument at some level should be included.
Raphael

4
@edA-qamort-ora-y Your answer assumes that the reader knows C++. To someone who is not familiar with this language, there is nothing to see: both finding the relevant passage and understanding what it's doing are a challenge. Readable pseudocode would make this a better answer. The C++ is not really useful on a computer science site.
Gilles 'SO- stop being evil'

3
If my answer proves not to be useful people don't need to vote for it.
edA-qa mort-ora-y

2
+1 for actually taking the time to write C++ code and test it out. Unfortunately as others pointed out, it's not SO. Still you put effort into this !
Julien Lebot

9
I don't get the point of this answer: you take someone else's solution, which is very simple and obviously very efficient, and "test" it. Why is testing necessary? This is like testing your computer adds numbers correctly. And there is nothing nontrivial abt your code either.
Sasho Nikolov
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.