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,但它们可能会更高。

在设计系统时,并没有要求该代码是什么样子。但是现在客户希望能够从一张纸上手动输入,而上面的代码并不适合。我们已经说过我们不会对此做任何事情,但这似乎是一个有趣的挑战。

我的问题是哪里是开始无损压缩此代码的好地方?明显的解决方案(例如使用较短的密钥存储此代码)不是一种选择;我们的数据库是只读的。我需要构建一种双向方法来使这段代码更人性化。

4

3 回答 3

1

1)我同意你肯定需要校验和——数据输入错误很常见,除非你有训练有素的员工和独立的重复键控和自动交叉检查。

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)一个非常大的字母表 - 给自己一个包含 2^n 个单词的列表,用于一些大小合适的 n 并将 n 位编码为一个单词,例如 GREEN ENCHANTED LOGICIAN... -

于 2011-09-04T05:24:55.343 回答
0

不久前我担心这个问题。事实证明,你不能比 base64 做得更好——试图为每个字符多压缩几位并不值得付出努力(一旦你进入“奇怪”的位数,编码和解码就会变得更加复杂)。但与此同时,您最终会遇到输入时可能出现错误的内容(将 0 与 O 等混淆)。一个选项是选择一组修改后的字符和字母(所以它仍然是基数 64,但是,比如说,你用“>”代替“0”。另一个是添加校验和。再次,为了实现简单,我觉得校验和方法更好。

不幸的是,我再也没有得到任何进一步的信息——事情改变了方向——所以我不能提供代码或特定的校验和选择。

ps 我意识到有一个我没有解释的缺失步骤:我打算在编码之前将文本压缩成某种二进制形式(使用一些标准压缩算法)。所以总结一下:压缩,添加校验和,base64编码;base 64 解码、校验和、解压缩。

于 2011-09-03T23:59:32.020 回答
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();
    }

}
于 2011-09-06T04:22:22.553 回答