goo.gl或jsfiddle等网站如何生成url代码?

我想生成像goo.gl和jsfiddle网站( http://jsfiddle.net/XzKvP/ )这样的代码。

我尝试了不同的东西,给了我太大的guid,重复的字母数字代码等。

我想我应该能够根据数据库表中的主键生成一个字母数字代码。 这样它会不重复吗? PK是一个自动递增的整数1.但不确定它应该如何完成。

我希望代码看起来是随机的,但它不一定是。 例如,我希望我的数据库中的项目1234BCDE1235项目是BCDF

例子:

请注意urlhttp://jsfiddle.net/XzKvP/如何与页面关联的唯一5字符代码XzKvP 。 我希望能够生成相同类型的代码。

goo.gl也是这样做的: http : //goo.gl/UEhtg有UEhtg

这是怎么做到的?

基于随机子串的解决方案并不好,因为输出会发生冲突。 它可能过早地发生(运气不好),并且最终会在生成的值列表变大时发生。 对于碰撞变高的可能性,它甚至不必那么大(见生日攻击 )。

这个问题的好处是增量ID和它的对应物之间的伪随机排列将在URL中显示。 这种技术保证了碰撞是不可能的,同时仍然产生一个与输入空间一样小的输出空间。

履行

我建议使用这个C#版本的Feistel密码,具有32位块,3轮和圆形function ,灵感来自伪随机生成器。

 private static double RoundFunction(uint input) { // Must be a function in the mathematical sense (x=y implies f(x)=f(y)) // but it doesn't have to be reversible. // Must return a value between 0 and 1 return ((1369 * input + 150889) % 714025) / 714025.0; } private static uint PermuteId(uint id) { uint l1=(id>>16)&65535; uint r1=id&65535; uint l2, r2; for (int i = 0; i < 3; i++) { l2 = r1; r2 = l1 ^ (uint)(RoundFunction(r1) * 65535); l1 = l2; r1 = r2; } return ((r1 << 16) + l1); } 

要在base62字符串中表示置换ID:

 private static string GenerateCode(uint id) { return ToBase62(PermuteId(id)); } 

Base62函数与前一个答案相同,只是采用uint而不是int (否则必须重写这些函数以处理负值)。

自定义算法

RoundFunction是算法的秘诀。 您可以将其更改为非公开版本,可能包括密钥。 Feistel网络有两个非常好的属性:

  • 即使提供的RoundFunction不可逆,算法也保证PermuteId()将是数学意义上的排列(意味着零冲突)。

  • 即使轻微改变圆函数内的表达式也会彻底改变最终输出值的列表。

请注意,在圆形表达式中放置一些过于微不足道的东西会破坏伪随机效应,尽管它仍然可以在每个PermuteId输出的唯一性方面PermuteId 。 此外,在数学意义上不是函数的表达式将与算法不兼容,因此例如不允许涉及random()任何事物。

可逆性

在其当前forms中, PermuteId函数是它自己的反转,这意味着:

 PermuteId(PermuteId(id))==id 

因此,如果程序生成一个短字符串,如果使用FromBase62函数将其转换回uint ,并将其作为PermuteId()输入,则返回相应的初始ID。 如果您没有用于存储[internal-ID / shortstring]关系的数据库,这非常酷:它们实际上并不需要存储!

产生更短的琴弦

上述函数的范围是32位,即从0到2^32-1大约40亿个值。 要在base62中表示该范围,需要6个字符。

只有5个字符,我们希望最多代表62^5值,这个值略低于10亿。 如果输出字符串限制为5个字符,则应按如下方式调整代码:

  • 找到N使得N是偶数且2^N尽可能高但低于62^5 。 这是28,所以我们的实际输出范围适合62^5将是2^28或约2.68亿的值。

  • PermuteId ,对于l1r1使用PermuteId 28/2=14位值而不是16位,同时注意不要忽略输入的单个位(必须小于2 ^ 28)。

  • RoundFunction的结果乘以16383而不是65535,以保持在14位范围内。

  • PermuteId结束时,重新组合r1l1以形成14+14=28位值而不是32位。

相同的方法可以应用于4个字符,输出范围为2^22 ,或约4百万个值。

它是什么样子的

在上面的版本中,以id = 1开头的前10个生成的字符串是:

 cZ6ahF
 3t5mM
 xGNPN
 dxwUdS
 ej9SyV
 cmbVG3
 cOlRkc
 bfCPOX
 JDr8Q
 eg7iuA

如果我在圆函数中做了一个微不足道的改变,那就变成了:

 ey0LlY
 ddy0ak
 dDw3wm
 bVuNbg
 bKGX22
 c0s5GZ
 dfNMSp
 ZySqE
 cxKH4b
 dNqMDA

您可以将五个字母的代码视为base-62表示法中的数字:您的“数字”是26个小写字母和26个大写字母,以及0到9个数字(26 + 26 + 10)个数字。 给定0到62^5 (等于916132832)(例如,您的主键),您可以将转换为五位数的基数-62,如下所示:

 private static char Base62Digit(int d) { if (d < 26) { return (char)('a'+d); } else if (d < 52) { return (char)('A'+d-26); } else if (d < 62) { return (char)('0'+d-52); } else { throw new ArgumentException("d"); } } static string ToBase62(int n) { var res = ""; while (n != 0) { res = Base62Digit(n%62) + res; n /= 62; } return res; } private static int Base62Decode(char c) { if (c >= '0' && c <= '9') { return 52 + c - '0'; } else if (c >= 'A' && c <= 'Z') { return 26 + c - 'A'; } else if (c >= 'a' && c <= 'z') { return c - 'a'; } else { throw new ArgumentException("c"); } } static int FromBase62(string s) { return s.Aggregate(0, (current, c) => current*62 + Base62Decode(c)); } 

以下是如何生成加密强大的随机数(您需要添加对System.Security的引用):

 private static readonly RNGCryptoServiceProvider crypto = new RNGCryptoServiceProvider(); private static int NextRandom() { var buf = new byte[4]; crypto.GetBytes(buf); return buf.Aggregate(0, (p, v) => (p << 8) + v) & 0x3FFFFFFF; } 

这就是我最终做的事情

(自DanielVérité回答以来更新):

 class Program { private static double RoundFunction(uint input) { // Must be a function in the mathematical sense (x=y implies f(x)=f(y)) // but it doesn't have to be reversible. // Must return a value between 0 and 1 return ((1369 * input + 150889) % 714025) / 714025.0; } private static char Base62Digit(uint d) { if (d < 26) { return (char)('a' + d); } else if (d < 52) { return (char)('A' + d - 26); } else if (d < 62) { return (char)('0' + d - 52); } else { throw new ArgumentException("d"); } } private static string ToBase62(uint n) { var res = ""; while (n != 0) { res = Base62Digit(n % 62) + res; n /= 62; } return res; } private static uint PermuteId(uint id) { uint l1 = (id >> 16) & 65535; uint r1 = id & 65535; uint l2, r2; for (int i = 0; i < 3; i++) { l2 = r1; r2 = l1 ^ (uint)(RoundFunction(r1) * 65535); l1 = l2; r1 = r2; } return ((r1 << 16) + l1); } private static string GenerateCode(uint id) { return ToBase62(PermuteId(id)); } static void Main(string[] args) { Console.WriteLine("testing..."); try { for (uint x = 1; x < 1000000; x += 1) { Console.Write(GenerateCode(x) + ","); } } catch (Exception err) { Console.WriteLine("error: " + err.Message); } Console.WriteLine(""); Console.WriteLine("Press 'Enter' to continue..."); Console.Read(); } }