一、设计目标
实现任意Q符号信源的二进制费诺编码,其中Q>10且由用户自行输入,信源的概率分布也由用户输入。展示编码结果、平均码长、信源熵、编码效率。
二、设计步骤
(1)输入模块:实现对Q和信源概率数组(odds)的输入,并对其作出判断:Q是否满足大于10、概率数组长度是否为Q、概率和是否为1;
(2)编码模块:先创建一个两行Q列的元胞,第一行存储各符号的概率,第二行存储各符号的码字,都为char类型。再对输入的概率进行排序并分组:分组由函数fano实现,每一次的fano函数都对传入函数的元胞cel作一次分组,并为两组概率各自分配码元(概率大者分配0,小者分配1),最后判断两个分组长度是否为1,不为1则对该分组继续进行fano函数操作(递归到各组都只包含一个概率为止);
(3)输出模块:打印编码结果和编码效率等参数。
三、代码实现
%%fano code
%%输入Q值并判断
while(1)
Q = input("请输入Q的值(Q>10): ");
if(Q <= 10)
disp('不满足Q>10的条件,请重新输入Q值!');
else
break;
end
end
%%输入Q个元素的概率并判断
%%提供待测数组Q=11:[0.1274 0.1416 0.0199 0.1428 0.0989 0.0152 0.0435 0.0855 0.1497 0.1509 0.0246]
while(1)
odds = input("请以数组形式[]输入Q个元素各自的概率:");
length_odds = length(odds);
if(length_odds ~= Q)
disp('输入概率个数错误(不为Q),请重新输入!');
elseif(sum(odds) ~= 1)
disp(['概率之和为:',num2str(sum(odds))]);%帮助概率纠错
disp('输入概率数值有误,请重新输入!');
else
break;
end
end
%%对概率进行排序与分组
[sort_odds , sort_index] = sort(odds , 'descend');%分别为排序后的概率与最初的索引
pri_cell = creat_charcell(2,Q);%第一行储存分组后的概率,第二行码字
aft_cell=fano(Q,sort_odds,pri_cell,1);
%%编码结果打印
disp('--------------------------------------');
disp('费诺编码结果:');
len_fanocode = 0;%计算平均码长
for k = 1 : Q
index = sort_index(k);
num = num2str(index);
od = num2str(sort_odds(k));%字符串形式的概率
co = char(aft_cell(2,k));%字符串形式的编码
len_fanocode = len_fanocode + length(co)*sort_odds(k);
disp(['第',num,'个符号的概率为:',od,',费诺编码为:',co]);
end
%%编码参数计算与展示
l = len_fanocode;%平均码长
odds(find(odds==0))=[];
H = sum(odds.*log2(1./odds));%信源熵
eff = H / (l*log2(2));%编码效率
disp('--------------------------------------');
disp(['平均码长:',num2str(l)]);
disp(['信源熵:',num2str(H)]);
disp(['编码效率:',num2str(eff)]);
%---------------------------------函数分割线---------------------------------%
%%fano函数:对已有的Q个递减概率进行费诺分组
%%输入参数分别为:待排序序列长度,待排序序列,元胞,本次操作的起始列数
%%输出参数分别为:输出元胞
function output = fano(Q,sort_odds,cel,start)
sum_odds = [];sub_odds = [];%比较各处分组的概率和,以确定分组位置
sumi_odds = 0; subi_odds = 0;
sum1 = sum(sort_odds);%待分组概率之和
for i = 1:Q
sumi_odds = sumi_odds + sort_odds(i);%概率累加值
subi_odds = sum1 - sumi_odds;%累加后剩余值
sum_odds = [sum_odds ,sumi_odds];
sub_odds = [sub_odds ,subi_odds];
end
diff = abs(sum_odds - sub_odds);%两组概率之差
[useless1,index_min] = min(diff);%概率差最小值及其索引
%%根据索引进行分组
group1 = sort_odds(1:index_min);
group2 = sort_odds(index_min + 1:end);
%%转化为字符串以便保存在cell数组中
group1_str = num2str(group1);
group2_str = num2str(group2);
Q1 = length(group1);
Q2 = length(group2);
%%修改output第一行--分组
cel(1,start) = {group1_str};
cel(1,index_min+start) = {group2_str};
%%分配码元,大的分组分配0,小的分组分配1
if(sum(group1) >= sum(group2))
cel(2,start:index_min+start-1) = {[char(cel(2,start)),'0']};
cel(2,index_min+start:index_min+start+Q2-1) = {[char(cel(2,index_min+start)),'1']};
else
cel(2,start:index_min+start-1) = {[char(cel(2,start)),'1']};
cel(2,index_min+start:index_min+start+Q2-1) = {[char(cel(2,index_min+start)),'0']};
end
output = cel;
%%递归
if(Q1 ~= 1)
output = fano(Q1,group1,output,start);
end
if(Q2 ~= 1)
output = fano(Q2,group2,output,start+index_min);
end
end
%---------------------------------函数分割线---------------------------------%
%%charcell函数:创建a*b维的cell类型的空char数组
function charcell = creat_charcell(a,b)
charcell=cell(a,b);
for i=1:a
for j=1:b
charcell(i,j)=cellstr(num2str(charcell{i,j}));
%%charcell中的元组由double→char→{char}
end
end
end
四、运行结果
此处以提供的待测数组为测试对象:
五、写在最后
(1)如果需要编码的符号数不足10个,修改输入模块即可;
(2)哈夫曼、香农编码如果有需要请留言(好像是叫留言或是评论?);
(3)代码较为冗杂,作者编程水平有限。第一次发csdn,不太会丰富内容和排版,但希望能为有需要者(eg.信息论与编码课程相关)提供一些帮助。