问题描述

给定一张灰度图,其像素为长度为\(n\)的灰度值序列:{\(p_1,p_2,\cdots,p_n\)},其中\(p_i \in [0:1:255]\)可表示为8位二进制数。现使用一种变位压缩方式对图像进行压缩,具体压缩过程如下:
将{\(p_1,p_2,\cdots,p_n\)}分割成为\(m\)段:\(S_1,S_2,\cdots,S_m\);
\(l[i]\)为\(S_i\)段的像素数,要求\(l[i]\leq 256\);
\(h_i\)为\(S_i\)段中最大像素灰度值对应的二进制位数,则有$$h_i=\left \lceil\log\left(\max\limits_{p_k \in s_i}{p_k}+1\right)\right\rceil$$
\(b[i]\)为\(S_i\)段中所有像素的灰度值二进制表示的最小位数,则有$$h_i \leq b[i] \leq 8$$
每个分段\(S_i\)的段头都有11位
\(b[i]\leq 8\)的二进制表示:3位
\(l[i]\leq 256\)的二进制表示:8位
\(S_i\)段的二进制总位数(占用空间):\(11+b[i]\times l[i]\);

可以看出,不同的分段方案\(T=\{S_1,S_2,\cdots,S_j\}\)导致不同的变位压缩结果,即占用不同大小的总空间。现需要确定空间占用最小的分段方案,即$$\min\limits_{T}\left{\sum_{i=1}^{j}(b[i]\times l[i]+11)\right}$$

约束条件

图像压缩问题中约束条件是:\(l[i]\leq 256\),即每个段中的像素数不超过256个。也就是说只要不违背这个约束条件的所有解均是可行解。

目标函数

图像压缩问题是最小化问题,其目标函数是:各个分段占用空间之和,即

 

\[\sum_{i=1}^{j}(b[i]\times l[i]+11) \]

 

算法设计

子问题边界参数化

在该问题中,我们将问题的左侧边界固定,右侧边界进行参数化,所有子问题可建模为:像素序列\(P_i=\{p_1,p_2,\cdots,p_i\}, i=1,2,\cdots,n\)。

递推方程设计

设\(s[i]\)是像素序列\(P_i=\{p_1,p_2,\cdots,p_i\},i=1,2,\cdots,n\)的最优分段所需存储的位数,则递推关系设计如下:

 

\[\begin{cases} s[i]=\min\limits_{1\leq j \leq \min\{i,256\}}\left\{s[i-j]+j\times b_{max}(i-j+1,i)+11\right\} \\ s[0] = 0 \end{cases} \]

 

其中,\(b_{max}(i-j+1,i)=\left\lceil \log \left ( \max\limits_{p_k \in S_m}p_k + 1 \right )\right\rceil \leq 8\)。

算法的伪代码描述
 function Compress(n, p, l, s, b)
 	lmax ← 256;header ← 11;s[0] ← 0
	for i = 1 → n do
		b[i] ← length(p[i])
		bmax ← b[i]
		s[i] ← s[i − 1] + bmax
		l[i] ← 1
		for j = 2 → min i, lmax do
			if bmax < b[i − j + 1] then
 				bmax ← b[i − j + 1]
 			if s[i] > s[i − j] + j ∗ bmax then
 				s[i] ← s[i − j] + j ∗ bmax
				 l[i] ← j
 		s[i] ← s[i] + header
 	return s, b, l
 end function
 
function Traceback(n, i, s, l)
	i ← 0
	if n == 0 then
		return
	Traceback(n − l[n], i, s, l)
	s[i + +] ← n − l[n]
end function
算法时空效率估计

(1)估计算法Compress的时间复杂度,试给出详细过程。

Compress只需\(O(n)\),由于在算法中j的次数不超过256次,故对每一个确定的i可在\(O(1)\)时间内完成,因此时间复杂度为\(O(n)\).

(2)估计算法Traceback的时间复杂度,试给出详细过程。

由于数组\(l[i],b[i]\)记录了最优分段所需的信息,最优分段的最后一段的段长度和像素位数分别存在\(l[n],b[n]\)中,其前一段的段长度和像素位数存储于\(l[n-l[n]]\)和\(b[n-l[n]]\)中,依次类推,在\(O(n)\)时间内构造最优解。

编码实现
#include<iostream>
#include<vector>
using namespace std;
//灰度值二进制位数 
int Length(int i){
	int k = 1;
	i = i/2;
	while(i > 0){
		k ++;
		i = i/2;
	}
	return k;
}
//迭代备忘录实现动态规划 
void Compress(int n,vector<int> &p,vector<int> &s,vector<int> &l,vector<int> &b){
	int lmax = 256;
	int header = 11;//分段首部 
	s[0] = 0;
	int bmax;
	for(int i = 1;i <= n;i ++){
		b[i] = Length(p[i]);
		bmax = b[i]; 
		s[i] = s[i-1] + bmax;
		l[i] = 1;
		int k = 0;
		if(i > lmax){
			k = lmax;
		}else{
			k = i;
		}//k取lmax和 i的较小值 
		for(int j = 2;j <= k;j ++){
			if(bmax < b[i-j+1]){
				bmax = b[i-j+1];
			}
			if(s[i] > s[i-j]+j*bmax){
				s[i] = s[i-j]+j*bmax;
				l[i] = j;//记录分段 
			}
		}
		s[i] = s[i] + header;//加上首部 
	}
}
//递归追踪解 
void Traceback(int n,int& i,vector<int> &s,vector<int> &l){
	if(n == 0){
		return;
	}
	Traceback(n-l[n],i,s,l);
	s[i++] = n-l[n];//用s表记录分段位置 
}

int main(){
	int n;
	cin >> n;
	vector<int> s,b,l,p;
	for(int i = 0;i <= n;i ++){
		s.push_back(0);
		b.push_back(0);
		l.push_back(0);
		p.push_back(0);
	}
	for(int i = 1;i <= n;i ++){
		int p1;
		cin >> p1;
		p[i] = p1;
	}
	Compress(n,p,s,l,b);
	cout << "最小存储位数:" << s[n] << endl; 
	int m = 0;
	Traceback(n,m,s,l);
	s[m] = n;
	cout << "共分段数:" << m << endl;
	for(int j = 1;j <= m;j ++){
		l[j] = l[s[j]];
		b[j] = b[s[j]];
	}
	for(int j = 1;j <= m;j ++){
		cout << "此段个数:" << l[j] << "所需储存位数:" << b[j] << endl; 
	}
	return 0;
}
结果展示

图像压缩问题_时间复杂度

结束语

若结局非你所愿,请在尘埃定前奋力一搏

作者:花城