【问题标题】:Large number array compression大数数组压缩
【发布时间】:2013-10-03 15:47:12
【问题描述】:

我有一个 javascript 应用程序,它通过网络发送大量数字数据。然后将该数据存储在数据库中。我遇到了大小问题(带宽太大,数据库太大)。我现在准备牺牲一些性能来进行压缩。

我正在考虑实现一个基数为 62 的 number.toString(62) 和 parseInt(compressed, 62)。这肯定会减少数据的大小,但在我继续这样做之前,我想我会把它交给这里的人们,因为我知道肯定有一些我没有考虑过的开箱即用的解决方案。

基本规格是: - 将大量数组压缩成字符串以进行 JSONP 传输(所以我认为 UTF 已经过时了) - 相对较快,看我不期待与现在相同的性能,但我也不想要 gzip 压缩。

任何想法将不胜感激。

谢谢

吉多·塔皮亚

【问题讨论】:

  • 为什么不用 gzip?那有什么问题? (或者更可能的是,DEFLATE)如果你想让它更简单,你也可以只做 Huffman 或 LZW 之类的事情。 Javascript 中的霍夫曼编码:tom-ash.net/blogs/Blog.aspx?File=Programming/…
  • 要明确一点 - 是通过 javascript 和 ajax/jsonp 从客户端向服务器发送数据?
  • @James:正确,我们将一个非常大的数字数组作为字符串发送到服务器。该数组不在 JSON 中(所以没有愚蠢的 xml 标记 - edit 哎呀,我的意思是花括号)只是整数之间的 string.join('.')。然后这个字符串也存储在数据库中(这是真正的大小问题)。
  • @Cheeso:我不想把压缩/解压负载放在服务器上。我希望客户端(浏览器)承担这个成本,所以 gzip 不可用,除非你能想到一种在 JS 中使用复杂压缩算法的方法。
  • @nick:我们正在讨论每个约 200k 的请求。我们每天谈论约 100,000 个请求,我们保留 2 周的数据,但是每天 100,000 个请求正在增加 :) 如果我可以将这些请求增加到 100k,它将让我能够忍受我当前的硬件设置至少另一个6 个月。

标签: javascript compression


【解决方案1】:

执行此操作的另一种方法可能是编码为二进制类型,例如有符号/无符号整数,然后像 http://snippets.dzone.com/posts/show/685 那样手动解码,这需要服务器端代码来创建二进制数据。

然后您可以使用 huffman 压缩或类似 RLE 之类的东西(请参阅 http://rosettacode.org/wiki/Run-length_encoding#JavaScript 了解实现,尽管在 IE 中可能会出现一些问题而无需修改)以进一步压缩数据。

编辑: 或者,您可以将数字本身转换为未编码的 URI 字符范围(请参阅http://en.wikipedia.org/wiki/Percent-encoding)中的基数(基数),如果许多数字大于 2 位,这应该可以正常工作。我将 http://code.activestate.com/recipes/111286-numeric-base-converter-that-accepts-arbitrary-digi/ 的代码从 python 转换为执行此操作。

它目前不处理浮点数,但它可以很容易地完成:

function get_map(s) {
    d = {}
    for (var i=0; i<s.length; i++) {
        d[s.charAt(i)] = i}
    d.length = s.length
    d._s = s
    return d}

var separate_with = '~';
var encodable = get_map('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_.'); // - is reserved for negatives obviously :-P
var base10 = get_map('0123456789')

// UNCOMMENT ME for length/speed testing in a wider base!
// You may wish to experiment with the ranges for a happy medium between bandwidth and DB space :-P
/*var encodable = ''
for (var i=1; i<128; i++) {
    encodable += String.fromCharCode(i)
}
encodable = get_map(encodable)*/

function baseconvert(number, fromdigits, todigits) {
    var number = String(number)

    if (number.charAt(0) == '-') {
        number = number.slice(1, number.length)
        neg=1}
    else {
        neg=0}

    // make an integer out of the number
    var x = 0
    for (var i=0; i<number.length; i++) {
        var digit = number.charAt(i)
        x = x*fromdigits.length + fromdigits[digit]
    }

    // create the result in base 'todigits.length'
    res = ""
    while (x>0) {
        remainder = x % todigits.length
        res = todigits._s.charAt(remainder) + res
        x = parseInt(x/todigits.length)
    }

    if (neg) res = "-"+res
    return res
}

function encodeNums(L) {
    var r = []
    for (var i=0; i<L.length; i++) {
         r.push(baseconvert(L[i], base10, encodable))
    }
    return r.join(separate_with)
}

function decodeNums(s) {
    var r = []
    var s = s.split(separate_with)
    for (var i=0; i<s.length; i++) {
         r.push(parseInt(baseconvert(s[i], encodable, base10)))
    }
    return r
}

var test = [5, 654645, 24324, 652124, 65, 65289543, 65278432, 643175874158, 652754327543]
alert(encodeNums(test))
alert(decodeNums(encodeNums(test)))

【讨论】:

  • 这是我在最初的问题中提出的(base 62 encoding)。但是,您的示例确实提高了更大基数(128)的潜力,但是我怀疑 JSONP 是否会正确传输,我认为 62 与它所能得到的一样好。感谢您的代码,我将与我的性能进行比较。
  • 仅供参考,这段代码会创建一堆全局变量。您可能想要更新它。在 get_map 中:d 将是一个全局变量,在 baseconvert 中:res、neg 和剩余都将创建全局变量。在那之后我停止了阅读,但这个想法似乎很有趣......
【解决方案2】:

选项

  • 使用 js 库(参见 Josh 的回答)但注意脚本超时
  • 使用某种也可以压缩的 activex 或 silverlight 上传器
  • 使用 java 插件
  • 使用基于 Flash 的压缩上传器

【讨论】:

    【解决方案3】:

    我现在正在考虑将数字的长度编码为数字本身的想法。我还没有完善这个算法,但一旦完成就会发布。但大致这就是我目前正在努力实现的目标:

    边界:

    • 允许损失精度 (+- 3)
    • 最大数量为 3200
    • 最小值为 0(因此没有负数)
    • 无小数 - 浮动

    所以现在给定我的最大允许数字,我知道以 62 为基数的编码数字的长度最大长度为 2。所以任何编码数字的长度都是 1 或 2 个字符。惊人的。所以现在我要让这个数字变成奇数,甚至取决于它的 1 个或 2 个字符(记住我可以处理精度损失)。这消除了对分隔符的需要。

    现在我看到大约 70%-80% 的压缩率,目前它非常有问题,但我对此很兴奋,所以这篇文章鼓励围绕这种方法进行讨论。

    【讨论】:

    • 如果允许使用二进制,我将使用每个字符中八位中的第一位来询问该数字是否继续为en.wikipedia.org/wiki/Variable-length_code。但是为“继续”保留 31 个字符,为“停止”保留 31 个字符可能也可以。话又说回来,你可能已经从它的声音中想到了:-)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-02-06
    • 2016-12-26
    • 2011-12-14
    • 2013-12-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多