Existe uma função do Excel para criar um valor de hash?

26

Estou trabalhando com várias listas de dados codificadas pelo nome do documento. Os nomes dos documentos, embora muito descritivos, são bastante complicados se eu precisar visualizá-los (até 256 bytes são muito imóveis) e eu adoraria poder criar um campo-chave menor que seja facilmente reproduzível, caso eu precise para fazer a VLOOKUPpartir de outra pasta de trabalho ou pasta de trabalho.

Estou pensando que um hash do título que seria único e reproduzível para cada título seria o mais apropriado. Existe uma função disponível ou estou desenvolvendo meu próprio algoritmo?

Quaisquer pensamentos ou idéias sobre esta ou outra estratégia?

dwwilson66
fonte

Respostas:

34

Você não precisa escrever sua própria função - outros já fizeram isso por você.
Por exemplo, eu coletei e comparei cinco funções de hash do VBA nesta resposta de stackoverflow

Pessoalmente, eu uso essa função VBA

  • é chamado =BASE64SHA1(A1)no Excel depois que você copiou a macro para um módulo VBA
  • requer .NET, pois usa a biblioteca "Microsoft MSXML" (com ligação tardia)

Public Function BASE64SHA1(ByVal sTextToHash As String)

    Dim asc As Object
    Dim enc As Object
    Dim TextToHash() As Byte
    Dim SharedSecretKey() As Byte
    Dim bytes() As Byte
    Const cutoff As Integer = 5

    Set asc = CreateObject("System.Text.UTF8Encoding")
    Set enc = CreateObject("System.Security.Cryptography.HMACSHA1")

    TextToHash = asc.GetBytes_4(sTextToHash)
    SharedSecretKey = asc.GetBytes_4(sTextToHash)
    enc.Key = SharedSecretKey

    bytes = enc.ComputeHash_2((TextToHash))
    BASE64SHA1 = EncodeBase64(bytes)
    BASE64SHA1 = Left(BASE64SHA1, cutoff)

    Set asc = Nothing
    Set enc = Nothing

End Function

Private Function EncodeBase64(ByRef arrData() As Byte) As String

    Dim objXML As Object
    Dim objNode As Object

    Set objXML = CreateObject("MSXML2.DOMDocument")
    Set objNode = objXML.createElement("b64")

    objNode.DataType = "bin.base64"
    objNode.nodeTypedValue = arrData
    EncodeBase64 = objNode.text

    Set objNode = Nothing
    Set objXML = Nothing

End Function

Personalizando o comprimento do hash

  • o hash inicialmente é uma string unicode de 28 caracteres (diferencia maiúsculas de minúsculas + caracteres especiais)
  • Você personaliza o comprimento do hash com esta linha: Const cutoff As Integer = 5
  • Hash de 4 dígitos = 36 colisões em 6895 linhas = taxa de colisão de 0,5%
  • Hash de 5 dígitos = 0 colisões em 6895 linhas = 0% de taxa de colisão

Também existem funções de hash ( todas as três funções CRC16 ) que não requerem .NET e não usam bibliotecas externas. Mas o hash é mais longo e produz mais colisões.

Você também pode fazer o download desta pasta de trabalho de exemplo e brincar com todas as 5 implementações de hash. Como você vê, há uma boa comparação na primeira folha

nixda
fonte
1
Parece ótimo. No entanto, não tenho experiência em VBA suficiente para impedir o retorno do Excel #NAME?. Exibir código> recortar e colar código em uma nova janela - dentro da planilha correta no navegador> salvar como planilha habilitada para macro> fechar e retornar ao excel ... mais alguma coisa que esteja faltando? Preciso compilá-lo de alguma forma?
Djilson66
Sim ... para esclarecer ... colei-o na nova janela de código que apareceu quando eu fui para a guia planilha> exibir código ... Fazendo o download da amostra agora, mas gostaria de entender por que o Excel não reconhece meu código
Djilson66
WooHoo ... a folha de amostra ajudou. Percebi que eu tinha colado o código na janela OBJECT do Excel e não em uma janela MODULE. Estou recebendo hashes na minha pasta de trabalho agora!
dwwilson66
1
Esta é uma excelente ferramenta.
Jay Killeen
1
Você pode tornar o cutoffparâmetro e o opcional com um padrão diferente movendo-o para a lista de parâmetros Function Public Function BASE64SHA1(ByVal sTextToHash As String, Optional ByVal cutoff As Integer = 8) e removendo a declaração dentro da função.
Core
9

Não me importo muito com colisões, mas precisava de um pseudo-randomizador fraco de linhas com base em um campo de string de comprimento variável. Aqui está uma solução insana que funcionou bem:

=MOD(MOD(MOD(MOD(MOD(IF(LEN(Z2)>=1,CODE(MID(Z2,1,1))+10,31),1009)*IF(LEN(Z2)>=3,CODE(MID(Z2,3,1))+10,41),1009)*IF(LEN(Z2)>=5,CODE(MID(Z2,5,1))+10,59),1009)*IF(LEN(Z2)>=7,CODE(MID(Z2,7,1))+10,26),1009)*IF(LEN(Z2)>=9,CODE(MID(Z2,9,1))+10,53),1009)

