如何种子化GUID生成?

12

在.NET中编写一个函数以基于种子生成GUID的最简单方法是什么,以便我可以更加确信它的唯一性?

string GenerateSeededGuid(int seed) { /* code here */ }
最理想的情况是种子来自于CryptGenRandom。该函数描述其随机数生成如下:
引用:

此函数生成的数据是密码学随机的,比您的C编译器附带的典型随机数生成器生成的数据要随机得多。

此函数常用于生成随机的初始化向量和盐值。

软件随机数生成器的工作原理基本相同。它们从一个称为种子的随机数开始,然后使用算法根据它生成一系列伪随机的位。这个过程中最困难的部分是获得真正随机的种子。这通常基于用户输入延迟或来自一个或多个硬件组件的抖动。

使用Microsoft CSP时,CryptGenRandom使用其他安全组件所使用的同一随机数生成器。这使得许多进程可以为系统范围内的种子做出贡献。CryptoAPI会将每个用户的中间随机种子存储在内部。为了形成随机数生成器的种子,调用应用程序提供可能具有的位——例如鼠标或键盘定时输入——然后将其与存储的种子和各种系统数据和用户数据结合起来,例如进程ID和线程ID,系统时钟、系统时间、系统计数器、内存状态、空闲磁盘群集和哈希用户环境块。这个结果用于种子伪随机数生成器(PRNG)。如果应用程序可以访问良好的随机源,则可以在调用CryptGenRandom之前使用一些随机数据填充pbBuffer缓冲区。CSP然后使用此数据进一步随机化其内部种子。在调用CryptGenRandom之前初始化pbBuffer缓冲区的步骤可以省略。


2
生成一个随机数。转换为GUID ..但我原以为NewGuid已经创建了一个UUIDv4(即:随机)。请参阅http://en.wikipedia.org/wiki/Globally_unique_identifier(注意算法部分中提到的位设置)来验证NewGuid返回的内容。 - user166390
2
这是你今天早些时候问过并删除的同样问题。你混淆了随机和唯一。GUID的目的是唯一而不是随机 - 它们完全不同。GUID甚至不在System.Security.Cryptography命名空间中。GUID基于MAC实现唯一性。如果你想要种子,那么请在密码学命名空间中使用适当的算法。 - paparazzo
1
@CJ7 你打算如何同时执行多次NewGuid? - paparazzo
1
@Blam:微软并没有声称它是独一无二的。 - CJ7
1
@CJ7:RFC4122规定v1 UTC“时间戳”以100纳秒间隔为单位。如果在同一100纳秒窗口内生成两个v1 GUID,则算法将停顿最多100纳秒,以获取不同的时间戳。这些都包含在RFC4122中。 - Stephen Cleary
显示剩余17条评论
4个回答

22

tldr; 使用Guid.NewGuid而不是试图发明另一种“更随机”的方法。(我能想到的唯一理由是从种子创建UUIDvX是当需要可预测,可重置序列时。但是,GUID可能也不是最佳方法2。)

根据有限范围的定义- 128位减去 6个版本位,因此122位用于v4的唯一性-存在只有这么多虽然非常巨大!天文数字级别的大!)“独特”的标识符。

由于鸽巢原理, 只有有限的鸽巢。如果鸽子不断繁殖,最终每只鸽子都找不到足够的巢。由于生日悖论,假设完全随机,两只鸽子将在所有巢都填满之前争夺同一个巢。因为没有主要的鸽巢列表1,这是无法预防的。此外,并非所有动物都是鸽子3
虽然无法保证使用哪个GUID生成器, 但.NET使用底层操作系统调用,即GUIDv4(又称随机UUID)生成器自Windows 2k以来。据我所知 - 或者说我真的很在意 - 对于这样的目的而言,这是最好的随机数。它经过了十多年的充分验证,并且没有被替换。

来自维基百科:

.. 只有在未来100年内每秒生成10亿个UUID时,才会有大约50%的概率仅创建一个重复项。 如果每个地球上的人都拥有6亿个UUID,则一个重复项的概率约为50%。

1 尽管还有一组有限的信鸽洞,UUIDv1(也称为 MAC UUID ) - 假设时间和空间唯一 - 保证以确定性方式生成唯一数字(在给定机器上每秒生成的UUID数量理论上存在“相对较小”的最大值)。生活在不同平行维度中的不同信鸽群体-真棒!

2 Twitter在其自己的分布式唯一ID方案中使用Snowflakes

3 兔子喜欢住在洞穴里,而不是鸽笼里。使用GUID还可以作为一种隐式的并行分区。只有当重复的GUID被用于相同的目的时,才会出现冲突相关的问题。就像有多少个重复的自增数据库主键一样!


1
Guids并不保证是随机的,只保证是唯一的。 - Enigmativity

21

在你的GenerateSeededGuid方法中,你只需要创建一个128位的随机数并将其转换为Guid。 像这样:

public Guid GenerateSeededGuid(int seed)
{
  var r = new Random(seed);
  var guid = new byte[16];
  r.NextBytes(guid);

  return new Guid(guid);
}

12
对于“从种子生成”的方法来说,虽然这种方法可能会产生无效的UUID(错误的版本位),而且由于种子范围有限,还会导致更多的冲突。GUID被设计为“具有非常低的冲突概率”,因此从本质上讲,没有理由相信一种自定义的随机生成方法会比在Windows 2K+中找到的GUIDv4更好或更随机。 - user166390
2
@pst:我同意。就个人而言,我认为没有理由不使用 NewGuid - Sani Huttunen
3
整个文档的单元测试 - 这些受益于这种技术。谢谢! - George Mauer
Guids不能保证是随机的,只能保证唯一。 - Enigmativity

2
    public static Guid SeededGuid(int seed, Random random = null)
    {
        random ??= new Random(seed);
        return Guid.Parse(string.Format("{0:X4}{1:X4}-{2:X4}-{3:X4}-{4:X4}-{5:X4}{6:X4}{7:X4}",
            random.Next(0, 0xffff), random.Next(0, 0xffff),
            random.Next(0, 0xffff),
            random.Next(0, 0xffff) | 0x4000,
            random.Next(0, 0x3fff) | 0x8000,
            random.Next(0, 0xffff), random.Next(0, 0xffff), random.Next(0, 0xffff)));
    }

    //Example 1
    SeededGuid("Test".GetHashCode());
    SeededGuid("Test".GetHashCode());

    //Example 2
    var random = new Random("Test".GetHashCode());
    SeededGuid("Test".GetHashCode(), random);
    SeededGuid("Test".GetHashCode(), random);

此方法基于php v4 uui https://www.php.net/manual/en/function.uniqid.php#94959


1

这有点过时了,但不需要随机生成器。但是对于测试目的来说确实很有用,但不适用于一般用途。

    public static Guid GenerateSeededGuid<T>(T value)
    {
        byte[] bytes = new byte[16];
        BitConverter.GetBytes(value.GetHashCode()).CopyTo(bytes, 0);
        return new Guid(bytes);
    }

GetHashCode并不总是针对相同的输入返回相同的值。例如,它可能会随着应用程序的重新启动而发生变化。 - EKS

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