如何基于GUID生成唯一的整数

时间:2010-05-27 11:37:36

标签: c#

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

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

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

哪一个更好?

9 个答案:

答案 0 :(得分:44)

Eric Lippert发表了关于probability of hash collisions的一篇非常有趣(一如既往)的帖子。

你应该阅读所有内容,但他总结了这个非常具有说明性的图表:

Probability of hash collisions

与您的具体问题相关,我也会选择GetHashCode,因为碰撞无论如何都是不可避免的。

答案 1 :(得分:17)

GetHashCode函数专门用于创建一个分布均匀的整数范围,具有较低的碰撞概率,因此对于这个用例可能是您可以做的最好的。

但是,我确信您已经知道,将128位信息散列到32位信息中会丢弃大量数据,因此如果您拥有足够多的GUID,几乎肯定会发生冲突。 / p>

答案 2 :(得分:16)

这是最简单的方法:

Guid guid = Guid.NewGuid();
Random random = new Random();
int i = random.Next();

你会注意到guid实际上并没有在这里使用,主要是因为使用它没有意义。微软的GUID算法不再使用计算机的MAC地址 - GUID实际上是使用伪随机生成器(基于时间值)生成的,所以如果你想要一个随机整数,那么使用Random类更有意义对此。

更新:实际上,使用GUID生成int可能比仅使用Random更糟糕(“更差”,因为这将是更有可能生成冲突)。这是因为并非GUID中的所有128位都是随机的。理想情况下,您可能希望从散列函数中排除非变量位,尽管生成随机数会容易得多,正如我之前提到的那样。 :)

答案 3 :(得分:11)

GUID是一个128位整数(它只是十六进制而不是十进制)。使用.NET 4,使用http://msdn.microsoft.com/en-us/library/dd268285%28v=VS.100%29.aspx,如下所示:

// Turn a GUID into a string and strip out the '-' characters.
BigInteger huge = BigInteger.Parse(modifiedGuidString, NumberStyles.AllowHexSpecifier)

如果您没有.NET 4,可以查看IntXSolver Foundation

答案 4 :(得分:5)

因为GUID空间大于32位整数的数量,所以如果您有足够的GUID,则可以保证发生冲突。鉴于你理解并准备好处理碰撞,无论多么罕见,GetHashCode()都是为了这个目的而设计的,应该是首选。

答案 5 :(得分:5)

如果您希望突破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;
    }

在你遇到碰撞之前,宇宙会衰变到寒冷和黑暗的地方。

答案 6 :(得分:2)

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

我的理由是,用户几乎不可能在相同的毫秒内启动两个.exe。这种行为是确定性的:如果你有碰撞,你知道问题是两个实例同时启动。取决于哈希码,GUID或随机数的方法可能会以不可预测的方式失败。

我将日期设置为0001-01-01,添加当前时间并将刻度除以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

答案 7 :(得分:1)

在静态类中,保留一个静态const整数,然后在每次访问之前为其添加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;

这将在正在运行的进程中生成唯一的整数值。由于您没有明确定义整数的唯一性,因此可能适合。

答案 8 :(得分:1)

也许不是整数而是小的唯一键,无论如何短于guids:

http://www.codeproject.com/Articles/14403/Generating-Unique-Keys-in-Net