Onde Z2está a célula que contém a sequência que você deseja hash.

Os "MOD" estão lá para evitar transbordar para notação científica. 1009é primo, poderia usar qualquer coisa X para que X * 255 < max_int_size. 10 é arbitrário; use qualquer coisa. Os valores "restantes" são arbitrários (dígitos de pi aqui!); use qualquer coisa. A localização dos caracteres (1,3,5,7,9) é arbitrária; use qualquer coisa.

Covarde anônimo
fonte
2
Honestamente, essa é a resposta mais simples, duvido que colisões sejam um problema para a maioria dos casos de uso do Excel.
rola
3

Para uma lista razoavelmente pequena, você pode criar um embaralhador (função de hash do pobre homem) usando funções internas do Excel.

Por exemplo

 =CODE(A2)*LEN(A2) + CODE(MID(A2,$A$1,$B$1))*LEN(MID(A2,$A$1,$B$1))

Aqui A1 e B1 mantêm uma letra inicial aleatória e o comprimento da string.

Um pouco de mexer e checar e, na maioria dos casos, é possível obter um ID exclusivo viável rapidamente.

Como funciona : a fórmula usa a primeira letra da string e uma letra fixa tirada do meio da string e usa LEN () como uma 'função de ventilação' para reduzir a chance de colisões.

CAVEAT : isso não é um hash, mas quando você precisa fazer algo rapidamente e pode inspecionar os resultados para ver se não há colisões, funciona muito bem.

Edit: Se suas strings tiverem comprimentos variáveis ​​(por exemplo, nomes completos), mas forem extraídas de um registro de banco de dados com campos de largura fixa, convém fazer o seguinte:

 =CODE(TRIM(C8))*LEN(TRIM(C8))
       +CODE(MID(TRIM(C8),$A$1,1))*LEN(MID(TRIM(C8),$A$1,$B$1))

para que os comprimentos sejam um embaralhador significativo.

Assad Ebrahim
fonte
1
Ótima resposta! (: "A função do homem pobre de hash", "ressalva", "como funciona" :)
noz sobre natty
1
Para "inspecionar os resultados para ver se não há colisões", você pode simplesmente tentar / testar isso executando DATA> REMOVE DUPLICATES e ver se há algum. [obviamente / presumivelmente, se você fizer duplicatas encouter você poderia apenas re-executar a função acima para estes iterativamente até que não haja duplicatas são deixados]
noz sobre natty
2

Estou usando isso, que fornece bons resultados com a prevenção de conflitos sem a necessidade de executar um script a cada vez. Eu precisava de um valor entre 0 - 1.

=ABS(COS((CODE(MID(A2,ROUNDUP(LEN(A2)/9,0),1))*(CODE(MID(A2,ROUNDUP(LEN(A2)/5,0),1))+100)/CODE(MID(A2,ROUNDUP(LEN(A2)/3,0),1))*(CODE(MID(A2,ROUNDUP(LEN(A2)*8/9,0),1))+25)/CODE(MID(A2,ROUNDUP(LEN(A2)*6/9,0),1))*(CODE(MID(A2,ROUNDUP(LEN(A2)*4/9,0),1))-25))/LEN(A2)+CODE(A2)))

Ele seleciona letras do outro lado da string, pega o valor de cada uma dessas letras, adiciona um valor (para evitar que as mesmas letras em lugares diferentes gerem os mesmos resultados), multiplica / divide cada uma e executa uma função COS sobre o total.

Formiga Cole
fonte
1

Você pode tentar isso. Execute um pseudo # em duas colunas:

= + SE (E (ISBLANK (D3), ISBLANK (E3)), "", CÓDIGO (TRIM (D3 & E3)) * LEN (TRIM (D3 & E3)) + CÓDIGO (MID (TRIM (D3 & E3), $ A $ 1 * LEN (D3 e E3), 1)) INT (LEN (TRIM (D3 e E3)) $ B $ 1))

Onde A1 e B1 armazenam sementes aleatórias inseridas manualmente: 0

Michael Polubinski
fonte
0

Que eu saiba, não há nenhuma função de hash embutida no Excel - você precisaria criar uma como uma Função Definida pelo Usuário no VBA.

No entanto, observe que, para o seu propósito, não acho que é necessário usar um hash ou realmente vantajoso! VLOOKUPfuncionará tão bem em 256 bytes quanto em um hash menor. Claro, pode ser um pouco mais lento - com certeza é tão pequeno que é incomensurável. E, em seguida, adicionar os valores de hash é mais trabalhoso para você - e para o Excel ...

Peter Albert
fonte
sim ... Eu sei disso, mas apenas do ponto de vista de apresentação, eu prefiro apresentar, digamos, 15 bytes de hash que 256 bytes de titleno meu painel esquerdo congelado ...
dwwilson66