如何为一个实体创建一个唯一的7位代码?

10
当用户在我的系统中添加新项目时,我希望为该项生成一个唯一的、非递增的伪随机7位代码。创建的项目数量只有数千个(<10,000)。
因为它需要是唯一的,没有两个项目会有相同的信息,我可以使用哈希,但它需要是他们可以与其他人分享的代码-因此是7位数字。
我的最初想法只是循环生成一个随机数,检查它是否已经被使用,如果是,那么重复这个过程。考虑到碰撞的可能性较低,我认为这是一个合理但不太好的解决方案。 这个问题的回答建议生成所有未使用数字的列表并对其进行洗牌。我可能可以在数据库中保留这样的列表,但我们正在谈论的是相对不频繁的事情,需要1000万条目。
有没有更好的方法?

12
static int i=9999999;int get_non_increasing_unique_code(void){return i--;}静态整型变量i被赋初值为9999999。调用get_non_increasing_unique_code函数将返回i的当前值,然后i减1。返回的值是一个不递增的独特代码(即每次调用都会产生唯一的代码)。 - kennytm
我希望它们是伪随机的。如果有人连续创建两个项目,它们应该具有不相关的代码。超过2个,就不应该有可辨别的模式。 - Damovisa
回应您对harryovers的评论,这基本上是一个人们可以加入的“群体”:它可以有一个名称而不是一个数字吗?人们记得7位数没问题,但他们真的很擅长记住名字。 - T.J. Crowder
关于名称:您只需要在创建群组时让用户想出一个名称。根据您的用户群,您可能需要适度管理名称。除此之外,这很简单。 - T.J. Crowder
名称不会公开,所以我并不在乎它们叫什么 :). 我还得检查一下这个名称是否已经存在。 - Damovisa
显示剩余3条评论
11个回答

15

选择一个七位数的质数,称之为A,再选一个大的质数,称之为B,然后

int nth_unique_7_digit_code(int n) {
    return (n * B) % A;
}

由此生成的所有唯一代码的数量将为A

如果您想更加“安全”,可以执行pow(some_prime_number, n) % A,即

static int current_code = B;
int get_next_unique_code() {
   current_code = (B * current_code) % A;
   return current_code;
}

我不完全确定这在做什么,但它看起来有点类似于RSA算法的重要部分。我想。 - rmeador
4
这是基础数论,它有效的原因是A是质数且A和B的最大公约数=1。这保证了没有重复,并且结果“看起来”随机。 - President James K. Polk
@KennyTM:你的 B 和 (B % A) 是等价的,因此你可以选择更小的 (B % A)。 - President James K. Polk
请注意,如果给定一些连续的数字,您可以轻松地计算出A和B,因此这在任何情况下都不是加密安全的。但是,如果目标只是为了看起来随机,那么它应该按照广告所述工作。 - Rasmus Faber
谢谢!我需要这样的东西。我使用了[node id] + [timestamp] + [the unique code]。所有这些都很好地进行了base58编码,用于我的数据库键值,只有10个字符。对于节点可以生成的密钥数量,我已经覆盖了。非常感谢! - Derick Schoonbee
显示剩余4条评论

5

您可以使用递增的ID,然后将其与一些固定密钥进行异或运算。

const int XORCode = 12345;

private int Encode(int id)
{
    return id^XORCode;
}

private int Decode(int code)
{
    return code^XORCode;
}

我需要进一步调查,但听起来好像可以行得通... 但你仍然可能会得到代码集群,不是吗? - Damovisa
这取决于您的ID和密钥选择。由于您只期望大约10000个项目,因此您应该能够处理数据并查看结果。 - Robin Day

4
坦白说,如果你只想生成几千个七位数代码,而有1000万个不同的代码可用,那么我认为只需随机生成一个并检查冲突即可。在最坏的情况下,第一次命中碰撞的概率约为千分之一,而仅生成新的七位数代码并再次检查冲突的计算工作量要比保留字典或类似解决方案要小得多。如harryovers所建议的,使用GUID而不是七位数代码也肯定可行,但当然GUID会稍微难记一些。

