Existe um filtro anti-Bloom?

25

Um filtro Bloom permite controlar com eficiência se vários valores já foram encontrados durante o processamento. Quando existem muitos itens de dados, um filtro Bloom pode resultar em uma economia significativa de memória em uma tabela de hash. O principal recurso de um filtro Bloom, que ele compartilha com uma tabela de hash, é que ele sempre diz "não novo" se um item não é novo, mas há uma probabilidade diferente de zero de que um item seja sinalizado como "não novo" "mesmo quando é novo.

Existe um "filtro anti-Bloom", que tem o comportamento oposto?

Em outras palavras: existe uma estrutura de dados eficiente que diz "novo" se um item é novo, mas que também pode dizer "novo" para alguns itens que não são novos?

Manter todos os itens vistos anteriormente (por exemplo, em uma lista vinculada classificada) satisfaz o primeiro requisito, mas pode consumir muita memória. Espero que também seja desnecessário, dado o segundo requisito relaxado.


Para aqueles que preferem um tratamento mais formal, escreva se o filtro Bloom considerar que é novo, caso contrário, e escreva se realmente for novo caso contrário.b(x)=1 1xb(x)=0 0n(x)=1 1xn(x)=0 0

Então ; ; ; , para alguns .Pr[b(x)=0 0|n(x)=0 0]=1 1Pr[b(x)=0 0|n(x)=1 1]=αPr[b(x)=1 1|n(x)=0 0]=0 0Pr[b(x)=1 1|n(x)=1 1]=1 1-α0 0<α<1 1

Estou perguntando: existe uma estrutura de dados eficiente, implementando uma função b com algum 0<β<1 , de modo que Pr[b(x)=0|n(x)=0]=β ; Pr[b(x)=0|n(x)=1]=0 ; Pr[b(x)=1|n(x)=0]=1β ; Pr[b(x)=1|n(x)=1]=1 ?


Editar: parece que essa pergunta já foi feita no StackExchange, pois /programming/635728 e /cstheory/6596 com várias respostas de "não podem ser done "through" pode ser feito, com algum custo "to" é trivial, revertendo os valores de b ". Ainda não está claro para mim qual é a resposta "certa". O que está claro é que um esquema de cache de LRU de algum tipo (como o sugerido por Ilmari Karonen) funciona bastante bem, é fácil de implementar e resultou em uma redução de 50% no tempo necessário para executar meu código.

András Salamon
fonte
Por alguma razão, sou tentado a dizer que isso é muito semelhante ao problema que os caches e os algoritmos de posicionamento de cache tentam resolver. Considere um cache usando a substituição menos usada com frequência (LFU). Um algoritmo de substituição teoricamente ideal, mas impossível, seria despejar o que você não verá novamente por mais tempo, o mesmo que para caches. Suponho que o cache se baseie em algumas suposições sobre a natureza da distribuição que geralmente não são válidas, mas vale a pena considerar se isso se aplica.
precisa saber é o seguinte
Você pode estar interessado na seguinte conversa: Filtros de associação de conjuntos baseados em satisfação
Kaveh
@ Kaveh: obrigado pelo ponteiro, vai assistir.
András Salamon

Respostas:

12

Seguindo a idéia de hash de Patrick87, aqui está uma construção prática que quase atende aos seus requisitos - a probabilidade de confundir falsamente um novo valor com um antigo não é zero, mas pode ser facilmente reduzida de maneira insignificante.

Escolha os parâmetros e ; valores práticos podem ser, digamos, e . Seja uma função hash criptográfica segura, produzindo (pelo menos) bits de saída.k n = 128 k = 16 H n + knkn=128k=16Hn+k

