2010-08-04 16 views
14

Tengo una clase que internamente es solo una matriz de enteros. Una vez construido, la matriz nunca cambia. Me gustaría precomputar un buen hashcode para que esta clase se pueda usar de manera muy eficiente como clave en un diccionario. La longitud de la matriz es inferior a unos 30 elementos, y los enteros están entre -1000 y 1000 en general.C# hashcode para una matriz de ints

+1

clave de diccionario es único y si la matriz de almacenamiento de objetos de valor, y la clave se calcula en base a ellos, entonces no hay garantía de que se puede obtener una clave hash único para el diccionario –

+1

@Fadrian: El PO no lo hace quiere calcular una clave, pero un HashValue. Busca lo que eso significa. Los valores de Hash son pseudo-únicos. –

+0

Gracias Henk. Sé cómo se supone que funcionan los valores de hash y es posible que haya leído mal la intención de la pregunta cuando publiqué el comentario y es genial que lo hayas señalado. –

Respuesta

20

No es muy inteligente, pero suficiente a efectos prácticos:

EDIT: cambiado debido a los comentarios de Henk Holterman, gracias por eso.

int hc=array.Length; 
for(int i=0;i<array.Length;++i) 
{ 
    hc=unchecked(hc*314159 +array[i]); 
} 
return hc; 

Si necesita algo más sofisticado, look here.

+10

Se ve bien, pero 314159 podría ser un poco grande. Un número como 17 o 31 también lo haría muy bien. Y: 'hc = desmarcado (hc * SHIFTVAL + matriz [i]);' para que sea independiente de la configuración del compilador. –

+1

Sí, se puede mejorar eso seguramente de muchas maneras diferentes, recomendó su comentario. –

+0

los puntos más finos de hecho no son relevantes, pero yo recomendaría fuertemente el operador 'desmarcado()'. –

0

Creo que elegir un buen algoritmo hash tendría que basarse en la distribución (en un sentido de probabilidad) de los valores enteros.

Tenga una mirada en Wikipedia para obtener una lista de algoritmos

1

Cualquier CRC (o incluso XOR) debería estar bien.

+2

El XOR nunca se movería fuera de la ventana -/+ 1000 –

+0

@Henk Holterman: Lo siento, no lo entiendo. Aún tendrá 10 bits de CRC válido si los valores son limitados. Editar: En realidad, el resto de los bits se voltean dependiendo del signo. – leppie

+1

CRC está bien, pero exagerado, simplemente XOR-ing los valores (sin desplazamiento) no está bien. –

2

Para una matriz de valores generalmente entre -1000 y 1000, probablemente usar algo como esto:

static int GetHashCode(int[] values) 
{ 
    int result = 0; 
    int shift = 0; 
    for (int i = 0; i < values.Length; i++) 
    { 
     shift = (shift + 11) % 21; 
     result ^= (values[i]+1024) << shift; 
    } 
    return result; 
} 
+2

FYI, elegí el número 11 porque 11 bits es lo que es necesario para almacenar un rango de 2048 valores distintos (-1000 a +1000 es 2000, que está cerca). Elegí el número 21 porque el entero de 32 bits menos 11 bits es igual a 21 bits. Al desplazar a la izquierda 21 bits, dejará 11 bits para contener un valor de 0 a 2048. – BlueMonkMN

3

Usted puede utilizar CRC32 checksum. Aquí está el código:

[CLSCompliant(false)] 
public class Crc32 { 
    uint[] table = new uint[256]; 
    uint[] Table { get { return table; } } 

