0

ファイルAにバイトがあるとしましょう:

2
5
8
0
33
90
1
3
200
201
23
12
55

そして、最後の3つの連続したバイトの合計を格納する単純なハッシュアルゴリズムがあるので、次のようになります。

2   
5   
8   - = 8+5+2 = 15
0   
33  
90  - = 90+33+0 = 123
1   
3   
200 - = 204
201 
23  
12  - = 236

したがって、ファイルAを次のように表すことができます。15, 123, 204, 236

そのファイルを新しいコンピューターBにコピーし、いくつかの小さな変更を加えたとしましょう。ファイルBのバイトは次のとおりです。

255 
2   
5   
8   
0   
33  
90  
1   
3   
200 
201 
23  
12
255
255 

「違いは、ファイルの先頭に1バイト追加され、最後に2バイト追加されることですが、残りは非常に似ていることに注意してください。」

そのため、同じアルゴリズムを実行して、ファイルの一部が同じであるかどうかを判断できます。ファイルAがハッシュコードで表されていたことを思い出してください。15, 123, 204, 236ファイルBがそれらのハッシュコードのいくつかを私に与えるかどうか見てみましょう!

したがって、ファイルBIは、連続する3バイトごとに実行する必要があります。

int[] sums; // array where we will hold the sum of the last bytes


255 sums[0]  =          255     
2   sums[1]  =  2+ sums[0]    = 257     
5   sums[2]  =  5+ sums[1]    = 262     
8   sums[3]  =  8+ sums[2]    = 270  hash = sums[3]-sums[0]   = 15   --> MATHES FILE A!
0   sums[4]  =  0+ sums[3]    = 270  hash = sums[4]-sums[1]   = 13
33  sums[5]  =  33+ sums[4]   = 303  hash = sums[5]-sums[2]   = 41
90  sums[6]  =  90+ sums[5]   = 393  hash = sums[6]-sums[3]   = 123  --> MATHES FILE A!
1   sums[7]  =  1+ sums[6]    = 394  hash = sums[7]-sums[4]   = 124
3   sums[8]  =  3+ sums[7]    = 397  hash = sums[8]-sums[5]   = 94
200 sums[9]  =  200+ sums[8]  = 597  hash = sums[9]-sums[6]   = 204  --> MATHES FILE A!
201 sums[10] =  201+ sums[9]  = 798  hash = sums[10]-sums[7]  = 404
23  sums[11] =  23+ sums[10]  = 821  hash = sums[11]-sums[8]  = 424
12  sums[12] =  12+ sums[11]  = 833  hash = sums[12]-sums[9]  = 236  --> MATHES FILE A!
55  sums[13] =  55+ sums[12]  = 888  hash = sums[13]-sums[10] = 90
255 sums[14] =  255+ sums[13] = 1143    hash = sums[14]-sums[11] =  322
255 sums[15] =  255+ sums[14] = 1398    hash = sums[15]-sums[12] =  565

そのテーブルを見ると、ハッシュコードが一致しているため、ファイルBにはファイルAのバイトと追加のバイトが含まれていることがわかります。

このアルゴリズムを示す理由は、それがn次であったためです。言い換えると、最後の3つの連続するバイトのハッシュを、それらを反復処理することなく計算できたからです。

最後の3バイトのmd5を実行するなど、より複雑なアルゴリズムを使用する場合は、n ^ 3の順序になります。これは、ファイルを反復処理するときに、BIにハッシュを計算する内部forループが必要になるためです。最後の3バイト。

だから私の質問は:

アルゴリズムをn次に保つように改善するにはどうすればよいですか。これは、ハッシュを1回だけ計算することです。md5などの既存のハッシュアルゴリズムを使用する場合、アルゴリズムの順序を大幅に増やす内部ループをアルゴリズム内に配置する必要があります。

足し算の代わりに掛け算でも同じことができることに注意してください。しかし、カウンターは非常に速く成長します。多分私は乗算と加算と減算を組み合わせることができます...

編集

また、私がグーグルで検索した場合:

再帰的ハッシュ関数(グラム単位)

たくさんの情報が出てきて、それらのアルゴリズムを理解するのは非常に難しいと思います...

プロジェクトにこのアルゴリズムを実装する必要があるので、車輪の再発明を行っています...そこにはたくさんのアルゴリズムがあることを知っています。

