Определить пропущенный номер в потоке данных


14

Мы получаем поток из n1 попарно различных чисел из множества {1,,n} .

Как я могу определить пропущенное число с помощью алгоритма, который читает поток один раз и использует память только O(log2n) бит?

Ответы:


7

Вы знаете , и потому чтоS=n(n+1)i=1ni=n(n+1)2 может быть закодирована вO(журнал(п))биты это может быть сделано вO(журналN)памяти и в одном пути (просто найтиS-сутгентСум, это отсутствует число).S=n(n+1)2О(журнал(N))О(журналN)ScurrentSum

Но эту проблему можно решить в общем случае (для константы ): у нас есть k пропущенных чисел, выяснить их все. В этом случае вместо того, чтобы вычислять просто сумму y i , вычислите сумму j-й степени x i для всех 1 j k (я предположил, что x i - это пропущенные числа, а y i - входные числа):ККYяИкся1jkxiyя

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

Помните , что вы можете вычислить просто, потому что S 1 = S - Е у я , S 2 = Σ я 2 - Σ у 2 я , ...S1,...SkS1=SyiS2=i2yi2

Теперь, чтобы найти пропущенные числа, вы должны решить чтобы найти все x i .(1)xi

Вы можете вычислить:

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

Для этого помните, что , P 2 = S 2 1 - S 2P1=S1 , ...P2=S12S22

Но - это коэффициенты P = ( x - x 1 ) ( x - x 2 ) ( x - x k ), но P можно учесть однозначно, поэтому вы можете найти пропущенные числа.PiP=(xx1)(xx2)(xxk)P

Это не мои мысли; прочитайте это .


1
Я не понимаю (2). Может быть, если вы добавили в детали сумм? Имеет ли пропустите Е ? Pk
Рафаэль

@Raphael, это тождество Ньютона, я думаю , если вы посмотрите на моей ссылочную вики - странице , вы можете получить представление расчета, каждый P я мог бы быть рассчитан на предыдущом P s, S J , помните простую формулу: 2 х 1х 2 = ( х 1 + х 2 ) 2 - ( х 2 1 + х 2 2 ) , вы можете применить подобный подход ко всем степеням. Также как я написал P яPiPiPSj2x1x2=(x1+x2)2(x12+x22)Piявляется сигмой чего-то, но не имеет Σ , потому что есть только один Π . PkΣΠ

Как бы то ни было, ответы должны быть в достаточной степени автономными. Вы даете некоторые формулы, так почему бы не сделать их полными?
Рафаэль

11

Из комментария выше:

Перед обработкой потока, выделение бит, в котором вы пишете х : = п я = 1 б я п ( я ) ( б я п ( я ) является двоичным представлением I и точечен исключающим или). Наивно, это занимает O ( N ) время.log2nx:=i=1nbin(i)bin(i)iО(N)

После обработки потока, всякий раз , когда один считывает число , вычислить х : = х б я п ( J ) . Пусть к будет одно число из { 1 , . , , n } это не входит в поток. Прочитав весь поток, мы имеем x = ( n i = 1 b i n ( i ) )( i k bJИксзнак равноИксбяN(J)К{1,,,,N} получением желаемого результата.

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

Следовательно, мы использовали пространство и имели общее время выполнения O ( n ) .O(logn)O(n)


3
яИксбяN(я)бяN(J)NИкс

0

valueО(журнал2N) биты, но я уверен, что вы можете легко показать, как на самом деле устанавливается только это количество бит.

Для тех, кто хочет псевдокод, используя простой складка операция с эксклюзивом или ():

Отсутствуетзнак равноскладка(,{1,...,N}InputStream)

Волнообразное доказательство: A никогда не требует большего количества бит, чем его вход, поэтому из вышесказанного не требуется, чтобы промежуточный результат, описанный выше, требовал больше, чем максимальные биты ввода (поэтому О(журнал2N) биты). коммутативен, и ИксИксзнак равно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 ++ не очень полезен на сайте информатики.
Жиль "ТАК ... перестать быть злым"

3
Если мой ответ окажется бесполезным, людям не нужно голосовать за него.
edA-qa mort-ora-y

2
+1 за то, что вы нашли время написать код на C ++ и проверить его. К сожалению, как отметили другие, это не так. Тем не менее вы приложите усилия в этом!
Жюльен Лебот

9
Я не понимаю смысл этого ответа: вы берете чужое решение, которое очень просто и очевидно очень эффективно, и «тестируете» его. Зачем нужно тестирование? Это похоже на то, как если бы тестирование компьютера правильно добавляло числа. И в вашем коде нет ничего нетривиального.
Сашо Николов
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.