我想生成一个短,独特的ID,而不必检查碰撞。
我现在做这样的事情,但我目前生成的ID是随机的,碰撞检测在一个循环是讨厌,会得到昂贵,如果记录的数目显著增长。
通常担心碰撞不是问题,但唯一的ID我要生成一个独特的短串5-8个字符,字母数字,像TinyURL的做。
编辑:我想用5个字符开始了,如果我打了6000万个条目,然后转到6 ..等等等等。
为此,我想我可以使用从用户隐藏的AUTO_INCREMENT值,并用替代目前他们MD5
或一些其他方法来生成从一个唯一的字符串。
生成的字符串不应该出现是线性的,所以只需将auto_incremented ID转换成base 36
[0-9A-Z]是有点过于简单,但功能类似的东西就是我这个打算。
编辑:安全不是一个问题,因为这将不被用于安全信息。 它只是一个快捷方式到一个较长的字符串。 谢谢。
谢谢您的建议和抱歉的延迟。 牙医..
你需要的东西,通过建设是正确的,即置换功能:这是做一个功能的一个对一个,一个整数(你的顺序计数器)到另一个可逆的映射。 一些例子(任意组合也应工作):
- 反相一些位(使用XOR音响,在^ PHP)
- 交换位的地方(($ I&位于0xC)>> 2 | $ I&0x3()<< 2),或者只是扭转所有位的顺序
- 加上一个常数的值模的最大范围(必须是两个因素,如果你与那些上面结合本)
例如:该函数将其转换为0,1,2,3,5,..为13,如图4所示,12,7,15,..为数字高达15:
$i=($input+97) & 0xf;
$result=((($i&0x1) << 3) + (($i&0xe) >> 1)) ^ 0x5;
编辑
更简单的方法将使用线性同余发生器(LCG,这通常是用于产生随机数),它是由以下形式的公式定义:
X_n+1 = (a * X_n + c) mod m
为良好的值 a,c和米,X_0,X_1 ..序列的X_m-1将包含在0和M-1之间的所有数字正好一次。 现在,你可以从一个线性增加索引开始,并在LCG顺序使用下一个值作为你的“秘密”键。
EDIT2
执行:你可以设计自己的LCG的参数 ,但如果你弄错了也不会覆盖全范围(因此有重复),所以我会用一个发布,并试图设置的参数从这里本文 :
a = 16807, c = 0, m = 2147483647
这给你一个范围内的2 ** 31。 随着包(),就可以得到结果整数为字符串,BASE64_ENCODE()使它成为可读的字符串(最多6个字符显著,每字节的6位),所以这可能是你的函数:
substr(base64_encode(pack("l", (16807 * $index) % 2147483647)), 0, 6)
You could probably generate a MD5 hash of the current datetime/random number and truncate it to the length you need (5-8 characters) and store it as the id field.
If you are using storing this information in a database, you don't need to use a for loop to do the collision check, but you could just do a select statement - something like
SELECT count(1) c FROM Table WHERE id = :id
where :id would be the newly generated id. If c is greater than 0 then you know it already exists.
EDIT
This may may not be the best way to go about it. But I'll give it a shot, so I guess what you need is someway of converting a numbers into a unique short string and that is not in sequence.
I guess as you said, base64 encoding already does the number to short string conversion. To avoid the sequence problem you could have some mapping between your auto-generated id's to some "random" value (unique mapping). Then you can base64 encode this unique value.
You could generate this mapping as follows. Have a temporary table store values from 1 - 10,000,000. Sort it in random order and store it into you Map table.
INSERT INTO MappingTable (mappedId) SELECT values FROM TemporaryTable ORDER BY RAND()
Where MappingTable would have the 2 fields id (your auto-generated id would look up against this) and mappedId (which is what you would generate the base64 encoding for).
As you get closer to 10,000,000 you could rerun the above code again and change the values in the temporary table with 10,000,001-20,000,000 or something like that.
你可以使用按位异或争夺一些位:
select thefield ^ 377 from thetable;
+-----+---------+
| a | a ^ 377 |
+-----+---------+
| 154 | 483 |
| 152 | 481 |
| 69 | 316 |
| 35 | 346 |
| 72 | 305 |
| 139 | 498 |
| 96 | 281 |
| 31 | 358 |
| 11 | 370 |
| 127 | 262 |
+-----+---------+
我想,这绝不会是真正安全的,因为你只需要找到短唯一的字符串后面的加密方法劫持的ID。 在循环中真正在你的设置有问题的碰撞检测?
递增数的MD5应该罚款,但我担心的是,如果你截断你的MD5(通常是128位)下降到5-8个字符,你几乎肯定会破坏它作为一个独特的签名功能。 ..
完全真实。 特别是如果你达到80%的碰撞几率截断的MD5将是不比任何人的随机数,以保证自身的独特性,也就是一文不值。
但是,由于您使用的是数据库无论如何,为什么不直接使用一个唯一索引? 通过这种方式,uniquness检查是(比使用一个循环更加有效的方式)由MySQL本身。 刚刚尝试做INSERT与MD5生成的密钥,如果失败,再尝试......
如果你不能使用自动递增字段,并希望有一个绝对独特的价值,使用UUID 。 如果您决定使用其他任何东西(除了自动递增),你将是愚蠢的不检查冲突。
本博客文章有一些接近你所追求的。
http://kevin.vanzonneveld.net/techblog/article/create_short_ids_with_php_like_youtube_or_tinyurl/
递增数的MD5应该罚款,但我担心的是,如果你截断你的MD5(通常是128位)下降到5-8个字符,你几乎肯定会破坏它作为一个独特的签名功能。 ..