将数字列表压缩或编码为单个字母数字字符串的最佳方法是什么?

时间:2010-10-04 18:57:46

标签: algorithm string encoding

将任意长度和大小的数字列表压缩或编码为单个字母数字字符串的最佳方法是什么?

目标是能够将1,5,8,3,20,212,42之类的内容转换为类似a8D1jN的内容,以便在URL中使用,然后再转换为1,5,8,3,20,212,42

对于结果字符串,我可以使用任何数字和任何ascii字符,小写和大写,所以:0-9a-zA-Z。我不想有任何标点符号。

更新:添加了关于哪些字符正常的说明。

7 个答案:

答案 0 :(得分:5)

如果您将列表视为字符串,则您需要编码11个不同的字符(0-9和逗号)。这可以用4位表示。如果您愿意添加,请说$和!在你的可接受字符列表中,你将有64个不同的输出字符,因此每个字符可以编码6位。

这意味着您可以将字符串映射到一个编码的字符串,该字符串比原始字符串短约30%,并且相当模糊和随机查看。

这样你就可以将数字系列[1,5,8,3,20,212,42]转码为字符串“gLQfoIcIeQqq”。

更新:我感到很有灵感并为此解决方案编写了一个python解决方案(不是很快但功能足够......)

ZERO = ord('0')
OUTPUT_CHARACTERS = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789$!"

def encode(numberlist):

    # convert to string -> '1,5,8,3,20,212,42'
    s = str(numberlist).replace(' ','')[1:-1]

    # convert to four bit values -> ['0010', '1011', '0110', ... ]
    # (add 1 to avoid the '0000' series used for padding later)
    four_bit_ints = [0 <= (ord(ch) - ZERO) <= 9 and (ord(ch) - ZERO) + 1 or 11 for ch in s]
    four_bits = [bin(x).lstrip('-0b').zfill(4) for x in four_bit_ints]

    # make binary string and pad with 0 to align to 6 -> '00101011011010111001101101...'
    bin_str = "".join(four_bits)
    bin_str = bin_str + '0' * (6 - len(bin_str) % 6)

    # split to 6bit blocks and map those to ints
    six_bits = [bin_str[x * 6 : x * 6 + 6] for x in range(0, len(bin_str) / 6)]
    six_bit_ints = [int(x, 2) for x in six_bits]

    # map the 6bit integers to characters
    output = "".join([OUTPUT_CHARACTERS[x] for x in six_bit_ints])

    return output

def decode(input_str):

    # map the input string from characters to 6bit integers, and convert those to bitstrings
    six_bit_ints = [OUTPUT_CHARACTERS.index(x) for x in input_str]
    six_bits = [bin(x).lstrip('-0b').zfill(6) for x in six_bit_ints]

    # join to a single binarystring
    bin_str = "".join(six_bits)

    # split to four bits groups, and convert those to integers
    four_bits = [bin_str[x * 4 : x * 4 + 4] for x in range(0, len(bin_str) / 4)]
    four_bit_ints = [int(x, 2) for x in four_bits]

    # filter out 0 values (padding)
    four_bit_ints = [x for x in four_bit_ints if x > 0]

    # convert back to the original characters -> '1',',','5',',','8',',','3',',','2','0',',','2','1','2',',','4','2'
    chars = [x < 11 and str(x - 1) or ',' for x in four_bit_ints]

    # join, split on ',' convert to int
    output = [int(x) for x in "".join(chars).split(',') if x]

    return output


if __name__ == "__main__":

    # test
    for i in range(100):
        numbers = range(i)
        out = decode(encode(numbers))
        assert out == numbers

    # test with original series
    numbers = [1,5,8,3,20,212,42]
    encoded = encode(numbers)
    print encoded         # prints 'k2UBsZgZi7uW'
    print decode(encoded) # prints [1, 5, 8, 3, 20, 212, 42]

答案 1 :(得分:3)

您可以使用Base64等编码方案。

Base64模块或库在多种编程语言中很常见。

答案 2 :(得分:2)

您可以使用逗号分隔数字,而不是逗号分隔数字,您可以使用“a”+数字替换每个数字的最后一位数字。因此,您的列表[1,5,8,3,20,212,42]会变得神秘bfid2a21c4c。 :)

我只会在有少量数字的情况下使用这样的东西,压缩将无法缩短字符串。如果我们谈论的是很多数字,你可以尝试对数据执行某种压缩+ base64编码。

答案 3 :(得分:1)

取决于数字的范围 - 在合理的范围内,简单的dictionary compression方案可以起作用。

鉴于您对10k行的编辑和估计,每个数字映射到[A-Za-z0-9]的三倍的字典方案对于62 * 62 * 62个不同的条目可能是唯一的。

答案 4 :(得分:0)

对于您的案例,可能会有超酷且高效的算法。但是,一个非常简单,经过测试和可靠的算法是在逗号分隔的数字串上使用“通用”编码或压缩算法。

有许多可供选择。

答案 5 :(得分:0)

'best'取决于您的标准。

如果最好意味着简单:只需将数字串在一起,用固定字符分隔:

1a5a8a3a20a212a42

这也应该是 fast

如果您希望生成的字符串 small ,您可以通过某些压缩算法(如zip)运行上面的字符串,然后通过某些编码(如base64或类似的结果)运行结果。

答案 6 :(得分:0)

你也可以使用中国剩余定理。

想法是找到一个数字X,以便

X = a1 mod n1
X = a2 mod n2
...
X = ak mod nk
对于每种组合(i j),

gcd(Ni Nj)= 1。

CRT说如何找到满足这些方程的最小数X.

像这样,你可以将数字a1 ... ak编码为X,并保持固定的Ns列表。每个Ni必须大于ai,完全如此。