Tentukan nomor yang hilang dalam aliran data

14

Kami menerima aliran n-1 angka yang berbeda secara berpasangan dari set {1,...,n} .

Bagaimana saya bisa menentukan angka yang hilang dengan algoritma yang membaca aliran sekali dan menggunakan memori hanya HAI(catatan2n) bit?

Antre
sumber

Jawaban:

7

Anda tahu , dan karenaS=n(n+1)i=1ni=n(n+1)2 dapat dikodekan dalambitO(log(n))ini dapat dilakukan dalammemoriO(logn)dan dalam satu jalur (cukup cariS-currentSum, ini adalah nomor yang hilang).S=n(n+1)2O(log(n))O(logn)S-ckamurrentSkamum

Tetapi masalah ini dapat diselesaikan dalam kasus umum (untuk konstan ): kami memiliki nomor k yang hilang, cari tahu semuanya. Dalam hal ini, alih-alih menghitung hanya jumlah y i , hitung jumlah kekuatan pertama x i untuk semua 1 j k (saya berasumsi x i adalah angka yang hilang dan y i adalah angka input):kkysayaxsaya1jkxsayaysaya

saya=1kxsaya=S1,saya=1kxsaya2=S2,saya=1kxsayak=Sk (1)

Ingat bahwa Anda dapat menghitung sederhana, karena S 1 = S - Σ y i , S 2 = Σ i 2 - Σ y 2S1,...SkS1=S-ysaya , ...S2=saya2-ysaya2

Sekarang untuk menemukan nomor yang hilang, Anda harus memecahkan (1) untuk menemukan semua .xi

Anda dapat menghitung:

, P 2 = x ix j , ..., P k = x i ( 2 ) .P1=xiP2=xixjPk=xi (2)

Untuk ini ingat bahwa , P 2 = S 2 1 - S 2P1=S1 , ...P2=S12S22

Tetapi adalah koefisien dari PPiP=(xx1)(xx2)(xxk) tetapi dapat diperhitungkan secara unik, sehingga Anda dapat menemukan angka yang hilang.P

Ini bukan pikiran saya; Baca ini .

Raphael
sumber
1
Saya tidak mendapatkan (2). Mungkin jika Anda menambahkan rincian penjumlahan? Apakah kehilangan Σ ? Pk
Raphael
@Raphael, adalah identitas Newton, saya pikir jika Anda melihat halaman wiki yang direferensikan Anda bisa mendapatkan ide perhitungan, setiap P i dapat dihitung dengan P s, S j sebelumnya , ingat rumus sederhana: 2 x 1x 2 = ( x 1 + x 2 ) 2 - ( x 2 1 + x 2 2 ) , Anda dapat menerapkan pendekatan yang sama untuk semua kekuatan. Juga ketika saya menulis adalah sigma dari sesuatu, tetapi PPsayaPsayaPSj2x1x2=(x1+x2)2(x12+x22)Psaya tidak memiliki Σ , karena hanya ada satu Π . PkΣΠ
Bagaimanapun, jawaban harus mandiri sampai tingkat yang wajar. Anda memberikan beberapa formula, jadi mengapa tidak membuatnya lengkap?
Raphael
11

Dari komentar di atas:

Sebelum memproses aliran, alokasikan bit, di mana Anda menulis x : = n i = 1 b i n ( i ) ( b i n ( icatatan2nx: =saya=1nbsayan(saya) adalah representasi biner dari i dan adalah pointwise eksklusif- atau). Secara naif, ini membutuhkan waktu O ( n ) .bsayan(saya)sayaHAI(n)

Setelah memproses streaming, setiap kali seseorang membaca angka , hitung x : = x b i n ( j ) . Biarkan k menjadi nomor tunggal dari { 1 , . . . n } yang tidak termasuk dalam aliran. Setelah membaca seluruh aliran, kita memiliki x = ( n i = 1 b i n ( i ) )( i k bjx: =xbsayan(j)k{1,...n}

x=(saya=1nbsayan(saya))(sayakbsayan(saya))=bsayan(k)sayak(bsayan(saya)bsayan(saya))=bsayan(k),

HAI(catatann)HAI(n)

HdM
sumber
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

Solusi HdM bekerja. Saya mengkodekannya dalam C ++ untuk mengujinya. Saya tidak bisa membatasi valueuntukHAI(catatan2n) bit, tapi saya yakin Anda dapat dengan mudah menunjukkan bagaimana hanya jumlah bit yang benar-benar ditetapkan.

Bagi yang menginginkan kode pseudo, gunakan yang sederhana melipat operasi dengan eksklusif atau ():

Tidak ada=melipat(,{1,...,N}InputStream)

Bukti bergelombang: A tidak pernah memerlukan lebih banyak bit dari inputnya, jadi itu berarti bahwa tidak ada hasil antara di atas membutuhkan lebih dari bit maksimum input (jadi HAI(catatan2n) bit). bersifat komutatif, dan xx=0, jadi jika Anda memperluas yang di atas dan memasangkan semua data yang ada dalam aliran Anda akan dibiarkan hanya dengan satu nilai yang tidak cocok, angka yang hilang.

#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;
}
edA-qa mort-ora-y
sumber
3
Harap posting kode yang hanya dapat dibaca (semu) dari algoritma saja (lewati utama). Juga, bukti / argumen kebenaran pada tingkat tertentu harus dimasukkan.
Raphael
4
@ edA-qamort-ora-y Jawaban Anda mengasumsikan bahwa pembaca tahu C ++. Bagi seseorang yang tidak terbiasa dengan bahasa ini, tidak ada yang bisa dilihat: menemukan bagian yang relevan dan memahami apa yang dilakukannya adalah tantangan. Kode semu yang dapat dibaca akan membuat ini menjadi jawaban yang lebih baik. C ++ tidak benar-benar berguna di situs ilmu komputer.
Gilles 'SANGAT berhenti menjadi jahat'
3
Jika jawaban saya terbukti tidak bermanfaat, orang tidak perlu memilihnya.
edA-qa mort-ora-y
2
+1 untuk benar-benar meluangkan waktu untuk menulis kode C ++ dan mengujinya. Sayangnya seperti yang ditunjukkan orang lain, itu bukan SO. Tetap Anda berusaha untuk ini!
Julien Lebot
9
Saya tidak mengerti maksud dari jawaban ini: Anda mengambil solusi orang lain, yang sangat sederhana dan jelas sangat efisien, dan "mengujinya". Mengapa pengujian diperlukan? Ini seperti menguji komputer Anda menambahkan angka dengan benar. Dan tidak ada yang abtrivial abt kode Anda baik.
Sasho Nikolov