java实现三种信源编码

信息通过信道传输到信宿的过程即为通信。
信源编码的目的是要减少冗余,提高编码效率。

一、香农码
编码步骤:
1)将个信源符号按概率递减的方式进行排列;
2)按香农不等式计算出每个信源符号的码长;
3)为了编成惟一可译码,计算第i个信源符号的累加概率
4)将累加概率 用二进制数表示;
5)取 对应二进制数的小数点后位构成该信源符号的二进制码字。

public class Shannon {

/**Shannon算法
*
* 1.符号按概率递减的方式排列
* 2.计算每一个符号的码长l
* 3.计算累加概率
* 4.将累加概率用二进制表示
* 5.取小数点后l位为信源符号的二进制码字
*
*/
double cumu = 0;//累加概率
Data data;//符号集
double prob;//概率
int codelength;//码长
// List list = new ArrayList();

/*累加概率的二进制转换*/
public static String getBinary(double cumu,int length){

String codeword = "";
for(int i = 0; i < length; i++){
cumu *= 2;//小数转化二进制乘以2
if(cumu >= 1){
cumu -= 1;
codeword += 1;//大于1取1
}else{
codeword += 0;//小于1取0
}
}
return codeword;
}
/*实现香农编码*/
public List isShannon(List list){
for(int i = 0;i < list.size();i++){
data = (Data)list.get(i);//得到每一个符号
prob = data.getProb();//取出符号概率
codelength =(int)Math.ceil(Math.log(1/prob)/Math.log((double)2));//计算码长
data.setCodeLength(codelength);//添加码长
data.setCodeword(getBinary(cumu, codelength));//添加码字
list.set(i, data);//改变符号列表信息
cumu += prob;//累加概率
}
return list;
}
}

二、费诺码
编码步骤:
1)信源符号以概率递减的次序排列起来;
2)将排列好的信源符号按概率值划分成两大组,使每组 的概率之和接近于相等,并对每组各赋予一个二元码符号“0”和“1”;
3)将每一大组的信源符号再分成两组,使划分后的两个组的概率之和接近于相等,再分别赋予一个二元码符号;
4)依次下去,直至每个小组只剩一个信源符号为止;
5)信源符号所对应的码字即为费诺码。

public class Fano {

/**Fano算法
*
* 1.信源符号按概率递减的方式排列
* 2.将排列好的符号分成两组,使每组的概率之和接近相等并对赋于0和1
* 3.将每一大组再分组,同2
* 4.依次下去,直到只剩一个信源符号
* 5.信源符号所对应的码字即为费诺码
*/
Data data;//符号集
double prob;//概率
int codelength;//码长
/*费诺码的实现*/
public List isFano(List list){
double[] arr = new double[list.size()];
for(int i = 0;i < list.size();i++){
data = (Data)list.get(i);//得到每一个符号
prob = data.getProb();//取出符号概率
arr[i] = prob;//将概率存放在一个数组中
}
String[] codeword = getGroup(arr,0,arr.length-1);//符号编码
for(int i = 0; i < codeword.length; i++){
data = (Data)list.get(i);//得到每一个符号
data.setCodeword(codeword[i]);//加上码字
data.setCodeLength(codeword[i].length());//加上码长
list.set(i, data);//加上符号
}


return list;//返回改变的列表
}
/*用分组法求出符号的编码*/
public static String[] getGroup(double[] a,int i,int j){

String[] p = new String[a.length]; //返回的字符编码
for(int t = 0; t < a.length; t++){
p[t] = "";//初始化
}
int flag = 0;//分组间隔点
if(i < j){
//采用递归法,将数组分为两半
double sum = 10;//比较中间量
for(int k = i; k <= j; k++){
//取累和间距最小量
if(Math.abs(sumGroup(a,i,k)-sumGroup(a, k+1, j)) < sum){
//以flag为中间点,分别累加左边和右边,然后比较
sum=Math.abs(sumGroup(a,i,k)-sumGroup(a, k+1, j));
flag = k;//取出中间点
}
}
String[] p1 = getGroup(a, i, flag);//递归第一组(左边)
String[] p2 = getGroup(a, flag+1, j);//递归第二组(右边)
for(int m = i; m <= flag; m++){
p[m] = "0" + p1[m];//第一组赋值0
}
for(int m = flag+1; m <= j; m++){
p[m] = "1" + p2[m];//第二组赋值1
}
}

return p;//得到分组后的码字
}
/*求数组第i位到第j位的和*/
public static double sumGroup(double[] a, int i, int j ){
double total = 0;
for(int k = i; k <= j; k++){
total = total + a[k];
}
return total;
}
}