Deixe ser uma matriz de -BIT bitstrings. Essa matriz armazena o estado do filtro, usando um total de bits. (Não importa particularmente como essa matriz é inicializada; podemos apenas preenchê-la com zeros ou com bits aleatórios.)2 k n n 2 ka2k nn2k

  • Para adicionar um novo valor ao filtro, calcule , em que indica os primeiros bits e indica os bits a seguir de . Seja .xiij=H(x)ij n H ( x ) a i = jkjnH(x)ai=j

  • Para testar se um valor foi adicionado ao filtro, calcule , como acima, e verifique se . Se sim, retorne verdadeiro; caso contrário, retorne false.i xa i = j ij=H(x)ai=j

Reivindicação 1: A probabilidade de um falso positivo (= novo valor que se afirma falsamente ter sido visto) é . Isso pode ser arbitrariamente pequeno, a um custo modesto no espaço de armazenamento, aumentando ; em particular, para , essa probabilidade é essencialmente insignificante, sendo, na prática, muito menor que a probabilidade de um falso positivo devido a um mau funcionamento do hardware. n n 1281/2n+knn128

Em particular, depois que valores distintos foram verificados e adicionados ao filtro, a probabilidade de ocorrer pelo menos um falso positivo é . Por exemplo, com e , o número de valores distintos necessários para obter um falso positivo com 50% de probabilidade é de cerca de .( N 2 - N ) / 2 n + k + 1 n = 128N(N2N)/2n+k+1n=1282 ( n + k ) / 2 = 2 72k=162(n+k)/2=272

Reivindicação 2: A probabilidade de um falso negativo (= valor adicionado anteriormente que se afirma falsamente ser novo) não é maior que , em que é o número de valores distintos adicionados ao filtro (ou, mais especificamente, o número de valores distintos adicionados após o valor específico que está sendo testado ter sido adicionado mais recentemente ao filtro). N1(12k)N1exp(N/2k)<N/2kN


Ps. Para colocar "insignificantemente pequeno" em perspectiva, a criptografia de 128 bits geralmente é considerada inquebrável com a tecnologia atualmente conhecida. Obter um falso positivo desse esquema com é tão provável quanto alguém adivinhar corretamente sua chave secreta de criptografia de 128 bits na primeira tentativa . (Com e , que é, na verdade, cerca de 65.000 vezes menos provável do que isso.)n+k=128n=128k=16

Mas se isso ainda o deixa irracionalmente nervoso, você sempre pode mudar para ; isso dobrará seus requisitos de armazenamento, mas posso apostar com segurança qualquer quantia que você gostaria de citar que ninguém nunca verá um falso positivo com - assumindo que a função hash não esteja quebrada.n=256n=256

Ilmari Karonen
fonte
11
Não apenas a probabilidade pode ser comparável à do mau funcionamento do hardware; também pode ser comparável à probabilidade de alguém adivinhar sua chave RSA para login SSH na primeira tentativa . O IMO transmite a praticidade da sua solução mais do que a anterior.
R ..
+1 Muito bom - meu entendimento é que isso resolve o problema de eficiência de espaço, permitindo algumas (muito pequenas) chances de responder incorretamente "não novo" quando o item é, de fato, novo. Análise muito prática e boa.
precisa saber é o seguinte
11
A reivindicação 1 está apenas afirmando que uma função hash decente tem uma baixa probabilidade de colisões. Isso já é verdade na prática se for pelo menos 50 ou mais. Para meu aplicativo, e funciona muito bem com uma função hash simples de 64 bits, não criptograficamente segura, mas rápida. n+kn=44k=20
András Salamon
@ AndrásSalamon: É verdade, embora uma função hash criptográfica segura realmente ofereça uma garantia um pouco mais forte: a saber, que é impraticável encontrar entradas em colisão, mesmo se você tentar procurá-las deliberadamente . Com um suficientemente grande (por exemplo, como sugeri acima), isso significa que armazenar os dados completos é desnecessário, mesmo que o custo de um falso positivo seja alto e mesmo que haja um adversário ativo tentando encontrá-lo. Obviamente, se você não precisa de uma garantia tão forte, um risco de colisão um pouco maior pode ser aceitável. nn=128
Ilmari Karonen
11
@Newtopian A razão pela qual especifiquei uma função de hash criptográfico é que, para eles, não há maneira conhecida de gerar colisões com mais eficiência do que por força bruta (ou seja, testando muitas entradas e selecionando aquelas que colidem), ou então o hash seria considerado quebrado (como, por exemplo, o MD5 atualmente é). Assim, para um hash criptográfico, podemos assumir com segurança que a taxa de colisão é a mesma que para uma função hash aleatória ideal. O uso de uma função hash universal ou de um MAC com chave (com uma chave secreta aleatória) tornaria essa garantia ainda mais forte.
Ilmari Karonen
8

