Diferença do conjunto de computação entre dois conjuntos grandes


14

Eu tenho dois grandes conjuntos de números inteiros e . Cada conjunto possui cerca de um milhão de entradas e cada entrada é um número inteiro positivo com no máximo 10 dígitos. BAB

Qual é o melhor algoritmo para calcular e ? Em outras palavras, como posso calcular com eficiência a lista de entradas de que não estão em e vice-versa? Qual seria a melhor estrutura de dados para representar esses dois conjuntos e tornar essas operações eficientes?B A A BABBAAB

A melhor abordagem que posso encontrar é armazenar esses dois conjuntos como listas classificadas e comparar todos os elementos de com todos os elementos de , de maneira linear. Podemos fazer melhor?BAB


Se você deseja armazená-lo de maneira diferente, poderá obter melhores resultados.
Realz Slaw

Além disso, se você deseja obter os resultados como uma estrutura de dados implícita; você pode criar uma estrutura que consulte os dois conjuntos para responder a cada uma de suas próprias consultas.
Realz Slaw

1
@ user917279 Um grande ponto é: você geralmente pode trocar o tempo de pré-processamento / construção, tempo de consulta e uso de memória entre si. Você edita a estrutura raramente, mas consulta muito? O contrário? A memória é uma preocupação ou não? Tais perguntas podem ser respondidas de um ponto de vista prático e informam a escolha do construto "correto" "teórico".
Raphael

1
@ Rafael Você sugere que se possa fazer melhor do que os conjuntos persistentemente confluentes (em termos de complexidade) usando mais memória e / ou gastando mais tempo na preparação. Só estou curioso se você acha que é possível. Não vejo tabelas de pesquisa como uma opção para conjuntos de entrada desse tamanho.
smossen

1
@ user917279 Se você considerar o exemplo de dois conjuntos enormes idênticos, qualquer estrutura de dados criada usando hash-consing suportará teste de igualdade em O (1), pois estruturas iguais serão mescladas quando criadas e, portanto, compartilharão o mesmo local de memória. Os conjuntos confluentemente persistentes também aproveitam o hash-consing quando duas estruturas são quase iguais. A complexidade é a melhor que já vi até agora para conjuntos encomendados.
smossen

Respostas:


9

Se você estiver disposto a armazenar os conjuntos em uma estrutura de dados especializada, poderá obter algumas complexidades interessantes.

I=O(min(|A|,|B|,|AΔB|))

