Per quanto riguarda la teoria, ogni output della funzione amazing_hash
ha una dimensione fissa, ed è mappato su un altro output della funzione hash, e un altro, e così via.
Quindi, lasciando da parte il primissimo input, hai una funzione da un insieme finito a se stesso. La funzione può o non può essere biettiva, ma non è una proprietà richiesta di una funzione di hash che è. Il dominio della funzione necessariamente è diviso in:
- uno o più cicli, più
- zero o più "code", cioè sequenze che portano in uno dei cicli o in un'altra coda. Quando due code si uniscono consideriamo arbitrario quale si sta portando nell'altra, ma userò il numero di code più tardi, quindi deve essere definito in questo modo :-) Definire anche la "fine" di una coda per essere il punto in cui unisce un'altra coda o un ciclo.
Ogni punto, quando è iterato, fa parte o meno di un ciclo, oppure segue una coda e le code che la coda unisce, finché non si unisce a un ciclo. Questa è una proprietà necessaria di una funzione da un insieme finito a se stesso. Un percorso non può funzionare per sempre senza entrare in un ciclo, perché ci sono solo un numero finito di valori e quindi alla fine deve ripeterne uno. È quindi possibile immaginare la funzione visivamente come un sacco di cerchi, con rami sporgenti dai lati di essi. I rami portano tutti nelle cerchie.
Con una singola iterazione (ovvero un ulteriore hash dopo l'hash iniziale), quante collisioni ci sono? Bene, è legato al numero di code, poiché la fine di una coda è un luogo in cui due valori non uguali hanno lo stesso hash. Ogni punto di unione implica che ci sia un numero di valori di collisione uguale al numero di strutture che si uniscono in quel punto. Ogni coda termina con un join, quindi se definiamo attentamente "code" e "collisioni", il numero di collisioni è solo il numero di code.
Dopo due iterazioni, quante collisioni ci sono? È il numero di code (dato che una volta che i due valori sono entrati in collisione rimangono in collisione), più il numero di nuove collisioni causate dall'iterazione extra. L'iterazione aggiuntiva provoca una collisione se "entrambi i lati" di un punto di unione sono lunghi almeno 2 nodi. Quindi, quando si uniscono due code, devono essere entrambi lunghi almeno 2 nodi e dove una coda si unisce a un ciclo deve essere almeno un 2 cicli.
Dopo n iterazioni, ulteriori collisioni vengono generate da code almeno n nodi lunghi e n cicli.
Nel caso estremo, una funzione di hash che è biiettiva non ha code. Questo è un teorema di funzioni finite: ogni permutazione divide il suo dominio in cicli. Quindi dovrebbe essere facile vedere che non importa quante iterazioni fai, ci sono collisioni no (diverse da quelle causate dall'hash iniziale, ovviamente). Ogni punto si muove attorno al suo ciclo. Spostando ogni punto un numero uguale di passaggi attorno a un ciclo, sono ancora tutti in posizioni diverse.
Altrimenti, per iniziare con più iterazioni che fai, più collisioni vengono generate quando le code si uniscono nei cicli. Tuttavia, c'è un limite superiore a questo processo, perché ogni coda e ogni ciclo ha una lunghezza finita. Alla fine non causerai più collisioni quando fai più iterazioni. Ciò non accadrà finché non avrai raggiunto la lunghezza della coda più lunga nella tua funzione.
Questo è tutto in teoria: in pratica la coda più lunga potrebbe essere ancora più grande di quanto tu abbia tempo per iterare. Se è così, continuerai ad aumentare il numero di collisioni per tutto il tempo che puoi praticamente eseguire.
Tuttavia , il numero di collisioni introdotte da ciascuna iterazione è ancora molto piccolo rispetto allo spazio hash, così piccolo che è incredibilmente improbabile che si verifichi una collisione in questo modo. Come facciamo a saperlo? Perché se non lo fosse, allora l'algoritmo di ricerca del ciclo di Floyd sarebbe un mezzo efficace per trovare le collisioni nella funzione di hash. La funzione di hash non sarebbe "sorprendente" secondo le ipotesi della domanda, sarebbe risaputo essere infranta: -)