0

這是我爲解決工作中的一個無法解決的問題而採取的一個副項目。我們的系統輸出代碼來代表另一件事情的組合。一些示例代碼是:使整數列表更加人性化

9-9-0-4-4-5-4-0-2-0-0-0-2-0-0-0-0-0-2-1 -2-1-2-2-2-4

9-5-0-7-4-3-5-7-4-0-5-1-4-2-1-5-5- 4-6-3-7-9-72

9-15-0-9-1-6-2-1-2-0-0-1-6-0-7

到目前爲止,我所看到的其中一個插槽中的最大數量大約是150個,但它們可能會更高。

當系統被設計時,沒有要求這個代碼看起來像什麼。但是現在客戶希望能夠從一張紙上手工輸入,上面的代碼不適合。我們說過我們不會做任何事情,但它似乎是一個有趣的挑戰。

我的問題是在哪裏開始無損壓縮此代碼的好地方?明顯的解決方案,例如用較短的密鑰存儲此代碼不是一種選擇;我們的數據庫是隻讀的。我需要構建一個雙向方法來使代碼更加人性化。

+0

「顯而易見的解決方案,如用較短的密鑰存儲此代碼不是一個選項;我們的數據庫是隻讀的。」 - 用人工約束得到愛情問題!也許代理鍵是爲了... –

+0

我是否理解你只是想讓鍵入更容易,而不是減少存儲的數據的大小? – ExCodeCowboy

+0

使用3D條形碼... –

回答

0

我擔心這個問題而回。事實證明,你不可能比base64做得更好 - 試圖擠壓每個字符幾個比特並不是真正值得的努力(一旦你進入「奇怪」的比特數編碼和解碼變得更加複雜)。但同時,最終會出現一些輸入時可能出現錯誤的情況(將0與O等混淆)。一種選擇是選擇一組經過修改的字符和字母(所以它仍然是64位的,但是,例如,您將「>」替換爲「0」,另一個選擇是添加校驗和,爲了簡化實現,我感覺校驗方法是更好

不幸的是我從來沒有得到任何進一步的 - 事情改變了方向 - 所以我不能提供代碼或特定校驗選擇

PS我意識到有梯級缺失我沒有解釋:。我將在編碼之前將文本壓縮成某種二進制形式(使用某種標準的壓縮算法),所以總結一下:壓縮,加校驗和,base64編碼; base 64解碼,校驗和,解壓縮

1

1)I agr即,你肯定需要一個校驗和 - 數據輸入錯誤是非常普遍的,除非你有真正的訓練有素的工作人員和獨立的重複鍵控自動交叉檢查。

2)我建議http://en.wikipedia.org/wiki/Huffman_coding把你的數字列表變成一個比特流。爲了獲得所需的概率,您需要一個體面的實際數據樣本,以便您可以進行計數,將Ni設置爲數字i出現在數據中的次數。然後我建議設置Pi =(Ni + 1)/(Sum_i(Ni + 1)) - 這可以使概率平滑一些。而且,用這種方法,如果你看到例如數字0-150,你可以通過輸入數字151-255並將它們設置爲Ni = 0來增加一點鬆弛。另一種方法是添加某種轉義序列。

3)找到一種方法讓人們輸入結果序列的位是真正的應用心理學問題,但這裏有一些想法捏的建議。3a)軟件許可證 - 只是在一些64字符的字母表中每個字符編碼6位,但是組合字符的方式使得人們可以更容易地保存位置。 BC017-06777-14871-160C4

3b)英國車牌照。使用字母變化來向人們展示如何對字符進行分組,例如ABCD0123EFGH4567IJKL ...

3c)一個非常大的字母表 - 讓自己爲一些體面大小爲n的2^n個單詞列表,並將n個單詞編碼爲單詞,例如, GREEN ENCHANTED LOGICIAN ... -

+0

