基于GUID生成唯一整数的方法是什么?

82

是否可能从GUID生成(高度可能的)唯一整数?

int i = Guid.NewGuid().GetHashCode();

int j = BitConverter.ToInt32(Guid.NewGuid().ToByteArray(), 0);

哪个更好?


1
为什么不直接使用 GUID 代替你原来使用 32 位整数的目的呢? - JAB
11
翻译:相对于哪个领域是独特的?我刚刚编写并执行了一个程序,它生成了所有32位整数,所以你不可能生成我没有的数! - Eric Lippert
11
如果你可以忘记 Guid,那么获得“唯一”(100%)的最佳方法就是在某个地方使用一个整数变量并进行 int++ 操作。你肯定会获得 2^32 个唯一值,而这已经是相当大的空间了。 - nawfal
回答这个问题需要一些上下文。重要的是,正如Microsoft文档所强调的那样,我们不应该存储GetHashCode的结果或期望它们在跨域、进程和平台时是唯一的。在这个意义上,它是不确定的。 - DvS
10个回答

66

Eric Lippert写了一篇非常有趣(像往常一样)的文章,讨论了哈希碰撞的概率

你应该读完整篇文章,但他最后用了这个非常形象的图表作为结论:

Probability of hash collisions

针对你的具体问题,我也会选择使用GetHashCode,因为无论如何都无法避免碰撞。


34
不要这样做。 - richard

24

GetHashCode 函数专门设计用于创建分布均匀且碰撞概率较低的整数范围,因此对于此使用情况,这可能是您能做到的最好的选择。

但是,正如您所知道的那样,将128位信息哈希成32位信息会丢失大量数据,因此如果您拥有足够多的 GUIDs,几乎肯定会发生冲突。


6
如果你把“足够大”定义为大于2^32,那么可以去掉“几乎”的修饰词。在这种情况下,碰撞是肯定发生的。 - phoog

21

5
这个可以工作,尽管需要注意生成的数字也会是负数。请参考这个示例:https://dotnetfiddle.net/B97Fhv。 - Sudhanshu Mishra
1
使用:Console.WriteLine(huge.ToString().Replace("-","").Remove(30)); 来解决 @dotnetguy 的问题。 - Amin Ghaderi
3
终于有一个实际的答案。 - deetz
我感到有必要说,在LinqPad中,要将一个对象输出到窗口,你需要调用“.Dump()”扩展方法。 - pwilcox

17

以下是最简单的方法:

Guid guid = Guid.NewGuid();
Random random = new Random();
int i = random.Next();
你会注意到这里实际上没有使用guid,主要是因为没有使用它的意义。Microsoft的GUID算法不再使用计算机的MAC地址,而是使用伪随机生成器(基于时间值)生成GUID,因此如果您想要一个随机整数,最好使用Random类来生成。 更新: 实际上,使用GUID生成int可能比仅使用Random更糟糕(在这种情况下,“更糟糕”指的是这将更有可能生成冲突)。这是因为GUID中并不是所有的128位都是随机的。理想情况下,您希望从哈希函数中排除非变化位,尽管直接生成随机数会更容易,就像我之前提到的那样。 :)

12
或许OP想从GUID中获取整数的原因很充分。 - LukeH
4
楼主可能认为从GUID中派生出一个整数有很好的理由(即为了确保int的唯一性),但实际上并没有这样的必要。 - MusiGenesis
1
在João Angelo的回答链接中显示,32位整数的碰撞机率较高。 9300个随机的32位整数有1%的碰撞机率,77000个则有50%的碰撞机率。因此,依靠32位随机数来实现唯一性是自相矛盾的。 - Justin
4
为了易读性而言:79,228,162,514,264,337,593,543,950,336。 - phoog
4
仅提供我的两分意见……我们所有的密钥都是GUID,而我们调用的一个Web服务要求密钥为整数。因此,这是从GUID转换为整数的原因。 - SASS_Shooter
显示剩余3条评论

12

如果您想突破2^32的限制,请尝试此方法:

