【问题标题】:Minimum compression time for a range of files一系列文件的最短压缩时间
【发布时间】:2014-05-04 04:39:19
【问题描述】:

有点算法问题,或者可能是优化问题,或者动态规划。

假设我们有 N 个文件要压缩。 平均压缩比为 L。 文件的压缩时间取决于两个因素 - 1. 当前正在处理的文件的大小,以及 2.系统剩余内存空间(总计=M,占用=压缩文件和未压缩文件的文件大小之和)

所以

t(i) = K * s(i) / (M-L*(s(1)+s(2)+....+s(i))-(s(i+1) + s(i+2) + .....+ s(n))

其中 s(i) 是第 i 个文件的大小,t(i) 是压缩第 i 个文件所用的时间。

我要做的是计算要压缩的文件的最佳系列,以使所需的总时间最短。那么如何计算这个系列呢?

【问题讨论】:

  • 听起来有点像家庭作业,不是吗?
  • 听起来有点,但事实并非如此。当我在考虑练习关于 DP 的问题时,我突然想到了这一点。
  • 首先,似乎最优策略是首先压缩最大的文件。
  • @Ante 实际上恰恰相反。查看 Ralor 的回答 :)

标签: algorithm math optimization dynamic-programming


【解决方案1】:

似乎最好的方法是按大小对文件进行排序并进行处理。这种贪婪的做法可以解释为“先压缩小文件,避免先压缩大文件”。

可能的批准是:

如果我们有两个文件 A,B 使得 size(A)

t(A,B)

A/M + B/(M - L*A)

A*(1/M - 1/(M - L*B))

B/A >= (1/M - 1/(M - L*B)) / (1/M - 1/(M - L*A)) = B*(M - L*A) / (A*(M - L*B))

1 >= (M - L*A)/(M - L*B)

-L*B >= -L*A

B >= A

所以这意味着第一个方程式也是正确的(如果没有在某处失败:D)

排序为我们提供了每对文件 A

我为 N

测试:N、L、M、K 和 N 个文件

8 0.5 80.0 1.0

7 1 6 3 4 5 6 5

结果:

0.515769

1 3 4 5 5 6 6 7

#include <iostream>
#include <algorithm>
using namespace std;

// will work bad for cnt > 10 because 10! = 3628800
int perm[] = {0,1,2,3,4,5,6,7,8,9};
int bestPerm[10];
double sizes[10];

double calc(int cnt, double L, double M, double K, double T) {
    double res = 0.0, usedMemory = 0.0;
    for(int i = 0; i < cnt; i++) {
        int ind = perm[i];
        res += K * sizes[ind] / (M - L * usedMemory - (T - usedMemory)); 
        usedMemory += sizes[ind];
    }
    return res;
}

int main() {
    int cnt;
    double L,M,K,T = 0.0;
    cin >> cnt >> L >> M >> K;
    for(int i = 0; i < cnt; i++)
        cin >> sizes[i], T += sizes[i];

    double bruteRes = 1e16;
    int bruteCnt = 1;
    for(int i = 2; i <= cnt; i++)
        bruteCnt *= i;
    for(int i = 0; i < bruteCnt; i++) {
        double curRes = calc(cnt, L, M, K, T);
        if( bruteRes > curRes ) {
            bruteRes = curRes;
            for(int j = 0; j < cnt; j++)
                bestPerm[j] = perm[j];
        }
        next_permutation(perm, perm + cnt);
    }
    cout << bruteRes << "\n";
    for(int i = 0; i < cnt; i++)
            cout << sizes[bestPerm[i]] << " ";
    cout << "\n";

    return 0;
}

更新当所有文件pastebin L 不同时的实现(似乎蛮力更喜欢按压缩比 L[i] 的降序对它们进行排序并首先使用较小的文件,如果 L 相等)。

【讨论】:

  • res += K * sizes[ind] / (M - L * usedMemory - (T - usedMemory)); 其中T 是文件的总大小。但是,即使这样也可以按升序给出输出。谢谢。我想知道您是否可以建议一些会导致权衡的更改,升序或降序都不是最佳的。
  • @k_programmer 我修好了。它并没有太大变化,因为它就像在读取值后反转 L = 1-L 并替换 M -= T 。我的第一个想法是尝试升序和降序,或者通过三元搜索找到一些中间位置“k”并将所有文件分成2组,但我发现它没有用。我的朋友说,如果所有文件的 L 相同,这个问题很简单,但我们都认为上面显示的批准是正确的。您可以尝试在 10^6 个随机(但正确)数据集上运行 bruteforce,并检查答案是否会在所有数据集中排序。
  • 变量 L 呢?让我尝试改变 L :)
  • 使用变量 L,它给出了一个既不是升序也不是降序的序列 - 正是我想要的!但现在的问题是如何动态计算最优序列(使用 DP 等)?
  • @k_programmer 我已经用 pastebin 链接更新了帖子,当 L[i] 不同时问题仍然很容易)贪婪仍然获胜)
【解决方案2】:

假设您有一个声称是最佳的时间表。考虑任何文件以及紧随其后处理的文件。如果您可以通过交换它们来改进时间表,那它就不是最佳的了。因此,如果您可以证明当两个文件并排时最好先处理一个小文件,然后再处理一个大文件,那么您可以证明最好的日程安排是按照最小文件的排序顺序排列的,因为您可以改进任何其他文件时间表。

因为您只是交换两个相邻的文件,所以在这两个文件之前和之后处理文件所花费的时间不会改变 - 之前和之后的可用内存量相同。您也可以缩放问题,以便其中一个文件的大小为一个单位。假设您在第一个文件之前总共有 K 个可用内存单元,并且假设第二个文件大小为 x 个单位,压缩比为 1:L,您最终会得到类似 1/K + x/(K+ L) - x/K - 1/(K - xL) 作为这对文件导致的压缩时间差异 - 我的代数非常容易出错,但我认为这可以归结为 L^2x(1- x) 在一些复杂但积极的事情上,这表明对于一对文件,您总是希望先压缩短文件,所以按照我之前所说的,最好的时间表是按排序顺序排列,最短的文件在前。

【讨论】:

  • 是的,看起来是这样,而且几乎被 Ralor 的回答所证明。很好的解释!但是,如果您建议对问题本身进行一些更改,以使最佳顺序不是升序或降序,那就太好了。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-10-17
  • 2014-10-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多