web-dev-qa-db-ja.com

ハッシュ値を作成するためのExcel関数はありますか?

私はドキュメント名をキーとしたいくつかのデータリストを扱っています。文書名は、非常にわかりやすいですが、表示する必要がある場合は非常に扱いにくく(最大256バイトまではかなりの面積です)、必要に応じて簡単に再現できる小さなキーフィールドを作成できるようにしたいです。他のワークシートまたはワークブックからVLOOKUPを実行する。

タイトルごとに一意で再現可能なハッシュ各タイトルが最も適切になると考えています。利用可能な機能はありますか、それとも私は自分自身のアルゴリズムの開発を検討していますか?

これや他の戦略について何か考えやアイデアはありますか?

24
dwwilson66

あなたはあなた自身の関数を書く必要はありません - 他の人がすでにあなたのためにそれをしました。
例えば、私はこの stackoverflow answerに対して5つのVBAハッシュ関数を集めて比較しました

私は個人的にこのVBA機能を使っています

  • マクロをVBAモジュールにコピーした後にExcelで=BASE64SHA1(A1)で呼び出されます
  • ライブラリ "Microsoft MSXML"を使用しているため(.NETバインディングが必要)、.NETが必要です。

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

ハッシュ長のカスタマイズ

  • ハッシュは、最初は28文字のUnicode文字列です(大文字と小文字の区別+特殊文字)。
  • この行を使ってハッシュ長をカスタマイズします。Const cutoff As Integer = 5
  • 4桁のハッシュ= 6895行で36回の衝突= 0.5%の衝突率
  • 5桁のハッシュ= 6895行で0回の衝突= 0%の衝突率

.NETを必要とせず外部ライブラリを使用しないハッシュ関数( 3つすべてのCRC16関数 )もあります。しかし、ハッシュは長くなり、衝突が増えます。

この サンプルワークブック をダウンロードして、5つすべてのハッシュ実装を試してみることもできます。ご覧のとおり、最初のシートには良い比較があります

32
nixda

衝突についてはあまり気にしませんが、可変長文字列フィールドに基づく行の弱い擬似ランダム化器が必要でした。これがうまくいった非常識な解決策の1つです:

=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)

Z2はハッシュしたい文字列を含むセルです。

"MOD"は科学的表記法へのオーバーフローを防ぐためにあります。 1009は素数で、X * 255 <max_int_sizeとなるように任意のXを使用できます。 10は任意です。何でも使う「その他」の値は任意です(ここではpiの桁です)。何でも使う文字の位置(1,3,5,7,9)は任意です。何でも使う.

7

かなり小さいリストの場合は、組み込みのExcel関数を使用してスクランブラ(貧乏人のハッシュ関数)を作成できます。

例えば。

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

ここでA1とB1はランダムな開始文字と文字列の長さを保持します。

ちょっと手間をかけてチェックするだけで、ほとんどの場合、実行可能な固有IDを非常に素早く取得できます。

仕組み:式は、文字列の最初の文字と文字列の中央から取られた固定文字を使い、ファンニング関数としてLEN()を使います。衝突の可能性を減らすため。

注意:これはハッシュではないが、必要なときは何かを素早く終わらせるために、そして衝突がないことを確かめるために結果を調べることができるなら、それは非常にうまくいきます。

編集:あなたの文字列が可変長(例えばフルネーム)を持つべきだが固定幅フィールドを持つデータベースレコードから引き出されるなら、あなたはそれをしたいでしょう。このような:

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

長さが意味のあるスクランブラになるように。

3
Assad Ebrahim

毎回スクリプトを実行しなくても衝突を防ぐことができ、かなり良い結果が得られます。 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)))

文字列全体から文字を選択し、それらの文字のそれぞれの値を取り、値を加算して(同じ結果になる同じ場所にある同じ文字を防ぐため)、それぞれを乗算/除算して合計に対してCOS関数を実行します。

1
Ant Cole

これを試すことができます。 2つの列で疑似#を実行します。

= + IF(AND(ISBLANK(D3)、ISBLANK(E3))、 ""、コード(TRIM(D3&E3))+コード(MID(TRIM(D3&E3)、$ A $ 1 * LEN) (D3&E3)、1))INT(LEN(TRIM(D3&E3))$ B $ 1))

A1とB1が手動で入力されたランダムシードを格納する場所:0

1

私の知る限りでは、ハッシュ関数はExcelに組み込まれていません - あなたはVBAのユーザー定義関数としてそれを構築する必要があるでしょう。

しかし、あなたの目的のためにはハッシュを使うことが必須ではない、あるいは本当に有利だとは思わないことに注意してください。 VLOOKUPは256バイトでも同様に動作しますが、小さいハッシュでも同様です。確かに、それは少し遅いかもしれません - 確かにそれは計り知れないほど小さいので少しです。そしてハッシュ値を追加することはあなたにとって、そしてExcelにとってもっと手間がかかります。

0
Peter Albert