费诺编码的MATLAB递归实现

一、设计目标

        实现任意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

四、运行结果

此处以提供的待测数组为测试对象:

费诺编码的MATLAB递归实现_第1张图片

 五、写在最后

(1)如果需要编码的符号数不足10个,修改输入模块即可;

(2)哈夫曼、香农编码如果有需要请留言(好像是叫留言或是评论?);

(3)代码较为冗杂,作者编程水平有限。第一次发csdn,不太会丰富内容和排版,但希望能为有需要者(eg.信息论与编码课程相关)提供一些帮助。

你可能感兴趣的:(信息论与编码,matlab)