我正在尝试使用位掩码来提供尽可能多的二进制值,以便最终值将存储在有限的字符串分配内存中。我当前的方法是找到一个最大数字,并将其转换为以base-36开头的字符串。
value = (0 | (1<<1318)).to_s(36)
结果是255个压缩数字的字符,我可以从中提取我的原始数字1318。缺点是我限于1,318个二进制值,并且我想扩展该数字。 Ruby中是否有其他替代策略可以进一步压缩该数字?
答案 0 :(得分:1)
您始终可以将数字编码为基数s
,然后将其表示为带有所需字母的字符串。
def encode(n, alphabet)
s = alphabet.size
res = []
while (n > 0)
res << n % s
n = n / s
end
res.reverse.map { |i| alphabet[i] }.join
end
然后您的方法等效于encode(n, alphabet)
,其中alphabet
被定义为
alphabet = ((0..9).to_a + ("a".."z").to_a).join
# => "0123456789abcdefghijklmnopqrstuvwxyz"
但是您最好使用所有可能的字符,而不是只使用36个字符:
extended_alphabet = (0..255).map { |i| i.chr }.join
这总共提供(256 ** 255)种可能性,即高达(2 ** 2040),这比实际的(2 ** 1318)好得多。
这种编码恰好是最佳的,因为字符串中的每个字符最多可以有256个不同的值,并且所有这些都在这里使用。
然后可以按以下方式执行解码:
def decode(encoded, alphabet)
s = alphabet.size
n = 0
decode_dict = {}; i = -1
alphabet.each_char { |c| decode_dict[c] = (i += 1) }
encoded.each_char do |c|
n = n * s + decode_dict[c]
end
n
end
如果您要对所有编码使用固定的字母,我建议您在函数外部计算解码字典并将其作为参数而不是alphabet
,以避免每次尝试时都进行计算编码数字。
答案 1 :(得分:1)
数字为非负数
如果数字为非负数,我们可以将每个数字的每个8位编码为字符串的一部分,然后将每个字符转换为数字的8位以对字符串进行解码。
def encode(n)
str = ''
until n.zero?
str << (n & 255).chr
n = n >> 8
end
str.reverse
end
def decode(str)
str.each_char.reduce(0) { |n,c| (n << 8) | c.ord }
end
这在类Integer中使用以下位操作方法:&
,>>
,<<
和|
。
def test(n)
encoded = encode(n)
puts "#{n} => #{encoded} => #{decode(encoded)}"
end
test 1 # 1 => ["\u0001"] => 1
test 63 # 63 => ["?"] => 63
test 64 # 64 => ["@"] => 64
test 255 # 255 => ["\xFF"] => 255
test 256 # 256 => ["\u0001", "\u0000"] => 256
test 123456 # 123456 => ["\x01", "\xE2", "@"] => 123456
例如,
n = 123456
n.to_s(2)
#=> "11110001001000000"
如此
n = 0b11110001001000000
#=> 123456
此数字的字节可以显示为:
00000001 11100010 01000000
我们看到了
a = [0b00000001, 0b11100010, 0b01000000]
a.map(&:chr)
#=> ["\x01", "\xE2", "@"]
数字可以为负
如果要编码的数字可以是负数,则我们需要先将其转换为绝对值,然后在编码的字符串中添加一些信息,指示它们是非负数还是负数。这将至少需要一个额外的字节,因此我们可能在非负数中包含"+"
,在负数中包含"-"
。
def encode(n)
sign = "+"
if n < 0
sign = "-"
n = -n
end
str = ''
until n.zero?
str << (n & 255).chr
n = n >> 8
end
(str << sign).reverse
end
def decode(str)
n = str[1..-1].each_char.reduce(0) { |n,c| (n << 8) | c.ord }
str[0] == '+' ? n : -n
end
test -255 # -255 => ["-", "\xFF"] => -255
test -256 # -256 => ["-", "\u0001", "\u0000"] => -256
test -123456 # -123456 => ["-", "\x01", "\xE2", "@"] => -123456
test 123456 # 123456 => ["+", "\x01", "\xE2", "@"] => 123456