簡體   English   中英

如何在C#中創建一個獨特的隨機字符序列?

[英]How can I create an unique random sequence of characters in C#?

我正在我的應用程序中實現URL縮短功能,以便為我的用戶提供可在Twitter中使用的更短的替代URL。 重點是獨立於提供相同服務的縮短服務,並將其作為我的網絡應用程序的一項功能。

創建一個大約6個字符的獨特隨機字符序列的最佳方法是什么? 我計划將其用作我的數據庫中具有備用URL的項目的索引。

編輯:

此功能將用於工作板網站,其中每個新的工作廣告都將獲得帶有標題的自定義URL以及要在Twitter中使用的較短的URL。 也就是說,長期以來,獨特的6個字符組合的總數將綽綽有余。

制作獨特序列的最簡單方法是按順序執行此操作,即:aaaaaa aaaaab aaaaac ...這些不一定是最漂亮的,但會保證第一個12230590463序列的唯一性(假設您使用az和AZ作為唯一字符)。 如果您需要更多的URL,則需要添加第七個字符。

但它們不是隨機序列。 如果你做隨機的,只需選擇48次,6次隨機字符。 但是,您需要檢查現有數據庫中的“已使用”序列,因為您更有可能發生沖突。

你真的需要'隨機',還是'獨特'就足夠了?

Unique非常簡單 - 只需將URL插入數據庫,然后將該記錄的順序ID轉換為base-n數字,該數字由您選擇的characterset表示。

例如,如果您只想在序列中使用[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;
}

我會使用自動編號系統,並創建一個算法來生成密鑰。 即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
/// <returns>Non-existant string as ID</returns>
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
/// <returns>true or false</returns>
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;
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM