Veri akışında eksik sayıyı belirleme


14

kümesinden farklı sayılardan oluşan bir akış alıyoruz .{ 1 , , n }n1{1,,n}

Akışı bir kez okuyan ve yalnızca bitlik bir bellek kullanan bir algoritmayla eksik sayıyı nasıl belirleyebilirim ?O(log2n)

Yanıtlar:


7

Biliyor ve çünkü içinde kodlanabilir bitleri belleğinde ve tek bir yolda yapılabilir (sadece bulun , bu sayı eksik). 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

Ancak bu sorun genel olarak (sabit ) çözülebilir : eksik sayılarımız var, hepsini öğrenin. Yerine hesaplama Bu durumda sadece bir özetlemek ait j'st gücünün hesapla toplamını herkes için (ı kabul sayıları eksikse ve girdi sayılardır):k y i x i 1 j k x i y ikkyixi1jkxiyi

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

hesaplayabileceğinizi basitçe, çünkü , , ...S 1 = S - y i S 2 = i 2 - y 2 iS1,...SkS1=SyiS2=i2yi2

Şimdi eksik numaraları bulmak için tüm bulmak için çözmelisiniz .x i(1)xi

Hesaplayabilirsiniz:

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

Bunun için hatırlıyorum , , ...P 2 = S 2 1 - S 2P1=S1P2=S12S22

Ancak , ancak benzersiz bir şekilde çarpanlarına ayrılabilir, böylece eksik sayıları bulabilirsiniz. P = ( x - x 1 ) ( x - x 2 ) ( x - x k ) PPiP=(xx1)(xx2)(xxk)P

Bunlar benim düşüncelerim değil; okumak bu .


1
Anlamıyorum (2). Belki meblağların ayrıntılarını eklediyseniz? özlüyor mu ? Pk
Raphael

@Raphael, size hesaplama fikir edinebilirsiniz benim başvurulan wiki sayfasından bakabilirsiniz eğer Newton'un kimlikler, bence, her önceki tespit ediliyordu s, , basit bir formül hatırlayın: , tüm yetkilere benzer bir yaklaşım uygulayabilirsiniz. Ayrıca yazdığım gibi şey sigma, ama herhangi bir , çünkü sadece bir . P i P S j 2 x 1x 2 = ( x 1 + x 2 ) 2 - ( x 2 1 + x 2 2 ) P i P k Σ ΠPiPiPSj2x1x2=(x1+x2)2(x12+x22)PiPkΣΠ

Olursa olsun, cevaplar makul derecede kendi içinde olmalıdır. Bazı formüller veriyorsunuz, neden onları tamamlamıyorsunuz?
Raphael

11

Yukarıdaki yorumdan:

Akışı işlemeden önce, ( bitlerini . ve ikili münhasırdır. Saf olarak, bu zaman alır.X : = n i = 1 b i , n ( i ) b ı , n ( i ) I O ( n )log2nx:=i=1nbin(i)bin(i)iO(n)

Akışı işledikten sonra, kişi sayısını okuduğunda hesaplayın . , akışa dahil olmayan aralığındaki tek sayı olsun . Akışın tamamını okuduktan sonra istenen sonucu verir.X : = x b i n ( j ) k, { 1 , . . . n } x = ( n i = 1 b i n ( i ) )( i k b i n ( i ) ) = b i n ( k ) i kjx:=xbin(j)k{1,...n}

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

Bu nedenle, alanı kullandık ve genel olarak çalışma süresine sahibiz .O(logn)O(n)


3
zaman adımında: Ben bu gerçek akışı tek geçişli algoritması yapar kolay bir optimizasyon önerebilir , xor ile ve girdi ile üzerine geldi Akış. bu, vaktinden önce bilinmese bile çalışmasını sağlayabileceğiniz ek bir avantaja sahiptir : sadece için ayrılan tek bir bitle başlayın ve gereken alanı "büyütün". ixbin(i)bin(j)nx
Sasho Nikolov

0

HdM'nin çözümü işe yarıyor. Test etmek için C ++ 'da kodladım. Ben sınırlayamaz valueiçin biti, ama emin kolayca bit sayısı gerçekte ayarlandığını nasıl yalnızca gösterebilir değilim.O(log2n)

Sahte kod isteyenler için , exclusive veya ( ) ile basit bir işlemi :fold

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

El-dalga geçirmez: A hiçbir zaman girişinden daha fazla bit gerektirmez, bu nedenle yukarıdaki hiçbir ara sonuç girişin maksimum bitlerinden daha fazlasını gerektirmez (yani bitleri). değişmeli ve olduğundan, yukarıdakileri genişletir ve akışta bulunan tüm verileri yalnızca tek bir eşleşmeyen değer olan eksik sayı kalır.O ( log 2 n ) x x = 0O(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
Lütfen bunun yerine yalnızca algoritmanın okunabilir (sözde) kodunu gönderin (ana bölümü atlayın). Ayrıca, bir düzeyde doğruluk kanıtı / argümanı da dahil edilmelidir.
Raphael

4
@ edA-qamort-ora-y Yanıtınız okuyucunun C ++ bildiğini varsayar. Bu dile aşina olmayan biri için, görülecek hiçbir şey yoktur: hem ilgili pasajı bulmak hem de ne yaptığını anlamak zor bir iştir. Okunabilir sahte kod bunu daha iyi bir cevap haline getirir. C ++ bir bilgisayar bilimi sitesinde gerçekten yararlı değildir.
Gilles 'SO- kötü olmayı bırak'

3
Cevabım yararlı olmadığını kanıtlarsa, insanların buna oy vermeleri gerekmez.
edA-qa mort-ora-y

2
C ++ kodunu yazmak ve test etmek için zaman ayırdığınız için +1. Ne yazık ki diğerlerinin de belirttiği gibi, SO değil. Hala buna çaba gösteriyorsun!
Julien Lebot

9
Bu cevabın amacını anlayamıyorum: Bir başkasının çözümünü alıyorsunuz, ki bu çok basit ve açıkçası çok verimli ve “test” ediyor. Test neden gereklidir? Bu, bilgisayarınızın doğru şekilde sayı eklediğini test etmek gibidir. Ve hiçbir şey önemsiz abt kodunuzu da.
Sasho Nikolov
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.