Anonimizar um valor int

Boa noite a todos,

Para um projeto da faculdade preciso anonimizar valores int de forma que não seja possível identificar o número original. O mesmo valor de entrada deve sempre gerar o mesmo valor de saída, minimizando ao máximo as colisões (onde duas entradas diferentes geram o mesmo valor de saída). Por exemplo, o número 321231 deveria sempre ser transformado em 27183, e o número 27183 só poderia ser gerado a partir do número 321231.

Minha primeira ideia foi utilizar o algoritmo de hashing MD5 e depois remover as letras. Por exemplo:
42423 se transforma em 127f36d53863c8ef53c6426e948bfdf6
Removendo as letras, temos: 127365386385364269486
No entanto, o problema evidente aqui é que o número resultante é muito grande para ser armazenado como um int de 32 bits.

Uma outra ideia seria tirar o hashCode() do int (com a classe wrapper Integer) e simplesmente usar esse valor. Mas eu nao sei se isso trará muitas colisoes…

Gostaria de sugestões sobre como fazer isso de maneira eficiente e evitando colisões. Não preciso do código, apenas de alguma orientação.

Agradeço desde já!

Dispersão modular:

321231 % 297048 = 27183

Se suas chaves estiverem no intervalo de 0 a 297048 vc não terá nenhuma colisão. Vc está complicando demais uma coisa simples para a sua necessidade (diminuir colisões). Como vc não explicou o motivo de precisar disso, estou pressupondo que vc está estudando algo relacionado a tabelas de dispersão. Uma implicação prática da baixa colisão pode ser uma quantidade grande de memória necessária para armazenar sua listas… Enfim, se vc explicar melhor o motivo da sua necessidade talvez dê para dar uma resposta melhor.

2 curtidas

O que eu preciso fazer é simples: anonimizar algumas colunas de uma tabela.

Por exemplo: “Produto” trem atributos “Nome” (String) e “Código” (int). Os valores de “Código” sao ints aleatórios.
Eu já consigo anonimizar o os valores de Nome com o MD5, mas o Código ainda nao. Eu vou pesquisar mais sobre esse método que vc mencionou e tentar aplicar ele.
Como eu falei eu queria algo que me desse sempre o mesmo resultado para um valor diretamente, como hashing. Nao quero ter que manter nenhuma tabela de mapeamento input-output ou um histórico de quais valores já foram utilizados (para evitar usá-los de novo).

Muito obrigada!

Vc quer criptografar/cifrar, certo? Em um dado momento, vc precisa descriptografar/decifrar o que foi armazenado, pq senão não faz muito sentido não é? Vc precisa de um algoritmo que seja simétrico, ou seja, você consegue codificar um valor e depois decodificá-lo. Até onde eu sei, MD5 não deveria ser usado para o que vc precisa, ou seja, uma via de ida e de volta, mas se o MD5 é suficiente para o seu problema, basta tratar seu código como uma String e gerar o MD5 dele.

1 curtida

desculpe se nao fui clara, eu nao posso falar os detalhes do projeto pois envolve uma empresa privada. No meu caso seria uma via “só de ida”. As tabelas serao usadas pra uma análise estatística e os valores originais nao serao relevantes. O problema é que eles sao valores privados que eu nao posso publicar, por isso preciso transforma-los.

eu usei o MD5 nas strings e isso funciona, mas como eu disse, com valores int sso gera problemas, pois o MD5 gera valores de 128 bits, grandes demais para caber num int Java. Por isso pensei que uma alternativa seria pegar os valores int, envolve-los com a Wrapper Integer (ou talvez transformar em String) e tirar o hashCode(). Isso também geraria sempre o mesmo valor para cada objeto…

Uai, se não vão servir pra nada, então “apaga” eles. Pra quê ficar carregando lixo?

Acho que entendi, vc precisa alterar esse valores para divulga-los, assim, não seria os números reais… O problema disso, que ainda sim teria a mesma proporção, mostrando os números talvez não teria um efeito claro, mas mostrando em gráficos, com toda certeza, mas isso não vem ao caso.

Quanto a sua dúvida, acredito que vc poderia criar um próprio padrão, não necessariamente precisa utilizar um tipo de criptografia, desde que aplique para todos valores, não vejo problema nisso, mas seguiria na sua ideia de utilizar o MD5, depois de remover os alfas, poderia recuperar apenas os 6 primeiros números, por exemplo. Isso garantia que sempre daria o mesmo valor.

1 curtida

Se o valor não é relevante, porque precisa dessa preocupação de um número gerar sempre o mesmo resultado? Trocar por qualquer valor aleatório não é suficiente? Caso não seja, não tem muito jeito. Ou vc faz algum cálculo, ou precisa manter algum controle para mapear os valores.

