Executando verificação de segurança...
6

Antes de pular direto para bibliotecas prontas, talvez seja interessante estudar e entender um pouco mais sobre os fundamentos.

A Abordagem Clássica: Feistel Cipher

O Feistel Cipher é uma técnica fundamental usada em muitas funções de hash populares, como AES e Blowfish. Ele é um método que permite criar uma função pseudoaleatória que é invertível, o que significa que cada entrada única produzirá uma saída única e vice-versa. Embora a implementação completa de algoritmos como DES seja complexa, uma versão simplificada do Feistel Cipher pode ser usada para gerar identificadores únicos de forma eficiente.

É importante entender que os bits que compõem um número podem ser "convertidos" ou "casted" para qualquer base ou formato. Para obter um identificador alfanumérico de 6 caracteres, podemos simplesmente realizar uma mudança de base. Por exemplo, podemos converter o número gerado pelo Feistel Cipher para Base62, que utiliza os caracteres 0-9, a-z, e A-Z. Em teoria, a Base62 é ideal para esse tipo de aplicação porque é uma representação de qualquer número como uma string alfanúmerica.

Aqui está uma implementação simples:

import math

# Constantes globais para o Feistel Cipher
MULTIPLIER = int(math.sqrt(2) * 1000)
INCREMENT = int(math.pi * 100000)
MODULUS = 2**20 + 3
SCALING_FACTOR = 2**24 - 1  # Fator de escala de 24 bits (16.777.215)

# Base62 charset
BASE62_CHARS = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"

def feistel_cipher(val, rounds=3):
    l1 = (val >> 16) & 0xffff
    r1 = val & 0xffff

    for i in range(rounds):
        l2 = r1
        r2 = l1 ^ int(math.floor(((MULTIPLIER * r1 + INCREMENT) % MODULUS) / MODULUS * SCALING_FACTOR))
        l1 = l2
        r1 = r2

    return (r1 << 16) + l1

def to_base62(num):
    if num == 0:
        return BASE62_CHARS[0]
    
    base62 = []
    while num > 0:
        remainder = num % 62
        base62.append(BASE62_CHARS[remainder])
        num = num // 62
    
    return ''.join(reversed(base62))

def generate_id(index):
    feistel_value = feistel_cipher(index)
    base62_id = to_base62(feistel_value)
    return base62_id.zfill(6)  # Garantir que o ID tenha exatamente 6 caracteres

# Exemplo de uso
print("Index | Feistel Value    | Base62 ID")
print("------+-----------------+----------")

for i in range(-10, 11):
    feistel_value = feistel_cipher(i)
    final_id = generate_id(i)
    print(f"{i:>5} | {feistel_value:>15} | {final_id}")

Aqui está a saída gerada:

Index | Feistel Value    | Base62 ID
------+-----------------+----------
   -10 |      -2028606875 | 02Awtc
    -9 |       -377150308 | 01tvDI
    -8 |      -1826568944 | 02Apzz
    -7 |        -39235198 | 00d7x0
    -6 |      -2325185930 | 031qFc
    -5 |       -800687972 | 01F0e4
    -4 |      -2247551437 | 02WGBa
    -3 |       -822742845 | 01E1Vc
    -2 |       -243871210 | 00xciQ
    -1 |      -1707877051 | 02DcXw
     0 |       2746720850 | 3jvTYx
     1 |        867012857 | 1G8o2V
     2 |        674985276 | 1AP7da
     3 |        889674871 | 1H2OpP
     4 |        745826968 | 1CytVA
     5 |       2939497522 | 3o7SRK
     6 |       1498284863 | 2cihU5
     7 |       2970999551 | 3oyt9r
     8 |       1134483750 | 2C5bNe
     9 |       2670139713 | 3jBcnT
    10 |       1222978124 | 2Ee2aY

A Realidade: Desempenho e Sugestões

Embora isso seja o que desejamos em teoria, essa implementação pode ser muito lenta para uso em produção. A finalidade dessa ilustração é mostrar como o processo funciona, mas em um ambiente real, você provavelmente não gostaria de fazer isso.

Sugestão Real:

Enquanto a solução que discutimos anteriormente serve como uma excelente ilustração de como conceitos como criptografia e conversão de base funcionam, para aplicações práticas, o uso de uma função de hash padrão e truncar o resultado é, na maioria dos casos, a melhor abordagem. Não há necessidade de reinventar a roda — aproveite as ferramentas robustas e otimizadas que já estão à sua disposição.

Além disso, evite adicionar bibliotecas desnecessárias ao seu projeto quando a solução já está disponível no seu ambiente. Mantenha seu código o mais simples possivel e você economizará tempo e recursos no longo prazo.

Realizar a geração de identificadores diretamente no banco de dados garante consistência, performance e reduz a complexidade na aplicação. No Postgres, por exemplo, você pode implementar essa lógica de forma simples usando um campo gerado, semelhante ao que o Git faz ao truncar hashes para gerar identificadores únicos e compactos.

CREATE TABLE my_table (
    id SERIAL PRIMARY KEY,
    unique_id VARCHAR(6) GENERATED ALWAYS AS (
        substring(encode(digest(CAST(id AS TEXT), 'sha256'), 'hex') FOR 6)
    ) STORED
);

Essa solução é simples e direta e mantém seu código fácil sem introduzir nenhuma dependência desnecessária. Ao entender os fundamentos, você pode fazer escolhas mais informadas e eficazes para suas necessidades práticas, utilizando de forma inteligente as ferramentas disponíveis.

Carregando publicação patrocinada...
1

Excelente explicação, obrigado pelas suas sugestōes. Achei muito interessante o algoritmo de Fesitel Cipher como modelo de estudo.

Eu tinha feito uma tentativa de truncar o valor de um hash a nível de aplicação, algo como:

hashed_token = hashlib.sha256()
short_hashed_token = hashed_token.hexdigest()[:6]

Porém acabei tendo problemas de colisōes, mas não tinha pensado em fazer isso a nivel de banco.

Vou testar fazer dessa forma!