如何在C#中创建一个独特的随机字符序列?

我正在我的应用程序中实现URL缩短function,以便为我的用户提供可在Twitter中使用的更短的替代URL。 关键是要独立于提供相同服务的缩短服务,并将其作为我的网络应用程序的一项function。

创建一个大约6个字符的独特随机字符序列的最佳方法是什么? 我计划将其用作我的数据库中具有备用URL的项目的索引。

编辑:

此function将用于工作板网站,每个新的工作广告将获得一个自定义URL,其标题加上在Twitter中使用的较短的一个。 也就是说,长期以来,独特的6个字符组合的总数将绰绰有余。

你真的需要’随机’,还是’独特’就足够了?

唯一非常简单 – 只需将URL插入数据库,并将该记录的顺序ID转换为由您选择的characterset表示的base-n数字。

例如,如果您只想在序列中使用[AZ],则将记录的id转换为基数为26的数字,其中A = 1,B = 2,… Z = 26。 algothithm是递归div26 / mod26,其中商是必需字符,余数用于计算下一个字符。

然后在检索URL时,执行反函数,即将base-26数转换回十进制数。 执行SELECT URL WHERE ID = decimal,你就完成了!

编辑:

private string alphabet = "abcdefghijklmnopqrstuvwxyz"; // or whatever you want. Include more characters // for more combinations and shorter URLs public string Encode(int databaseId) { string encodedValue = String.Empty; while (databaseId > encodingBase) { int remainder; encodedValue += alphabet[Math.DivRem(databaseId, alphabet.Length, out remainder)-1].ToString(); databaseId = remainder; } return encodedValue; } public int Decode(string code) { int returnValue; for (int thisPosition = 0; thisPosition < code.Length; thisPosition++) { char thisCharacter = code[thisPosition]; returnValue += alphabet.IndexOf(thisCharacter) * Math.Pow(alphabet.Length, code.Length - thisPosition - 1); } return returnValue; } 

制作独特序列的最简单方法是按顺序执行此操作,即:aaaaaa aaaaab aaaaac …这些不一定是最漂亮的,但会保证第一个12230590463序列的唯一性(假设您使用az和AZ作为唯一字符)。 如果您需要更多的URL,则需要添加第七个字符。

但它们不是随机序列。 如果你做随机的,只需选择一个48,6的随机字符。 但是,您需要检查现有数据库中的“已使用”序列,因为您更有可能发生冲突。

我会使用自动编号系统,并创建一个算法来生成密钥。 即1 = a,2 = b,27 = aa等

您可以使用数据库自​​动编号来保证您的URL是唯一的,并且您可以在数据库或业务层的sproc中计算URL?

此外,您现在可以索引递增数字,该数字便宜,并且DB优化用于这些数字并且作为主键/外键进行散列,而不是可变长度随机字符串。

随机生成器的用处仅限于防止用户插入随机URL以查找他们不应该链接的内容。 如果这不是您的目标,那么顺序ID应该可以正常工作。 如果您只是不想让用户觉得他们正在使用“婴儿”技术(当他们看到他们的工作广告是#000001时),为什么不以某个任意值启动序列?

如果您指出“随机生成的长期独特6个字符组合的总数将足够多 ”,您是否将生日悖论计算在计算中? 这通常是在一个范围内创建随机ID的任何尝试的祸根,该范围仅比所需的预期范围小1个数量级或更少。

要创建真正的随机ID,您需要创建一个生成新随机值的循环,检查是否已使用该值,然后根据需要重复循环。 生日悖论意味着您很快就会到达生成的许多值已经被使用的点(尽管只消耗了总量程的一小部分),这会导致程序随着时间的推移变得越来越慢,直到数千万尝试(和数据库查找)生成每个ID。

我建议你继续编码顺序ID的想法。 为了避免用户能够简单地将URL中的值递增/递减为“探索”的问题,您可以使用组合位移和替代的有序字母列表(而不是1 = a,2 = b使用1 = t,2 = j等)。

在这里更多地考虑这个是一个想法。

您可以从密钥表开始,递增字符AAAAAA – ZZZZZZ。

然后在每次插入新URL时从该表中随机选择,并从可用键中删除。

思考?

对于随机选择尝试此链接

 Select a random row with MySQL: SELECT column FROM table ORDER BY RAND() LIMIT 1 Select a random row with PostgreSQL: SELECT column FROM table ORDER BY RANDOM() LIMIT 1 Select a random row with Microsoft SQL Server: SELECT TOP 1 column FROM table ORDER BY NEWID() Select a random row with IBM DB2 SELECT column, RAND() as IDX FROM table ORDER BY IDX FETCH FIRST 1 ROWS ONLY Thanks Tim Select a random record with Oracle: SELECT column FROM ( SELECT column FROM table ORDER BY dbms_random.value ) WHERE rownum = 1 

不要保留所有可能值的表,只需保留一个您使用过的值的表。 使用随机函数生成6个随机值,1到26,从中生成字符串并将其保存在数组或表中。 如果它已经存在,您可以(a)生成另一个字符串,或(b)在表中移动到下一个可用(缺失)的6个字母的字符串并使用该值。 (b)当桌子填满时会更有效率。

按照Reed Copsey的回答,我提出以下代码:

 class IDGetter { private StringID ID = new StringID(); public string GetCurrentID() { string retStr = ""; if (ID.char1 > 51) id.char1 = 0; if (ID.char2 > 51) id.char2 = 0; if (ID.char3 > 51) id.char3 = 0; if (ID.char4 > 51) id.char4 = 0; if (ID.char5 > 51) id.char5 = 0; if (ID.char6 > 51) throw new Exception("the maximum number of id's has been reached"); return ToIDChar(ID.char1) + ToIDChar(ID.char2) + ToIDChar(ID.char3) + ToIDChar(ID.char4) + ToIDChar(ID.char5) + ToIDChar(ID.char6) id.char1++; } public void SetCurrentID(StringID id) //for setting the current ID from storage or resetting it or something { this.ID = id; } private const string alphabet = "abcdefghijklmopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; private static string ToIDChar(int number) { if (number > 51 || number < 0) { throw new InvalidArgumentException("the number passed in (" + number + ") must be between the range 0-51"); } return alphabet[number]; } } public struct StringID { public int char1 = 0; public int char2 = 0; public int char3 = 0; public int char4 = 0; public int char5 = 0; public int char6 = 0; } 

您可能想要提出一种存储当前ID的方法,但这应该可行。

我用它来做一些非常相似的事情。 我不担心它的速度,因为它将是一个很少使用的事件和表。 但是可以根据需要增加字符串。

 /// Generates a string and checks for existance /// Non-existant string as ID public static string GetRandomNumbers(int numChars, string Type) { string result = string.Empty; bool isUnique = false; while (!isUnique) { //Build the string result = MakeID(numChars); //Check if unsued isUnique = GetValueExists(result, Type); } return result; } /// Builds the string public static string MakeID(int numChars) { string random = string.Empty; string[] chars = { "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z" }; Random rnd = new Random(); for (int i = 0; i < numChars; i++) { random += chars[rnd.Next(0, 35)]; } return random; } /// Checks database tables based on type for existance, if exists then retry /// true or false private static bool GetValueExists(string value, string Type) { bool result = false; string sql = ""; if (Type == "URL") { sql = string.Format(@"IF EXISTS (SELECT COUNT(1) FROM myTable WHERE uniqueString = '{0}') BEGIN SELECT 1 END ELSE BEGIN SELECT 0 END ", value); } //query the DB to see if it's in use result = //ExecuteSQL return result; }