音频压缩算法-算术编码
参考:https://blog.csdn.net/qq_36752072/article/details/77986159
一 .算法思路:
1.1编码:
1.首先的准备工作——按照各信源信号好出现的频率,将[0, 1)这个区间分成若干段,那么每个信号源就会有自己
对应的区间了;
2.将[0, 1)这个区间设置为初始间隔;
3.然后编码过程就是,按照待处理的信号,一个一个信号源读入,每读入一个信号,就将该信号源在[0, 1)上的范围等比例的缩小到 最新得到的间隔中。
4.然后依次迭代,不断重复进行步骤3,直到最后信号中的信源信号全部读完为止;
伪代码:
set lowLevel = 0
set highLevel = 1
input symbol
do
delta = highLevel - lowLevel
lowLevel = lowLevel + delta * symbol[i].lowLevel
highLevel = highLevl + delta * symbol[i].highLevel
while symbol traversed
output lowLevel
1.2解码:
- 待解码的数据,首先我们仍然需要在编码时候的那项准备工作,即获取创建一个编码解码表,但是由于我们是在同一个类中完成的,那么我们把该对应表作为类的一个属性就可以实现获取表了;
- 准备工作完成之后,我们就可以开始正式的解码工作了,首先判断待解码的数据在哪个范围内,将该范围对应的信源信号输出即可;
- 重复步骤2,按理说,按照这样的方法,解码会一直进行下去,无穷不尽,直到达到计算机的精度为止,那么为了正确的进行解码,我们需要对解码的次数进行限定,即我们首先需要事先知道,解码后的信源信号的长度,然后在进行解码,这样才能比较完美的进行解码。
伪代码:
input code
do
find the Interval which contains the code
set range = highLevel - lowLevel
set code = (code - lowLevel) / range
while times out
二.具体实现
#include<iostream>
#include<map>
#include<string>
using namespace std;
//区间类,主要的就是用来存放上下界
class Range{
public:
Range(double l=0.0,double h=1.0,double delta=1.0):low(l),high(h),deltaLevel(delta){}
double getlow(){ return this->low;} //获得下限
double gethigh() {return this->high;}//获得上限
double getdalataLevel() {return this->deltaLevel;} //获得区间大小
private:
double low;
double high;
double deltaLevel;
};
//算术编码类
class Arithmeticcoding{
public:
Arithmeticcoding(){
length=0;
}
~Arithmeticcoding(){}
void requirecode(); //获得编码,创建初始对应表
double encode(string str); //编码
string decode(double value); //解码
int getLength();
private:
int length;
std::map<char, Range> map;
};
//获得编码,创建初始对应表
void Arithmeticcoding::requirecode(){
char ch;
double l=0.0;
double h=0.0;
double probability;
while(true){
cin>>ch;
if(ch=='#'){
break;
}else{
cin>>probability;
l=h;
h=h+probability;
Range range(l,h,probability);
map.insert(std::pair<char,Range>(ch,range));
}
}
}
//编码
double Arithmeticcoding::encode(string str){
double lowRange=0.0,highRange=1.0;
for(auto i=str.begin();i!=str.end();++i){
// 使用map来通过字符完成概率的查找
// map[*i]表示的就是对应的字符的出现概率
double delta=highRange-lowRange;
highRange=lowRange+delta*map[*i].gethigh();
lowRange=lowRange+delta*map[*i].getlow();
++length;
}
return lowRange;
}
//解码
string Arithmeticcoding::decode(double value){
double low,high;
string symbol="";
// 译出第一个编码
for(auto i=map.begin();i!=map.end();i++){
if((i->second).getlow()<value&&(i->second).gethigh()>value){
low=(i->second).getlow();
high=(i->second).gethigh();
symbol+=(i->first);
}
}
//解码
for(int i=0;i<length-1;++i){
double detal;
detal=high-low;
value -= low;
value /= detal;
for(auto j=map.begin();j!=map.end();j++){
if((j->second).getlow()<value&&(j->second).gethigh()>value){
low=(j->second).getlow();
high=(j->second).gethigh();
symbol+=(j->first);
break;
}
}
}
return symbol;
}
int main(){
Arithmeticcoding ac;
ac.requirecode();
string str;
cin>>str;
double encode1=ac.encode(str);
cout<<encode1<<endl;
cout<<ac.decode(encode1)<<endl;
return 0;
}
测试数据:
A
0.1
B
0.4
C
0.2
D
0.3
#
CADACDB
0.514388
CADACDB