對3c的+1:例如,每個字使用8位編碼PGP [生物統計單詞列表](http://en.wikipedia.org/wiki/biometric_word_list)。 –

0

這與我以前使用的相似。這樣做肯定有更好的方法,但是我使用了這種方法,因爲在當時需要的Transact-SQL中很容易進行鏡像。如果你的id分佈是非隨機的,你當然可以修改這個來結合Huffman編碼,但它可能是不必要的。

您沒有指定語言,所以這是在c#中,但它應該很容易過渡到任何語言。在查找中,您會看到通常會混淆的字符被忽略。這應該加快進入。我也有要求有一個固定的長度,但你可以很容易地修改它。

static public class CodeGenerator 
{ 
    static Dictionary<int, char> _lookupTable = new Dictionary<int, char>(); 

    static CodeGenerator() 
    { 
     PrepLookupTable(); 
    } 

    private static void PrepLookupTable() 
    { 
     _lookupTable.Add(0,'3'); 
     _lookupTable.Add(1,'2'); 
     _lookupTable.Add(2,'5'); 
     _lookupTable.Add(3,'4'); 
     _lookupTable.Add(4,'7'); 
     _lookupTable.Add(5,'6'); 
     _lookupTable.Add(6,'9'); 
     _lookupTable.Add(7,'8'); 
     _lookupTable.Add(8,'W'); 
     _lookupTable.Add(9,'Q'); 
     _lookupTable.Add(10,'E'); 
     _lookupTable.Add(11,'T'); 
     _lookupTable.Add(12,'R'); 
     _lookupTable.Add(13,'Y'); 
     _lookupTable.Add(14,'U'); 
     _lookupTable.Add(15,'A'); 
     _lookupTable.Add(16,'P'); 
     _lookupTable.Add(17,'D'); 
     _lookupTable.Add(18,'S'); 
     _lookupTable.Add(19,'G'); 
     _lookupTable.Add(20,'F'); 
     _lookupTable.Add(21,'J'); 
     _lookupTable.Add(22,'H'); 
     _lookupTable.Add(23,'K'); 
     _lookupTable.Add(24,'L'); 
     _lookupTable.Add(25,'Z'); 
     _lookupTable.Add(26,'X'); 
     _lookupTable.Add(27,'V'); 
     _lookupTable.Add(28,'C'); 
     _lookupTable.Add(29,'N'); 
     _lookupTable.Add(30,'B');   
    } 


    public static bool TryPCodeDecrypt(string iPCode, out Int64 oDecryptedInt) 
    { 
     //Prep the result so we can exit without having to fiddle with it if we hit an error. 
     oDecryptedInt = 0; 

     if (iPCode.Length > 3) 
     { 
      Char[] Bits = iPCode.ToCharArray(0,iPCode.Length-2); 

      int CheckInt7 = 0; 
      int CheckInt3 = 0; 
      if (!int.TryParse(iPCode[iPCode.Length-1].ToString(),out CheckInt7) || 
       !int.TryParse(iPCode[iPCode.Length-2].ToString(),out CheckInt3)) 
      { 
       //Unsuccessful -- the last check ints are not integers. 
       return false; 
      } 
      //Adjust the CheckInts to the right values. 
      CheckInt3 -= 2; 
      CheckInt7 -= 2; 

      int COffset = iPCode.LastIndexOf('M')+1; 


      Int64 tempResult = 0; 
      int cBPos = 0; 
      while ((cBPos + COffset) < Bits.Length) 
      { 
       //Calculate the current position. 
       int cNum = 0; 
       foreach (int cKey in _lookupTable.Keys) 
       { 
        if (_lookupTable[cKey] == Bits[cBPos + COffset]) 
        { 
         cNum = cKey; 
        } 
       } 
       tempResult += cNum * (Int64)Math.Pow((double)31, (double)(Bits.Length - (cBPos + COffset + 1))); 
       cBPos += 1; 
      } 

      if (tempResult % 7 == CheckInt7 && tempResult % 3 == CheckInt3) 
      { 
       oDecryptedInt = tempResult; 
       return true;  
      } 


      return false; 

     } 
     else 
     { 
      //Unsuccessful -- too short. 
      return false; 
     } 
    } 
    public static string PCodeEncrypt(int iIntToEncrypt, int iMinLength) 
    { 
     int Check7 = (iIntToEncrypt % 7) + 2; 
     int Check3 = (iIntToEncrypt % 3) + 2; 

     StringBuilder result = new StringBuilder(); 
     result.Insert(0, Check7); 
     result.Insert(0, Check3); 

     int workingNum = iIntToEncrypt; 

     while (workingNum > 0) 
     { 
      result.Insert(0, _lookupTable[workingNum % 31]); 
      workingNum /= 31; 
     } 

     if (result.Length < iMinLength) 
     { 
      for (int i = result.Length + 1; i <= iMinLength; i++) 
      { 
       result.Insert(0, 'M'); 
      } 
     } 

     return result.ToString(); 
    } 

}