Não, não é possível ter uma estrutura de dados eficiente com essas propriedades, se você quiser ter uma garantia de que a estrutura de dados dirá "novo" se for realmente novo (nunca, nunca dirá "não novo" se é de fato novo; não são permitidos falsos negativos). Qualquer estrutura de dados desse tipo precisará manter todos os dados para responder "não novos". Veja a resposta de pents90 em cstheory para uma justificativa precisa.

Por outro lado, os filtros Bloom podem garantir que a estrutura de dados diga "não nova" se não for nova, de maneira eficiente. Em particular, os filtros Bloom podem ser mais eficientes do que armazenar todos os dados: cada item individual pode ser bastante longo, mas o tamanho do filtro Bloom é escalonado com o número de itens, e não com o comprimento total. Qualquer estrutura de dados para o seu problema precisará ser dimensionada com o comprimento total dos dados, não com o número de itens de dados.

jbapple
fonte
Veja também a resposta aceita, já que a pergunta é a mesma #
Joe
-1 Você provavelmente deve qualificar o que quer dizer quando diz que não é possível. Claramente, é possível fazê-lo de forma eficiente, e também é possível fazê-lo com uma baixa taxa de erro; portanto, é possível obter algum equilíbrio em uma determinada implementação ... em particular, seria útil explicar exatamente o que se entende por "todos os dados de todos os tempos", pois isso não é estritamente necessário para atender à pergunta da pergunta. Negativos falsos - respondendo "novo" quando a resposta deve ser "não nova" - são permitidos aqui; portanto, nem todos os dados precisam ser mantidos.
precisa saber é o seguinte
11
Essa resposta é perfeitamente razoável e parece abordar a carta da minha pergunta, mas talvez não o espírito.
András Salamon
@DW Obrigado por reservar um tempo para atualizar a resposta. Estou inclinado a deixar isso como resposta agora, embora ainda me oponha à linguagem usada ao descrever a ineficiência dos filtros antiflores, além de pensar que seria melhor elaborar um pouco mais sobre os "detalhes" mencionados. .. deixando o -1 por enquanto. Limpou alguns comentários obsoletos.
Patrick87
@DW Por "falso negativo", pretendo responder "novo" quando a resposta deveria ter sido "não nova". (Um pouco contra-intuitivamente, "não é novo" é o caso positivo aqui.) Você não precisa salvar "todos os dados de todos os tempos" para obter isso, embora eu esteja inclinado a acreditar que você precisa salvar elementos inteiros (apenas nem todos os elementos -. a menos que você está disposto a aceitar a chance hipoteticamente significativa de erro, de acordo com a outra resposta para a pergunta aqui)
Patrick87
6

Que tal apenas uma tabela de hash? Quando você vir um novo item, verifique a tabela de hash. Se o local do item estiver vazio, retorne "novo" e adicione o item. Caso contrário, verifique se o local do item está ocupado pelo item. Nesse caso, retorne "não novo". Se o local estiver ocupado por algum outro item, retorne "novo" e substitua o local pelo novo item.

