OK, quindi sento di aver fatto abbastanza ricerche per iniziare almeno a spostare le cose in una direzione utile.
Il termine per ciò che stai cercando generalmente è una " funzione di hash perfetta ", con l'aggiunta che desideri un certo grado di casualità potenzialmente gestibile.
In generale, lo stato dell'arte è che si utilizza un metodo algoritmico che genera una mappatura e quindi si salva la mappatura finale. Ci sono molti modi interessanti per andare su questo ( e altro ), ma il problema deriva dal fatto che la probabilità di una collisione con qualsiasi metodo casuale è probabile che vada a garantire una collisione prima di esaurire il tuo spazio di input. Fornirò un esempio di questo ora, nel codice C # (copia / incolla il codice, credo):
System.Text.StringBuilder Sb = new System.Text.StringBuilder();
System.Collections.Generic.HashSet<string> results = new System.Collections.Generic.HashSet<string>();
using (System.Security.Cryptography.SHA512 hash = System.Security.Cryptography.SHA512Managed.Create() ) {
System.Text.Encoding enc = System.Text.Encoding.UTF8;
for (int input = 0; input < 10000; input++) {
Byte[] result = hash.ComputeHash(enc.GetBytes(input.ToString()));
foreach (Byte b in result) {
Sb.Append(b.ToString("x2"));
}
results.Add(Sb.ToString().Substring(Sb.Length - 3));
}
}
Quello che sto facendo è fornire un valore di input compreso tra 0 e 9999, convertirlo in un hash SHA512 e quindi prendere solo le ultime 3 cifre alfanumeriche. Puoi quindi confrontare la dimensione di HashSet con gli input per determinare quanti duplicati hai.
Il risultato: con 10000 ingressi ottieni solo 3735 risultati unici. Ahi: ci sono molte collisioni! Se cambi la tua richiesta di mappatura a 4 cifre (cambia la riga di codice finale sopra a Sb.Length - 4), ottieni 9303 - non male! Se si concedono 5 cifre di output si ottiene 9955 - ancora con le collisioni e abbiamo esteso in modo massivo il nostro output consentito e solo 10000 input!
Quindi, se hai usato un metodo del genere, devi limitare notevolmente i tuoi input massimi e probabilmente aumentare anche la dimensione dell'output.
Se non ti interessa molto della casualità, puoi usare (x + 18) % 46656
, come in sostituzione dell'ultima riga con questo:
results.Add(((input + 18) % 46656).ToString());
Il risultato è zero collisioni e anche molto più veloce. L'output non è del tutto casuale, ovviamente, specialmente se si sale in sequenza.
Ora, con un po 'di sintonizzazione manuale sono riuscito a creare questo piccolo stupido:
results.Add(((input * (40001) + 11) % 46656).ToString());
Quindi prendi l'input, moltiplicalo per 40001, aggiungi 11, quindi "avvolgilo" al tuo input massimo. Su 0-46656 come input non genera collisioni, eppure salta dappertutto - f (1) - > 400012, mentre f (2) - > 33357. Se si salta usando i primi pochi input (0-2) la funzione che genera questi numeri è ancora più opaca e, poiché non è lineare, non è facile trovare la funzione che genera questo set. Moltiplicare per un numero dispari e puoi usare questo come una sorta di "seme" per la tua funzione: i numeri primi potrebbero essere una scelta ancora migliore! I piccoli numeri si traducono in minore nervosità, mentre moltiplicando per lo stesso numero si sta prendendo il mod di è ... beh, sempre 0. Sospetto che da qualche parte nel medio / alto sia l'ideale.
Quindi analizza l'output intero nel formato di stringa desiderato, e tadah, Bob è tuo zio!
Ora, se vuoi qualcosa di più casuale con entropia misurabile e che impiega molto tempo a decodificare ... beh, sarà molto più difficile organizzare di molto.
Spero che questo sia almeno utile per avvicinarti a dove vuoi andare.