如何有效地创建一个包含不同 UUID 的大列表?
How to create a big list of different UUIDs, efficiently?
我想为活动生成门票。我需要生成很多,因此我决定将票号作为 UUID。问题是如何生成一个大的 UUID 列表并且它是不同的。
我知道检查现有列表中生成的每个新 UUID 的简单方法,但这对性能不是很友好。 :)
我正在使用带有 UUID v4 的 NodeJS。
谢谢!
您可以创建一个空对象,每次生成 UUID 时,您都会向该对象添加一个属性,其中键是生成的 UUID。当您将生成另一个 UUID 时,您只需检查对象属性是否为 undefined
或不查看它是否已被使用。
const uuids = [];
let uuidUsed = {};
const size = 10;
for (let i = 0; i < size; i++) {
let uuid = uuidv4();
while (uuidUsed[uuid] !== undefined) {
uuid = uuidv4();
}
uuidUsed[uuid] = true;
}
您可以使用自制的 UUID 函数,它保证是 [0...2128) 范围内唯一的伪随机整数。下面是基于 Linear Contguential Generator. Constants are taken from here or here 的一个。您只需要保留上一个 number/UUID 就可以生成下一个,无需检查,因为它只会在 2128.
的完整周期后重复
代码依赖 BigInt,使用节点 v12 测试
const a = 199967246047888932297834045878657099405n; // should satisfy a % 8n = 5n
const c = 1n; // should be odd
const m = (1n << 128n);
const mask = m - 1n;
function LCG128(state) {
return (BigInt(state) * a + c) & mask; // same as % m
}
q = 7654321n; // seed
q = LCG128(q);
q.toString(16); // first UUID
q = LCG128(q);
q.toString(16); // second UUID
q = LCG128(q);
q.toString(16); // third UUID
更新
只是为了在手头的问题上更具哲学性:
- 您可以将 UUID4 视为黑盒并相信它 - 这是@ChrisWhite 提出的建议
- 您可以将 UUID4 视为黑匣子并且不信任它 - 这是您建议检查列表或@KevinPastor 回答的内容
- 制作你自己的透明盒子,它产生的数字在适当的范围内并且是独一无二的 - 这是我的建议
LCG 方法的美妙之处在于,如果有良好的乘数和进位,它可以唯一且可逆地将范围 [0...2128) 映射到自身(它可以为64 位数字,具有不同的 a
、c
或 32 位数字等等)。您甚至可以使用从 0 开始到 2128-1 的计数器作为输入,它会在相同范围内生成不可重复的数字,填充整个 [0...2128).所以你知道,如果你将它与以前的 uuid 链接起来,或者使用计数器,则冲突的可能性为 0。
这里是 446,538 IPs 的列表,格式如下: |编号 |日期 |姓名 | uuid | ip |
我想为活动生成门票。我需要生成很多,因此我决定将票号作为 UUID。问题是如何生成一个大的 UUID 列表并且它是不同的。
我知道检查现有列表中生成的每个新 UUID 的简单方法,但这对性能不是很友好。 :)
我正在使用带有 UUID v4 的 NodeJS。
谢谢!
您可以创建一个空对象,每次生成 UUID 时,您都会向该对象添加一个属性,其中键是生成的 UUID。当您将生成另一个 UUID 时,您只需检查对象属性是否为 undefined
或不查看它是否已被使用。
const uuids = [];
let uuidUsed = {};
const size = 10;
for (let i = 0; i < size; i++) {
let uuid = uuidv4();
while (uuidUsed[uuid] !== undefined) {
uuid = uuidv4();
}
uuidUsed[uuid] = true;
}
您可以使用自制的 UUID 函数,它保证是 [0...2128) 范围内唯一的伪随机整数。下面是基于 Linear Contguential Generator. Constants are taken from here or here 的一个。您只需要保留上一个 number/UUID 就可以生成下一个,无需检查,因为它只会在 2128.
的完整周期后重复代码依赖 BigInt,使用节点 v12 测试
const a = 199967246047888932297834045878657099405n; // should satisfy a % 8n = 5n
const c = 1n; // should be odd
const m = (1n << 128n);
const mask = m - 1n;
function LCG128(state) {
return (BigInt(state) * a + c) & mask; // same as % m
}
q = 7654321n; // seed
q = LCG128(q);
q.toString(16); // first UUID
q = LCG128(q);
q.toString(16); // second UUID
q = LCG128(q);
q.toString(16); // third UUID
更新
只是为了在手头的问题上更具哲学性:
- 您可以将 UUID4 视为黑盒并相信它 - 这是@ChrisWhite 提出的建议
- 您可以将 UUID4 视为黑匣子并且不信任它 - 这是您建议检查列表或@KevinPastor 回答的内容
- 制作你自己的透明盒子,它产生的数字在适当的范围内并且是独一无二的 - 这是我的建议
LCG 方法的美妙之处在于,如果有良好的乘数和进位,它可以唯一且可逆地将范围 [0...2128) 映射到自身(它可以为64 位数字,具有不同的 a
、c
或 32 位数字等等)。您甚至可以使用从 0 开始到 2128-1 的计数器作为输入,它会在相同范围内生成不可重复的数字,填充整个 [0...2128).所以你知道,如果你将它与以前的 uuid 链接起来,或者使用计数器,则冲突的可能性为 0。
这里是 446,538 IPs 的列表,格式如下: |编号 |日期 |姓名 | uuid | ip |