10

キーが 10 進数である大きな辞書がありますが、System.Decimal の GetHashCode() はひどく悪いです。私の推測を証明するために、100.000 個の隣接小数を使用して for ループを実行し、分布を確認しました。100.000 の異なる 10 進数は、2 つ (2 つ!!!) の異なるハッシュコードのみを使用しました。

10 進数は 16 バイトで表されます。ギドそっくり!しかし、Guid の GetHashCode() ディストリビューションはかなり優れています。できるだけ安くC#で小数をGuidに変換するにはどうすればよいですか? アンセーフコードもOK!


編集: テストが要求されたので、コードは次のとおりです。

decimal d = 96000000000000000000m;
Dictionary<int, int> hashcount = new Dictionary<int, int>();
int length = 100000;
for (int i = 0; i < length; i++)
{
    int hashcode = d.GetHashCode();
    int n;
    if (hashcount.TryGetValue(hashcode, out n))
    {
        hashcount[hashcode] = n + 1;
    }
    else
    {
        hashcount.Add(hashcode, 1);
    }
    d++;
}

Console.WriteLine(hashcount.Count);

これは 7 を出力します。2 になった最初の 10 進数を覚えていません。

4

4 に答える 4

23

非常にハッキーなソリューション(ただし、おそらく最速)

public static class Utils
{
    [StructLayout(LayoutKind.Explicit)]
    struct DecimalGuidConverter
    {
        [FieldOffset(0)]
        public decimal Decimal;
        [FieldOffset(0)]
        public Guid Guid;
    }

    private static DecimalGuidConverter _converter;
    public static Guid DecimalToGuid(decimal dec)
    {
        _converter.Decimal = dec;
        return _converter.Guid;
    }
    public static decimal GuidToDecimal(Guid guid)
    {
        _converter.Guid = guid;
        return _converter.Decimal;
    }
}

// Prints 000e0000-0000-0000-8324-6ae7b91d0100
Console.WriteLine(Utils.DecimalToGuid((decimal) Math.PI));

// Prints 00000000-0000-0000-1821-000000000000
Console.WriteLine(Utils.DecimalToGuid(8472m));

// Prints 8472
Console.WriteLine(Utils.GuidToDecimal(Guid.Parse("00000000-0000-0000-1821-000000000000")));
于 2010-08-25T07:54:01.273 に答える
5

別のハッシュ アルゴリズムを取得しようとしているだけの場合は、Guid に変換する必要はありません。このようなもの:

public int GetDecimalHashCode(decimal value)
{
    int[] bits = decimal.GetBits(value);
    int hash = 17;
    foreach (int x in bits)
    {
        hash = hash * 31 + x;
    }
    return hash;
}

(もちろん、必要に応じて別のアルゴリズムに置き換えてください。)

確かに、これには依然として配列の作成が含まれますが、これは理想的ではありません。本当にGuid を作成したい場合は、上記のコードを使用してビットを取得し、配列から適切な値を渡す長いGuidコンストラクターを使用できます。

decimalただし、ハッシュコードが非常に悪いのではないかと少し疑っています。そのためのサンプルコードはありますか?

于 2010-08-25T07:59:11.483 に答える
0

10 進数値をバイト配列に変換し、そこから GUID を作成します。

public static byte[] DecimalToByteArray (decimal src) 
{
    using (MemoryStream stream = new MemoryStream()) 
    {
        using (BinaryWriter writer = new BinaryWriter(stream))
        {
            writer.Write(src);
            return stream.ToArray();
        }
    }
}

Decimal myDecimal = 1234.5678M;
Guid guid = new Guid(DecimalToByteArray(myDecimal));
于 2010-08-25T07:50:40.560 に答える
0

GUID の配布は、一意であることを意図しているため、適切です...

これに使用される数値の範囲は何ですか? のデフォルトのGetHashcode()実装でDecimalは、特定の範囲の値のみが考慮される場合があります。

于 2010-08-25T07:51:05.083 に答える