また、私が考えていた別の解決策は、同じアルゴリズムに加えて強力な別のアルゴリズムを実行することでした。したがって、ファイルAIは、3バイトごとに同じアルゴリズムを実行し、さらに3バイトごとにmd5を実行します。2番目のファイルでは、最初のアルゴリズムが実現した場合に2番目のアルゴリズムを実行します。

4

3 に答える 3

2

編集:

「再帰的」の意味を考えれば考えるほど、以前に提示した解決策が、何か役に立つことを行うために実装すべきものであることに疑問が生じます。

おそらく、再帰的な操作であるハッシュ ツリー アルゴリズムを実装する必要があります。

これを行うには、リストをハッシュし、リストを 2 つに分割し、これら 2 つのサブリストに再帰します。リストのサイズが 1、または必要な最小のハッシュ サイズになったときに終了します。再帰の各レベルは、合計ハッシュ出力のサイズを 2 倍にするためです。

擬似コード:

create-hash-tree(input list, minimum size: default = 1):
  initialize the output list
  hash-sublist(input list, output list, minimum size)
  return output list

hash-sublist(input list, output list, minimum size):
  add sum-based-hash(list) result to output list // easily swap hash styles here
  if size(input list) > minimum size:
    split the list into two halves
    hash-sublist(first half of list, output list, minimum size)
    hash-sublist(second half of list, output list, minimum size)

sum-based-hash(list):
  initialize the running total to 0

  for each item in the list:
    add the current item to the running total

  return the running total

このアルゴリズム全体の実行時間はO(hash(m)); m = n * (log(n) + 1)で、hash(m)通常は線形時間です。

ストレージスペースは のようなものO(hash * s); s = 2n - 1で、ハッシュは通常一定のサイズです。

C# の場合、出力リストを aにしますが、記憶域を節約するためList<HashType>に入力リストを anにし、Linq を使用して、2 つの新しいサブリストを割り当てずにリストをすばやく「分割」します。IEnumerable<ItemType>

オリジナル:

O(n + m)これを実行時間にすることができると思います。ここnで、 はリストmのサイズ、 は実行中の集計のサイズですn < m(それ以外の場合、すべての合計は等しくなります)。

両端キュー付き

メモリ消費量は、スタック サイズmに一時ストレージのサイズを加えたものになります。

これを行うには、両端キューと実行中の合計を使用します。現在の合計に追加しながら、新しく遭遇した値をリストにプッシュし、キューが sizemに達すると、リストからポップして現在の合計から減算します。

ここにいくつかの擬似コードがあります:

initialize the running total to 0

for each item in the list:
  add the current item to the running total
  push the current value onto the end of the dequeue
  if dequeue.length > m:
    pop off the front of the dequeue
    subtract the popped value from the running total
  assign the running total to the current sum slot in the list

reset the index to the beginning of the list

while the dequeue isn't empty:
  add the item in the list at the current index to the running total
  pop off the front of the dequeue
  subtract the popped value from the running total
  assign the running total to the current sum slot in the list
  increment the index

これは再帰的ではなく、反復的です。

このアルゴリズムの実行は次のようになります ( の場合m = 3):

value   sum slot   overwritten sum slot
2       2          92
5       7          74
8       15         70
0       15         15
33      46
90      131
1       124
3       127
200     294
201     405
23      427
12      436
55      291

インデックス付き

m最初に最後の値の合計を取得し、デキューをポップする代わりにインデックスのオフセットを使用することで、キューとスロットの再割り当てを削除できますarray[i - m]

実行中の集計を構築するためのループと、すべての値を入力するためのループの 2 つのループが必要なため、実行時間が短縮されることはありません。ただし、メモリ使用量をスタックスペースのみに減らします (事実上O(1))。

ここにいくつかの擬似コードがあります:

initialize the running total to 0

for the last m items in the list:
  add those items to the running total

for each item in the list:
  add the current item to the running total
  subtract the value of the item m slots earlier from the running total
  assign the running total to the current sum slot in the list

m slots earlierトリッキーな部分です。これを 2 つのループに分割することもできます。

  • リストの末尾から m を引いて i を足したもの
  • i から m を引いたもの

または、モジュロ演算を使用して、次の場合に値を「ラップ」できますi - m < 0

int valueToSutract = array[(i - m) % n];
于 2011-12-07T03:50:26.313 に答える
1

