Unix junção comando complexidade

6

Eu queria saber se alguém saberia a complexidade do comando Unix join ? Eu tinha assumido que poderia ser linear, já que ambos os arquivos precisam ser classificados.

Alguém insistiu para mim que era logarítmico que eu duvido. Ou talvez dependa dos arquivos e pode ser logarítmico (ou N*log(N) ) quando um dos arquivos é pequeno e se aproxima linear quando ambos são grandes?

    
por Ben 02.05.2018 / 19:52

1 resposta

7

A implementação do BSD join é bastante simples de seguir e parece ser linear em relação ao número de linhas nos arquivos. Isso praticamente não mudou em todos os sistemas BSD desde pelo menos o BSD 4.4 Lite2. O trecho abaixo vem do sistema atual do OpenBSD , para comparação, é um link para o código BSD 4.4 Lite2 originalmente enviado por Keith Bostic em 1991 (substituindo uma versão anterior do utilitário):

/*
 * We try to let the files have the same field value, advancing
 * whoever falls behind and always advancing the file(s) we output
 * from.
*/
while (F1->setcnt && F2->setcnt) {
        cval = cmp(F1->set, F1->joinf, F2->set, F2->joinf);
        if (cval == 0) {
                /* Oh joy, oh rapture, oh beauty divine! */
                if (joinout)
                        joinlines(F1, F2);
                slurp(F1);
                slurp(F2);
        }
        else {
                if (F1->unpair
                && (cval < 0 || F2->set->cfieldc == F2->setusedc -1)) {
                        joinlines(F1, NULL);
                        slurp(F1);
                }
                else if (cval < 0)
                        /* File 1 takes the lead... */
                        slurp(F1);
                if (F2->unpair
                && (cval > 0 || F1->set->cfieldc == F1->setusedc -1)) {
                        joinlines(F2, NULL);
                        slurp(F2);
                }
                else if (cval > 0)
                        /* File 2 takes the lead... */
                        slurp(F2);
        }
}

Eu olhei para o código para join em GNU coreutils , mas o código GNU tem tanta coisa acontecendo que eu realmente só posso adivinhar , baseado nos comentários no código, que ele mais ou menos também implementa o mesmo tipo de algoritmo:

/* Keep reading lines from file1 as long as they continue to
   match the current line from file2.  */

[...]

/* Keep reading lines from file2 as long as they continue to
   match the current line from file1.  */

[...]

Se você considerar a classificação e assumir um algoritmo de classificação N*log(N) , a complexidade completa do tempo será N*(1 + log(N)) , ou N*log(N) para grandes valores N . Ou seja, a operação JOIN é mais rápida que a classificação.

Você não pode fazer melhor do que linear para uma operação JOIN, porque você não pode pular linhas (a menos que você tenha um índice pré-calculado de alguma descrição e não inclua a indexação na complexidade do tempo). O melhor cenário possível é que nenhuma das linhas se junte, nesse caso você precisa ler todas as linhas de um dos dois arquivos e compará-las com a primeira linha do outro arquivo. A pior das hipóteses é que todas as linhas se juntem, caso em que você precisa ler os dois arquivos e fazer comparações de pares entre os dois conjuntos de linhas (uma operação linear em arquivos classificados). Se o usuário solicitar ver linhas não pareadas, você será forçado a ler os dois arquivos completamente.

Se você conseguir fazer algo pior do que linear apenas para o JOIN, então está fazendo algo errado.

    
por 02.05.2018 / 20:29