三、霍夫曼码
编码步骤:
1)将q个信源符号按概率递减的方式排列起来;
2)用“0”、“1”码符号分别表示概率最小的两个信源符号,并将这两个概率最小的信源符号合并成一个新的符号,从而得到只包含q-1个符号的新信源,称之为S信源的S1缩减信源;
3)将缩减信源中的符号仍按概率大小以递减次序排列,再将其最后两个概率最小的符号合并成一个符号,并分别用“0”、“1”码符号表示,这样又形成了由q-2个符号构成的缩减信源S2;
4)依次继续下去,直到缩减信源只剩下两个符号为止,将这最后两个符号分别用“0”、“1”码符号表示;
5)从最后一级缩减信源开始,向前返回,沿信源缩减方向的反方向取出所编的码元,得出各信源符号所对应的码符号序列,即为对应信源符号的码字。

public class Huffman {

/**Huffman算法
*
* 1.信源符号按概率递减的方式排列
* 2.用0、1分别表示最小的两个符号,将这两个符号合并成一个新的符号,从而得到一个新信源
* 3.将新信源重排列,再取最后两位最小的分别用0、1表示,并合并,得到一个缩减信源
* 4.重复步骤3,直到只有两符号为止,将这两个符号分别用0、1表示
* 5.从最后一级缩减信源开始向前返回,反向列出所编码元,得到各信源符号的码字
*/
/*霍夫曼的实现*/
public void isHuffman(List l){
List list = new ArrayList();//新建一个表
list.addAll(l);//复制
for(int i = list.size()-1; i >=0 ; i--){
double d = ((Data)list.get(i)).getProb();//得到概率
TreeNode root = new TreeNode(d);//将概率作为根结点
list.remove(i);//移除最后一项
list.add(i, root);//将结点添加到最后一项
}
createTree(list);//创建树
TreeNode root = (TreeNode)list.get(0);//得到最终树的根结点
list.clear();//清空列表
printTree(root,list);//根据树的叶子得到码字、码长
for(int i = 0; i < list.size(); i ++){
Data data = new Data();
data.setSymbol(((Data)l.get(i)).getSymbol());//取出符号
data.setProb(((Data)l.get(i)).getProb());//取出概率
data.setCodeLength(((Data)list.get(i)).getCodeLength());//取出码长
data.setCodeword(((Data)list.get(i)).getCodeword());//取出码字
l.set(i, data);//将新得的Data对象加到列表
}
}
//创建树
public static void createTree(List list){
double d1 = ((TreeNode)list.get(list.size()-1)).data;//取出倒数第一个
double d2 = ((TreeNode)list.get(list.size()-2)).data;//取出倒数第二个
double cumu = d1 + d2;//最小两个相加
TreeNode root = new TreeNode(cumu);//将和作为树根结点
root.left = (TreeNode)list.get(list.size()-1);//将倒数第一个作为左孩子
root.right = (TreeNode)list.get(list.size()-2);//将倒数第二个作为右孩子
list.remove(list.size()-1);//移除最后二个
list.remove(list.size()-1);
sortList(list, root);//将根结点加到列表,并排序
if(list.size() > 1){
createTree(list);//只要list大于1时就递归创建树
}else{

}
}
//将树转为码字
public static void printTree(TreeNode root,List list){
Data data = new Data();
if(root.left != null){
root.left.codeword = root.codeword + "0";//左孩子加0
printTree(root.left,list);//遍历左孩子
}
if(root.right != null){
root.right.codeword = root.codeword + "1";//右孩子加1
printTree(root.right,list);//遍历右孩子
}
//如果是树的叶子,刚将其作为对应的码字
if(root.left == null && root.right == null){
data.setProb(root.data);//概率
data.setCodeLength(root.codeword.length());//码长
data.setCodeword(root.codeword);//码字
Util.sortList(list, data);//将Data对象加入列表
}
}
/*将根结点顺序插入list*/
public static void sortList(List list,TreeNode root){
int i = 0;
if(list.size() == 0){
list.add(root);//list为空,直接插入
}else{
while(i < list.size()){
if(((TreeNode)list.get(i)).data <= root.data){
list.add(i,root);//将根结点按顺序插入
break;
}else{
i++;
}
}
if(i == list.size()){
list.add(i,root);//走到最后为最小,直接插入
}
}
}
}
/*创建一棵树*/
class TreeNode {

public double data;//结点数据
public String codeword = "";//码字
public TreeNode left;//左孩子
public TreeNode right;//右孩子

public TreeNode(double data){
this.data = data;
}
}
  • 2
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值