Usar o hashCode do Integer não vai adiantar, pois na implementação atual ele sempre retorna o próprio número.

O que daria pra fazer é sempre efetuar algum cálculo simples, como já sugerido acima com o operador %. Mas se os dados são confidenciais, uma conta simples é fácil de adivinhar e portanto tem grandes chances de ser reversível.


Sem mais detalhes, posso sugerir uma solução simplista e “caseira”. Primeiro vc precisa ver quantos valores únicos existem (se for um banco de dados, um simples SELECT COUNT(DISTINCT valor) FROM tabela te dá a resposta). Vamos supor que os valores sejam: 10, 20, 30, 40, 50, 10, 40 e 30. Ou seja, 8 valores, mas destes, temos apenas 5 valores únicos (10, 20, 30, 40 e 50).

Se não estão no banco de dados, dá pra saber a quantidade usando um Set:

// verificar quais são os valores únicos
List<Integer> todosValores = Arrays.asList(10, 20, 30, 40, 50, 10, 40, 30);

Set<Integer> valoresUnicos = new HashSet<>(todosValores);
int qtdValoresUnicos = valoresUnicos.size(); // 5

Se a quantidade for pequena, daria para criar um Set e ir adicionando números aleatórios, até que a quantidade seja atingida:

Set<Integer> valoresUnicos = // obtido anteriormente, ver código acima
int qtdValoresUnicos = // obtido anteriormente, ver código acima

Random rand = new Random();
Set<Integer> numeros = new HashSet<>();
while (numeros.size() < qtdValoresUnicos) {
    // gera um valor entre 1000 e 100000

    // a partir do Java 17, é assim
    numeros.add(rand.nextInt(1000, 100000));

    // antes do Java 17, é assim
    //numeros.add(rand.nextInt(99000) + 1000);
}

// cria o mapeamento
Map<Integer, Integer> map = new HashMap<>();
Iterator<Integer> it = numeros.iterator();
for (Integer value : valoresUnicos) {
    map.put(value, it.next());
}

E depois eu crio um Map que mapeia cada valor para um número aleatório único (a unicidade foi garantida pelo Set, que não permite valores duplicados). Assim, basta pegar o respectivo valor no Map, por exemplo, map.get(10) sempre retornará o mesmo número. E como este foi gerado aleatoriamente, não há como deduzir uma fórmula que permita o caminho inverso (desde que vc não guarde o Map em nenhum lugar, claro).


Um detalhe é que este método não escala se tiver muitos valores possíveis. Imagine que a quantidade de valores únicos é 98 mil, e o Set já está com 97.999 valores. Como no exemplo acima estou gerando números entre mil e 100 mil, há 99 mil possibilidades. A chance de sair algum número repetido é maior, o que quer dizer que o loop pode tentar várias vezes até sair algum que ainda não está no Set.

Neste caso, uma alternativa é criar uma lista com todos os números, embaralhar e pegar a quantidade que precisa:

Set<Integer> valoresUnicos = // obtido anteriormente, ver código acima
int qtdValoresUnicos = // obtido anteriormente, ver código acima

// gera todos os números entre 1000 e 100000
List<Integer> numeros = new ArrayList<>();
for (int i = 1000; i < 100000; i++) {
    numeros.add(i);
}
Collections.shuffle(numeros); // embaralha

// pega os primeiros (a quantidade é qtdValoresUnicos)
Iterator<Integer> it = numeros.subList(0, qtdValoresUnicos).iterator();
Map<Integer, Integer> map = new HashMap<>();
for (Integer value : valoresUnicos) {
    map.put(value, it.next());
}

E pronto, o Map gerado funciona da mesma forma.


E claro, vc pode ajustar o intervalo dos números aleatórios para a sua necessidade.

E como já disse, é uma solução bem caseira e simplista. A única garantia é que cada valor resulta em um outro valor único, e não permite volta. Claro, assumindo que o Map será descartado logo em seguida. Ainda seria possível reproduzir os mesmos dados se for possível adivinhar o seed usado pela classe Random, e aí vc poderia trocar por SecureRandom, etc. Sem saber dos requisitos exatos, dá pra ficar especulando infinitamente sobre o que é “melhor” para este problema.

Se isso é o suficiente para o seu caso, não temos como saber só com as informações passadas. De qualquer forma, caso precise de algo mais robusto, aí o buraco é mais embaixo. Técnicas de anonimização de dados vão muito além da substituição simples:

1 curtida

essa solucao serve ao meu propósito! muito obrigada!

1 curtida