Em seguida, você pode definir as operações e , cada uma em tempo esperado. Então, basicamente, você obtém o tamanho mínimo dos dois conjuntos ou o tamanho da diferença simétrica, o que for menor. Isso é melhor que linear, se a diferença simétrica for pequena; ie se eles tiverem um grande cruzamento. De fato, para as duas operações de diferença de conjunto que você deseja, isso é praticamente sensível à saída, pois juntas elas compõem o tamanho da diferença simétrica.Um Δ B O ( I log | A | + | B |AB,AB,ABAΔBO(Ilog|A|+|B|I)

Consulte Conjuntos e mapas confluentemente persistentes de Olle Liljenzin (2013) para obter mais informações.


As trufas no jornal são ordenadas por árvores de busca. Eu não os contaria como estruturas de dados não classificadas.
smossen

@smossen É verdade, eu editei isso.
Realz Slaw

6

Uma varredura linear é a melhor que eu sei fazer, se os conjuntos forem representados como listas vinculadas classificadas. O tempo de execução é .O(|A|+|B|)

Observe que você não precisa comparar todos os elementos de com todos os elementos de B , em pares. Isso levaria a um tempo de execução de O ( | A | × | B | ) , que é muito pior. Em vez disso, para calcular a diferença simétrica desses dois conjuntos, você pode usar uma técnica semelhante à operação "mesclar" no mergesort, modificada adequadamente para omitir valores comuns a ambos os conjuntos.ABO(|A|×|B|)

Mais detalhadamente, você pode criar um algoritmo recursivo como o seguinte para calcular , assumindo que A e B sejam representados como listas vinculadas com seus valores em ordem classificada:ABAB

difference(A, B):
    if len(B)=0:
        return A # return the leftover list
    if len(A)=0:
        return B # return the leftover list
    if A[0] < B[0]:
        return [A[0]] + difference(A[1:], B)
    elsif A[0] = B[0]:
        return difference(A[1:], B[1:])  # omit the common element
    else:
        return [B[0]] + difference(A, B[1:])

Eu representei isso em pseudo-Python. Se você não lê Python, A[0]é o chefe da lista vinculada A, A[1:]é o restante da lista e +representa a concatenação de listas. Por motivos de eficiência, se você estiver trabalhando em Python, provavelmente não deseja implementá-lo exatamente como acima - por exemplo, talvez seja melhor usar geradores, para evitar a criação de muitas listas temporárias - mas eu queria mostre as idéias da forma mais simples possível. O objetivo deste pseudocódigo é apenas ilustrar o algoritmo, não propor uma implementação concreta.

Eu não acho que seja possível melhorar, se seus conjuntos forem representados como listas classificadas e você desejar que a saída seja fornecida como uma lista classificada. Você fundamentalmente tem que olhar para cada elemento de e B . Esboço informal da justificativa: se houver algum elemento que você não tenha examinado, não poderá produzi-lo; portanto, o único caso em que você pode omitir a observação de um elemento é se souber que ele está presente em A e B , mas como você poderia saber que ele está presente se não analisou seu valor?ABAB


fantástico, temos outras opções se a restrição de que os conjuntos sejam armazenados como listas classificadas for removida?
user917279

2

Se A e B são de tamanho igual, disjuntos e intercalados (por exemplo, números ímpares em A e números pares em B), a comparação entre pares de itens em tempo linear provavelmente é ideal.

Se A e B contiverem blocos de itens que estão exatamente em um de A ou B, ou em ambos, é possível calcular a diferença de conjunto, união e interseção em tempo sub linear. Por exemplo, se A e B diferem em exatamente um item, a diferença pode ser calculada em O (log n).

http://arxiv.org/abs/1301.3388


1
Ele diz que os conjuntos são ordenados, o que pode significar que eles são armazenados como listas, árvores de pesquisa ou qualquer outra coisa. Se os dados precisam ser armazenados como listas, é bastante desinteressante pedir "o melhor algoritmo para calcular AB" quando nenhum algoritmo poderia fazer melhor do que varrer as listas em tempo linear (para o qual ele já encontrou um algoritmo).
smossen

1
Deus, você ligava o mesmo papel que I (I, mesmo que você, em vez) ... nomear seus links na próxima vez: D
Realz Slaw

@smossen fantastic, para qualquer conhecimento (?) que possuo, eu os representei como listas classificadas, mas humildemente gostaria de receber outras sugestões também.
user917279

2

nABumab¯uma,b


1010

1
R., erra o ponto. um único longpode armazenar 32 elementos ou 1 byte, 8 elementos. portanto, 1 milhão de entradas podem ser armazenadas em apenas ~ 125K RAM! o armazenamento pode ser significativamente mais eficiente do que outras representações dependendo de como o problema é implementado ...
vzn

Portanto, você precisará de mais de 12MB para os conjuntos nos quais o OP estiver interessado. Isso sopra todos os caches (atualmente) e será horrível para os conjuntos esparsos. Em particular, a criação de um conjunto vazio domina todas as outras operações (para conjuntos esparsos). Knuth aborda esse problema no TAoCP, a propósito.
Raphael

12MB? Hã? cartaz disse que ele só tem 2 conjuntos. o pôster não especificou a escassez / densidade de seu set. isso é apontado na minha resposta. você está assumindo que ele tem conjuntos esparsos? não há uma resposta correta, a abordagem é apontada como uma opção alternativa que pode ser útil dependendo das circunstâncias. não é incomum usado neste contexto ...
vzn

10101061010b1,15GB
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.