http://en.wikipedia.org/wiki/Rabin%E2%80%93Karp_algorithmは、 http://en.wikipedia.org/wiki/Rolling_hash と呼ばれる更新可能なハッシュ関数を使用します。これは、その MD5/SHA を計算するのがはるかに簡単になり、劣っていない可能性があります。

それについていくつかのことを証明できます: それは選択された定数 a の次数 d の多項式です。誰かが 2 つのテキストを提供し、ランダムに 1 つを選択したとします。衝突の確率は?ハッシュ値が同じなら、それらを減算すると、a をルートとする多項式が得られます。ゼロでない多項式の根は多くても d であり、a はランダムに選択されるため、確率は多くても法 / d であり、大きな法に対しては非常に小さくなります。

もちろん、MD5/SHA は安全ですが、安全なバリアントについてはhttp://cr.yp.to/mac/poly1305-20050329.pdfを参照してください。

于 2011-12-07T05:55:45.980 に答える
0

それが私がこれまでに得たものです。ハッシュの配列を比較したり、読み取りのためにファイルを開いたりするなど、時間がかからないはずの手順がありません。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace RecursiveHashing
{
    static class Utilities
    {

        // used for circular arrays. If my circular array is of size 5 and it's
        // current position is 2 if I shift 3 units to the left I shouls be in index
        // 4 of the array.
        public static int Shift(this int number, int shift, int divisor)
        {
            var tempa = (number + shift) % divisor;
            if (tempa < 0)
                tempa = divisor + tempa;
            return tempa;
        }

    }
    class Program
    {
        const int CHUNCK_SIZE = 4; // split the files in chuncks of 4 bytes

        /* 
         * formula that I will use to compute hash
         * 
         *      formula =  sum(chunck) * (a[c]+1)*(a[c-1]+1)*(a[c-2]+1)*(-1^a[c])
         *      
         *          where:
         *              sum(chunk)  = sum of current chunck
         *              a[c]        = current byte
         *              a[c-1]      = last byte
         *              a[c-2]      = last last byte
         *              -1^a[c]     = eather -1 or +1  
         *              
         *      this formula is efficient because I can get the sum of any current index by keeping trak of the overal sum
         *      thus this algorithm should be of order n
         */

        static void Main(string[] args)
        {
            Part1(); // Missing implementation to open file for reading
            Part2();
        }



        // fist part compute hashes on first file
        static void Part1()
        {
            // pertend file b reads those bytes
            byte[] FileB = new byte[]{2,3,5,8,2,0,1,0,0,0,1,2,4,5,6,7,8,2,3,4,5,6,7,8,11,};

            // create an array where to store the chashes
            // index 0 will use a fast hash algorithm. index 1 will use a more secure hashing algorithm
            Int64[,] hashes = new Int64[(FileB.Length / CHUNCK_SIZE) + 10, 2];


            // used to track on what index of the file we are at
            int counter = 0;
            byte[] current = new byte[CHUNCK_SIZE + 1]; // circual array  needed to remember the last few bytes
            UInt64[] sum = new UInt64[CHUNCK_SIZE + 1]; // circual array  needed to remember the last sums
            int index = 0; // position where in circular array

            int numberOfHashes = 0; // number of hashes created so far


            while (counter < FileB.Length)
            {
                int i = 0;
                for (; i < CHUNCK_SIZE; i++)
                {
                    if (counter == 0)
                    {
                        sum[index] = FileB[counter];
                    }
                    else
                    {
                        sum[index] = FileB[counter] + sum[index.Shift(-1, CHUNCK_SIZE + 1)];
                    }
                    current[index] = FileB[counter];
                    counter++;

                    if (counter % CHUNCK_SIZE == 0 || counter == FileB.Length)
                    {
                        // get the sum of the last chunk
                        var a = (sum[index] - sum[index.Shift(1, CHUNCK_SIZE + 1)]);
                        Int64 tempHash = (Int64)a;

                        // conpute my hash function
                        tempHash = tempHash * ((Int64)current[index] + 1)
                                          * ((Int64)current[index.Shift(-1, CHUNCK_SIZE + 1)] + 1)
                                          * ((Int64)current[index.Shift(-2, CHUNCK_SIZE + 1)] + 1)
                                          * (Int64)(Math.Pow(-1, current[index]));


                        // add the hashes to the array
                        hashes[numberOfHashes, 0] = tempHash;
                        numberOfHashes++;

                        hashes[numberOfHashes, 1] = -1;// later store a stronger hash function
                        numberOfHashes++;

                        // MISSING IMPLEMENTATION TO STORE A SECOND STRONGER HASH FUNCTION

                        if (counter == FileB.Length)
                            break;
                    }

                    index++;
                    index = index % (CHUNCK_SIZE + 1); // if index is out of bounds in circular array place it at position 0
                }
            }
        }


        static void Part2()
        {
            // simulate file read of a similar file
            byte[] FileB = new byte[]{1,3,5,8,2,0,1,0,0,0,1,2,4,5,6,7,8,2,3,4,5,6,7,8,11};            

            // place where we will place all matching hashes
            Int64[,] hashes = new Int64[(FileB.Length / CHUNCK_SIZE) + 10, 2];


            int counter = 0;
            byte[] current = new byte[CHUNCK_SIZE + 1]; // circual array
            UInt64[] sum = new UInt64[CHUNCK_SIZE + 1]; // circual array
            int index = 0; // position where in circular array



            while (counter < FileB.Length)
            {
                int i = 0;
                for (; i < CHUNCK_SIZE; i++)
                {
                    if (counter == 0)
                    {
                        sum[index] = FileB[counter];
                    }
                    else
                    {
                        sum[index] = FileB[counter] + sum[index.Shift(-1, CHUNCK_SIZE + 1)];
                    }
                    current[index] = FileB[counter];
                    counter++;

                    // here we compute the hash every time and we are missing implementation to 
                    // check if hash is contained by the other file
                    if (counter >= CHUNCK_SIZE)
                    {
                        var a = (sum[index] - sum[index.Shift(1, CHUNCK_SIZE + 1)]);

                        Int64 tempHash = (Int64)a;

                        tempHash = tempHash * ((Int64)current[index] + 1)
                                          * ((Int64)current[index.Shift(-1, CHUNCK_SIZE + 1)] + 1)
                                          * ((Int64)current[index.Shift(-2, CHUNCK_SIZE + 1)] + 1)
                                          * (Int64)(Math.Pow(-1, current[index]));

                        if (counter == FileB.Length)
                            break;
                    }

                    index++;
                    index = index % (CHUNCK_SIZE + 1);
                }
            }
        }
    }
}

