一、算术编码起源
早在1948年,香农就提出将信源符号依美出现的概率降序排序,用符号序列累计概率的二进值作为对信源的编码,并从理论上论证了它的优越性。1960年,Peter Elias 发现无需排序,只要编、解码端使用相同的符号顺序即可,提出了算术编码的概念。Elias 没有公布他的发现,因为他知道算术编码在数学上虽然成立,但不可能在实际中实现。1976年,R. Pasco和] Rissanen分别用定长的寄存器实现了有限精度的算术编码。1979年Rssnen和G. G Langdon一起将算术编码系统化,并于1981年实现了二进制编码。1987 年Witten等人发表了一个实用的算术编码程序.即CACM87(后用于ITU_T的H.263视频压缩标准)。同期,IBM公司发表了著名的Q编码器(后用于JPEG和JBIG图像压缩标准)。从此,算术编码迅速得到了广泛的注意。
二、算术编码过程
算术编码的基本原理是将编码的消息表示成实数0和1之间的一个同隔(Interval), 消息越长,编码表示它的间隔就越小,表示这一间隔所需的二进制位就越多。
编码步骤:
1.根据二维码中提取的有效文本信息,计算各个信号源出现的频率,将[0, 1)这个区间分成若干段,这样每个信号源就会有自己对应的区间了;
2.将[0.1)这个区间设置为初始间隔;
3.从RFID中提取出来有效信号,共有6组,每组信号长度不等;
4.将待处理的信号,一个一个信号源的读入,每读入一个信号,就将该信号源在[0.1)上的范围等比例的缩小到最新得到的间隔中。
5.然后依次迭代,不断重复进行步骤4,直到该组中信号源全部被读完为止;
6.若通过步骤5计算得到的该组信号的概率区间记为: [n1,n2),则该组信号对应的红外报警器码计算方式为:(n1+n2)*10^x/2)%256 (其中x为各组总的迭代次数,x取值范围1-5)
7.重复步骤4、5、6将6组信号全部处理完成,最终得到红外报警器的六字节开启码。
三、算术编码示例
1.从二维码中提取的有效信息为:A1B2C3D4,则每个信号源出现的概率分别为A:0.1;B: 0.2; C: 0.3; D: 0.4;
2.将[0, 1)这个区间设置为初始间隔,则每个信号源所在的概率区间(在这里我们规定以A、B、C、D的顺序划分概率区间)为:
3.从RFID中提取的6组信号(在这里我们规定,按照数据读取的先后顺序进行排序)分别为:AAB、DCAB、CCB、ABCD、BCA、AC;
4.首先处理第一组信号,将信号源一个一个读入,先读入A.得到概率区间为[0,01);
5.重复步骤4,读入还是A,因为A在初始区间内是占整个区间的前10%, 因此对应的也是占上一次编码间隔的前10%,所以此时编码区间变为: [0,0.01)了;再然后我们读入B,B占整个区间的10%-30%,所以读入之后也占上一个编码区间的10%~30%, 读入之后得到新的编码操作区间为[0.001, 0.003);
6.通过公式计算得到第一组信号的报警码为0x02 (十进制为2);
7.重复步骤4、5、 6将6组信号全部处理完成,最终得到红外报警器的六字节开启码为: 0x02、0x38、 0x98、 0xD0、 0xA3、 0x04
8.附各组信号编码过程:
四、关键分析
设Low和High分别表示“当前间隔”的下边界和上边界,CodeRange为编码间隔的长度,LowRange(symbol)和HighRange(symbol)分别代表为了事件symbol分配的初始间隔下边界和上边界。上述过程的实现可用伪代码描述如下:
set Low to 0
set High to 1
while there are input symbols do
take a symbol
CodeRange = High – Low
High = Low + CodeRange *HighRange(symbol)
Low = Low + CodeRange * LowRange(symbol)
end of while
output Low
五、具体代码如下
import org.w3c.dom.ranges.Range;
import java.math.BigDecimal;
public class Ssbm {
//private static Object Range;
public static double getIndex(char s){
char xinhaos[] = { 'A', 'B', 'C', 'D' };
double gsilv[] = {0.1,0.2,0.3,0.4};
for (int i = 0;i<xinhaos.length;i++){
if (s==xinhaos[i]){
// System.out.println(gsilv[i]);
return gsilv[i];
}
}
return 0;
}
public static double getLow(char s){
char xinhaos[] = { 'A', 'B', 'C', 'D' };
double gsilv[] = {0.0, 0.1,0.3,0.6};
for (int i = 0;i<xinhaos.length;i++){
if (s==xinhaos[i]){
return gsilv[i];
}
}
return 0;
}
// public static double getLcForLow(char res[]){
// double Lc = 1;
// for (int i=0;i< res.length-1;i++){
// Lc = Lc * getIndex(res[i]);
// }
// return Lc;
// }
public static void getInfos() {
String[] rresResult = {"AAB","DCAB","CCB","ABCD","BCA","AC"};
for (int i =0;i<rresResult.length;i++){
// String res[] = {"D", "C", "A","B"};
char[] res =rresResult[i].toCharArray();
double low = 0;
double high =1;
double codeRange = 0;
double LowRange = 0;
double range = 1.0;
double zwl;
//codeRange = getLcForLow(res);
for (int j = 0; j < res.length; j++) {
LowRange = getLow(res[j]);
codeRange = getIndex(res[j]);
range = high - low;
low = add(low , mul(range,LowRange));
high = add(low , mul(range,codeRange));
//System.out.println(low);
//System.out.println(high);
}
zwl = mul(add(low , high),Math.pow(10,res.length)/2)%256;
String zwll= Integer.toHexString((int) zwl); //十进制转成十六进制
System.out.println(zwll);
}
}
public static double mul(double v1,double v2){
BigDecimal b1 = new BigDecimal(Double.toString(v1));
BigDecimal b2 = new BigDecimal(Double.toString(v2));
return b1.multiply(b2).doubleValue();
}
public static double add(double v1,double v2){
BigDecimal b1 = new BigDecimal(Double.toString(v1));
BigDecimal b2 = new BigDecimal(Double.toString(v2));
return b1.add(b2).doubleValue();
}
public static double div(double v1,double v2,int scale){
if(scale<0){
throw new IllegalArgumentException("The scale must be a positive integer or zero");
}
BigDecimal b1 = new BigDecimal(Double.toString(v1));
BigDecimal b2 = new BigDecimal(Double.toString(v2));
return b1.divide(b2,scale,BigDecimal.ROUND_HALF_UP).doubleValue();
}
public static void main(String args[]){
// System.out.println("hello world");
//getIndex("B");
//getHigh("A");
//getLow("D");
getInfos();
}
}