Você definitivamente sempre será "Novo" corretamente se nunca viu o hash do item antes. Definitivamente, você sempre obterá "Not New" corretamente se você só viu o hash do item quando viu o mesmo item. A única vez em que você obterá "Novo" quando a resposta correta for "Não é novo" é se vir o item A, depois ver o item B, depois ver o item A novamente e o hash A e B na mesma coisa. Importante, você nunca pode obter "Not New" incorretamente.

Patrick87
fonte
11
Suponho que isso ignore o problema de eficiência de espaço, ou melhor, seja significativamente menos eficiente do que seria um filtro de bloom, já que um filtro de bloom realmente precisa apenas de um pouco por bucket e isso requer tanto espaço por bucket quanto espaço para representa os itens. Oh, bem ... a menos que o universo seja finito (como na resposta da Wandering Logic), acho que você provavelmente não pode se aproximar muito da eficiência espacial de um filtro de bloom.
precisa saber é o seguinte
Pessoalmente, acho que sua resposta é muito melhor que a minha. Um filtro de bloom não é apenas um pouco por intervalo, se você deseja probabilidades superiores a 50%. Também é um tamanho fixo e, quando você o preenche mais da metade, a probabilidade de falsos positivos aumenta vertiginosamente. Não há uma maneira conveniente de expandi-lo, nenhuma maneira conveniente de usá-lo como cache e nenhuma maneira conveniente de excluir elementos. Vou pegar uma tabela de hash toda vez.
Wandering Logic
@WanderingLogic O uso de um pequeno contador de saturação em vez de um único bit permite que a exclusão seja suportada (ao custo da capacidade e somente se o contador não estiver no máximo, obviamente).
Paul A. Clayton
4

No caso em que o universo de itens é finito, então sim: basta usar um filtro de bloom que registra quais elementos estão fora do conjunto e não no conjunto. (Ou seja, use um filtro de bloom que represente o complemento do conjunto de interesse.)

Um local em que isso é útil é permitir uma forma limitada de exclusão. Você mantém dois filtros de floração. Eles começam vazios. À medida que você insere elementos, insere-os no filtro de bloom A. Se você desejar excluir um elemento posteriormente, insira esse elemento no filtro de bloom B. Não há como cancelar a exclusão. Para fazer uma pesquisa, primeiro procure no filtro de bloom A. Se você não encontrar correspondência, o item nunca foi inserido (com probabilidade 1). Se você encontrar uma correspondência, o elemento pode (ou não) ter sido inserido. Nesse caso, você faz uma pesquisa no filtro de bloom B. Se não encontrar correspondência, o item nunca foi excluído. Se você encontrar uma correspondência no filtro B de bloom, provavelmente o item foi inserido e excluído.

Isso realmente não responde à sua pergunta, mas, neste caso limitado, o filtro de bloom B está executando exatamente o comportamento do "filtro anti-bloom" que você está procurando.

Os pesquisadores de filtro do Real Bloom usam maneiras muito mais eficientes de representar a exclusão, consulte a página da publicação de Mike Mitzenmacher .

Lógica Errante
fonte
Nesta pergunta, estamos processando itens e não há exclusões. Não há nenhuma maneira significativa para armazenar o elogio sem ter que remover itens do filtro bloom
Joe
11
@ Joe: Eu concordo que o problema é insolúvel em geral, então restringi minha resposta ao caso em que o complemento era finito e pequeno.
Wandering Logic
1

vi

Um exemplo pode ser o endereço IP e você deseja saber sempre que aparecer um que nunca viu antes. Mas ainda é um conjunto finito, para que você saiba o que pode esperar.

A solução real é simples:

  1. Adicione todos os seus itens ao filtro de contagem florida.
  2. 1
  3. Depois de ver um novo item real, subtraia-o do filtro.

Portanto, você pode ter valores de "falsos positivos" que eram antigos, mas reconhecidos como novos. No entanto, você nunca terá 'não novo' para um novo valor, pois o valor ainda estará em todos os slots, e ninguém mais poderia ter tirado isso.

Thomas Ahle
fonte