同じアルゴリズムを使用して表に表された同じファイル

                        hashes
bytes       sum Ac  A[c-1]  A[c-2]  -1^Ac   sum * (Ac+1) * (A[c-1]+1) * (A[c-2]+1)
2       2                   
3       5                   
5       10  5   3   2   -1  
8       18  8   5   3   1   3888
2       20  2   8   5   1   
0       20  0   2   8   1   
1       21  1   0   2   -1  
0       21  0   1   0   1   6
0       21  0   0   1   1   
0       21  0   0   0   1   
1       22  1   0   0   -1  
2       24  2   1   0   1   18
4       28  4   2   1   1   
5       33  5   4   2   -1  
6       39  6   5   4   1   
7       46  7   6   5   -1  -7392
8       54  8   7   6   1   
2       56  2   8   7   1   
3       59  3   2   8   -1  
4       63  4   3   2   1   1020
5       68  5   4   3   -1  
6       74  6   5   4   1   
7       81  7   6   5   -1  
8       89  8   7   6   1   13104
11      100 11  8   7   -1  -27648






file b                          
                            rolling hashes
bytes       0   Ac  A[c-1]  A[c-2]  -1^Ac   sum * (Ac+1) * (A[c-1]+1) * (A[c-2]+1)
1       1                   
3       4                   
5       9   5   3   1   -1  
8       17  8   5   3   1   3672
2       19  2   8   5   1   2916
0       19  0   2   8   1   405
1       20  1   0   2   -1  -66
0       20  0   1   0   1   6
0       20  0   0   1   1   2
0       20  0   0   0   1   1
1       21  1   0   0   -1  -2
2       23  2   1   0   1   18
4       27  4   2   1   1   210
5       32  5   4   2   -1  -1080
6       38  6   5   4   1   3570
7       45  7   6   5   -1  -7392
8       53  8   7   6   1   13104
2       55  2   8   7   1   4968
3       58  3   2   8   -1  -2160
4       62  4   3   2   1   1020
5       67  5   4   3   -1  -1680
6       73  6   5   4   1   3780
7       80  7   6   5   -1  -7392
8       88  8   7   6   1   13104
11      99  11  8   7   -1  -27648
于 2011-12-07T08:00:56.620 に答える