/// <summary>
/// Generate a BigInteger given a Guid. Returns a number from 0 to 2^128
/// 0 to 340,282,366,920,938,463,463,374,607,431,768,211,456
/// </summary>
    public BigInteger GuidToBigInteger(Guid guid)
    {
        BigInteger l_retval = 0;
        byte[] ba = guid.ToByteArray();
        int i = ba.Count();
        foreach (byte b in ba)
        {
            l_retval += b * BigInteger.Pow(256, --i);
        }
        return l_retval;
    }

在你经历碰撞之前,宇宙将会衰变成一个冷漠黑暗的广袤空间。


是的,但有时宇宙的崩溃速度比人们预期的要快。例如,当您错误地将int32视为可以放置int64的位置时-宇宙是无限的(潜在的)-int32不是。而Guid中的字节没有按从左到右的重要性排序,这就是为什么即使宇宙不会崩溃,这仍然是错误的原因。 - Stefan Steiger

10

我有一个需求,需要多个控制台应用程序获取唯一的整数ID。它用于标识实例并在启动时分配。因为.exe是手动启动的,所以我选择了使用启动时间的tick解决方案。

我的想法是,用户几乎不可能在同一毫秒内启动两个.exe。这种行为是确定性的:如果发生冲突,您就知道问题是两个实例同时启动了。依赖于哈希码、GUID或随机数的方法可能会以不可预测的方式失败。

我将日期设置为0001-01-01,加上当前时间,将ticks除以10000(因为我不设置微秒),以获得一个小到足以放入整数的数字。

 var now = DateTime.Now;
 var zeroDate = DateTime.MinValue.AddHours(now.Hour).AddMinutes(now.Minute).AddSeconds(now.Second).AddMilliseconds(now.Millisecond);
 int uniqueId = (int)(zeroDate.Ticks / 10000);

编辑:有一些注意事项。为了让碰撞变得不太可能,请确保:

  • 手动启动实例(间隔超过一毫秒);
  • ID在启动时每个实例只生成一次;
  • ID必须仅对当前正在运行的其他实例具有唯一性;
  • 只需要少量ID。

这对于整数值ID非常适用 - 我所做的一个更改是使用DateTime.UtcNow而不是DateTime.Now,它使用机器的本地时间。 - neoRiley

6
由于 GUID 空间比 32 位整数的数量要大,如果有足够多的 GUID,就保证会发生冲突。但是,只要你理解这一点并准备好处理即便非常罕见的冲突,GetHashCode() 就是专门为此设计的,应当优先考虑使用。

2

1
在一个静态类中,保持一个静态的常量整数,然后在每次访问之前将其加1(使用公共的get属性)。这将确保您在获得非唯一值之前循环整个int范围。
    /// <summary>
    /// The command id to use. This is a thread-safe id, that is unique over the lifetime of the process. It changes
    /// at each access.
    /// </summary>
    internal static int NextCommandId
    {
        get
        {
            return _nextCommandId++;
        }
    }       
    private static int _nextCommandId = 0;

这将在运行的进程内生成一个唯一的整数值。由于您没有明确定义整数应该有多独特,因此这可能会适合。


4
直到下一次您打开应用程序,或者您的Web服务器重新启动,或者您正在运行分布式应用程序(多个服务器)。/ 显而易见(我相信您已经知道了这一点)。 - drzaus
@drzaus 是的,正如所述,它仅在运行进程内是唯一的。 - Marcel

1

这里有一个最简单的解决方案,只需在 Guid 上调用 GetHashCode()。请注意,guid 是 128 位整数,而 int 是 32 位。因此不能保证其唯一性。但对于大多数实现来说,它可能在统计上足够好。

    public override bool Equals(object obj)
    {
        if (obj is IBase)
            return ((IBase)obj).Id == this.Id;

        return base.Equals(obj);
    }
    public override int GetHashCode()
    {
        if (this.Id == Guid.Empty)
            return base.GetHashCode();

        return this.Id.GetHashCode();
    }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接