尽管在现实中这种情况不太可能发生,但永远不要忘记,以这种方式生成时的真正最坏情况是无限的。 - Robin Day
@Robin,幸运的是这种机会也是无限小的 :) - Aistina

2

我建议使用GUID而不是7位数字代码,因为它更加唯一,您不必担心生成它们,因为.NET将为您完成此操作。


4
他说人们需要与他人分享这个数字。GUID对于这个目的来说并不是……我们可以说是“理想的”…… - T.J. Crowder
是的,没错。为了更好地理解,这本质上是一个人们可以加入的“群组”。让人们加入a53df3d0-171f-11df-8a39-0800200c9a66群组并不容易。 - Damovisa
@Damovisa:如果有人邀请你加入那个组,你也不会想加入,对吧?;-) 躲开并逃跑 - T.J. Crowder

2

所有“唯一”ID的解决方案都必须在某个数据库中:其中一个包含已使用的ID,或者一个包含空闲ID的数据库。正如您所注意到的那样,带有空闲ID的数据库将非常庞大,因此大多数人使用“已使用的ID”数据库并检查冲突。

话虽如此,一些数据库提供了“随机ID”生成器/序列,它已经以随机顺序返回ID范围内的ID。

这是通过使用一个随机数生成器来创建一个范围内的所有数字,而不重复本身加上保存在某个地方其状态的特性来实现的。因此,您可以运行生成器一次,使用该ID并保存新状态。对于下一次运行,您加载状态并将生成器重置为最后状态以获取下一个随机ID。


谢谢,你当然是对的 - 我必须在某个地方存储那个“唯一”的ID。你有关于数据库随机序列生成器的更多信息吗?我目前正在使用SQL Server Express 2008。 - Damovisa
@Damovisa:请参考SideShowCoder关于LFSRs的回答。 - Aaron Digulla

2

我假设您将拥有一张“生成的”表。在这种情况下,我认为随机选取数字并检查它们是否与数据库匹配不是问题,但我不会逐个执行此操作。生成它们很便宜,与此相对,查询数据库是昂贵的。我会一次生成100或1,000个,然后询问数据库哪些存在。我打赌大部分时间您不必再做一遍。


2
你有不到10,000个项目,所以只需要4位数字来存储所有项目的唯一编号。由于你有7位数字,因此额外有3位数字。
如果将一个4位数的唯一序列号与一个3位数的随机数组合起来,你就会得到独特且随机的编号。每次生成新的ID时,你都需要递增序列号。
你可以按任何顺序或混合它们来附加它们。
序列号=abcd,随机数=ABC
你可以创建以下ID:
- abcdABC - ABCabcd - aAbBcCd
如果只使用一种混合算法,你将拥有看起来随机的唯一编号。

1

我会尝试使用LFSR(线性反馈移位寄存器),代码非常简单,你可以在各处找到示例,例如Wikipedia,虽然它不是加密安全的,但看起来非常随机。此外,由于主要使用移位操作,实现速度也非常快。


0

如果数据库中只有数千个项目,您的原始想法似乎是正确的。在几万个项目的排序(索引)列表中检查值的存在只需要进行几次数据获取和比较。

预先生成列表听起来不是一个好主意,因为您要么会存储比必要更多的数字,要么就必须处理用完它们的情况。


好吧,他只有七个数字 - 无论他是否将它们存储在数据库中,他都会在同样的时间内用完它们。 :-) - T.J. Crowder
根据问题,他不太可能使用完所有一千万个可能的数字。因此,存储全部一千万是浪费的。那么他需要存储多少个呢?十万也是浪费的。一万可能不够。找到适当的平衡是一种权衡,这对我来说并不是一个好计划。我更喜欢那些保证在所有潜在情况下都能良好运行的解决方案。 - Jeffrey L Whitledge

0

具有命中的概率非常低。
例如 - 你有10^4个用户和10^7个可能的ID。
你连续十次选择已使用过的ID的概率现在是10^-30。
这个机会比任何人一生中都要低。


这里不适用。他应该添加检查点击次数的代码,但很少有可能需要尝试多次。 - Luka Rahne

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