    public Crc32() { 
     MakeCrcTable(); 
    } 
    void MakeCrcTable() { 
     for (uint n = 0; n < 256; n++) { 
      uint value = n; 
      for (int i = 0; i < 8; i++) { 
       if ((value & 1) != 0) 
        value = 0xedb88320^(value >> 1); 
       else 
        value = value >> 1; 
      } 
      Table[n] = value; 
     } 
    } 
    public uint UpdateCrc(uint crc, byte[] buffer, int length) { 
     uint result = crc; 
     for (int n = 0; n < length; n++) { 
      result = Table[(result^buffer[n]) & 0xff]^(result >> 8); 
     } 
     return result; 
    } 
    public uint Calculate(Stream stream) { 
     long pos = stream.Position; 
     const int size = 0x32000; 
     byte[] buf = new byte[size]; 
     int bytes = 0; 
     uint result = 0xffffffff; 
     do { 
      bytes = stream.Read(buf, 0, size); 
      result = UpdateCrc(result, buf, bytes); 
     } 
     while (bytes == size); 
     stream.Position = pos; 
     return ~result; 
    } 
} 
+5

Parece demasiado complejo para una matriz de ~ 30 enteros de -1000 a 1000. Requiere convertir primero la matriz de enteros en una matriz de bytes o una secuencia porque no hay ninguna función que acepte una matriz de enteros como entrada, ¿verdad? – BlueMonkMN

+0

Es fácil convertir cada int a byte []: int valor = 0; byte [] bytes = BitConverter.GetBytes (valor); Estos bytes se pueden usar para calcular suma de comprobación en lugar de bytes leídos de la secuencia. – osprey

+0

Sí, pero descuidó el hecho de que tiene que convertir toda la matriz en bytes. Eso también es fácil, pero aún así termina siendo una sobrecarga significativa en la complejidad del código y en el tiempo de ejecución en relación con una solución específicamente dirigida al hashing de una matriz de enteros directamente. – BlueMonkMN

0

Puede tomar un enfoque diferente y utilizar un diccionario recursivo para cada valor en su matriz int. De esta manera, puede dejar .net para hacer hashing de tipo primitivo.

internal class DictionaryEntry<TKey, TValue> 
{ 
    public Dictionary<TKey, DictionaryEntry<TKey, TValue>> Children { get; private set; } 
    public TValue Value { get; private set; } 
    public bool HasValue { get; private set; } 

    public void SetValue(TValue value) 
    { 
     Value = value; 
     HasValue = true; 
    } 

    public DictionaryEntry() 
    { 
     Children = new Dictionary<TKey, DictionaryEntry<TKey, TValue>>(); 
    } 
} 

internal class KeyStackDictionary<TKey, TValue> 
{ 
    // Helper dictionary to work with a stack of keys 
    // Usage: 
    // var dict = new KeyStackDictionary<int, string>(); 
    // int[] keyStack = new int[] {23, 43, 54}; 
    // dict.SetValue(keyStack, "foo"); 
    // string value; 
    // if (dict.GetValue(keyStack, out value)) 
    // { 
    // } 

    private DictionaryEntry<TKey, TValue> _dict; 

    public KeyStackDictionary() 
    { 
     _dict = new DictionaryEntry<TKey, TValue>(); 
    } 

    public void SetValue(TKey[] keyStack, TValue value) 
    { 
     DictionaryEntry<TKey, TValue> dict = _dict; 

     for (int i = 0; i < keyStack.Length; i++) 
     { 
      TKey key = keyStack[i]; 
      if (dict.Children.ContainsKey(key)) 
      { 
       dict = dict.Children[key]; 
      } 
      else 
      { 
       var child = new DictionaryEntry<TKey, TValue>(); 
       dict.Children.Add(key, child); 
       dict = child; 
      } 

      if (i == keyStack.Length - 1) 
      { 
       dict.SetValue(value); 
      } 
     } 
    } 

    // returns false if the value is not found using the key stack 
    public bool GetValue(TKey[] keyStack, out TValue value) 
    { 
     DictionaryEntry<TKey, TValue> dict = _dict; 

     for (int i = 0; i < keyStack.Length; i++) 
     { 
      TKey key = keyStack[i]; 

      if (dict.Children.ContainsKey(key)) 
      { 
       dict = dict.Children[key]; 
      } 
      else 
      { 
       break; 
      } 

      if (i == keyStack.Length - 1 && dict.HasValue) 
      { 
       value = dict.Value; 
       return true; 
      } 
     } 

     value = default(TValue); 
     return false; 
    } 
} 
Cuestiones relacionadas