10

在 .NET 中编写函数以基于种子生成 GUID 以便我对其唯一性更有信心的最简单方法是什么?

string GenerateSeededGuid(int seed) { /* code here */ }

理想情况下,种子将来自CryptGenRandom,它描述了它的随机数生成如下:

此函数生成的数据是加密随机的。它比典型的随机数生成器(例如 C 编译器附带的随机数生成器)生成的数据要随机得多。

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

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

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

4

4 回答 4

21

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

根据对有限范围的定义——128位减去6 个版本控制位,因此v4 的唯一性为 122 位——只有这么多尽管数量巨大! 天文数字!)“唯一”标识符。

由于鸽笼原理,鸽笼只有这么多。如果鸽子最终继续繁殖,那么每只鸽子都不会有足够的洞。由于生日悖论,假设完全随机,两只鸽子会在它们都被填满之前尝试争夺同一个鸽子洞。因为没有 Master Pigeonhole List 1,所以这是无法避免的。此外,并非所有动物都是鸽子3

虽然无法保证将使用哪个GUID 生成器,但 .NET 使用底层操作系统调用,它是自 Windows 2k 以来的 GUIDv4(又名随机 UUID)生成器。据我所知 - 或关心,真的 - 这是一个随机的,因为它得到了这样一个目的。它经过十多年的严格审查,从未被取代。


来自维基百科:

.. 仅在接下来的 100 年每秒生成 10 亿个 UUID 之后,仅创建一个副本的概率约为 50%。如果地球上每个人都拥有 6 亿个 UUID,那么出现重复的概率约为 50%。

1虽然仍然存在一组有限的 Pigeonholes,但 UUIDv1(又名MAC UUID)——假设是唯一的时空——保证生成确定性唯一的数字(在给定的机器上每秒生成一些“相对较小”的理论最大 UUID 数)。生活在不同平行维度的不同鸽群——太棒了!

2 Twitter在其自己的分布式唯一 ID 方案中使用并行维度的雪花。

3兔子喜欢住在洞穴,而不是鸽子洞。GUID的使用还充当隐式并行分区。只有当重复的 GUID用于相同目的时,才会出现与冲突相关的问题。想想有多少重复的自增数据库主键!

于 2012-11-02T03:18:34.697 回答
18

您在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);
}
于 2012-11-02T02:22:27.947 回答
0
    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

于 2019-12-26T14:33:48.967 回答
-1

这有点旧,但不需要随机生成器。但是是的,这对于测试目的很有用,但不适用于一般用途

    public static Guid GenerateSeededGuid<T>(T value)
    {
        byte[] bytes = new byte[16];
        BitConverter.GetBytes(value.GetHashCode()).CopyTo(bytes, 0);
        return new Guid(bytes);
    }
于 2018-11-13T14:14:55.867 回答