香农编码及十进制小数转换为二进制(Python实现)

香农编码

前段时间在《信息论与编码》这门课中学习了各种编码方式,这里记录一下香农码。

设离散无记忆信源 ( X P ( X ) ) = ( a 1 a 2 … a n p ( a 1 ) p ( a 2 ) … p ( a n ) ) \begin{pmatrix} X \\ P(X) \end{pmatrix}=\begin{pmatrix} a_1&a_2&\ldots&a_n \\ p(a_1)&p(a_2)&\ldots&p(a_n) \end{pmatrix} (XP(X))=(a1p(a1)a2p(a2)anp(an)) 0 ≤ p ( a i ) ≤ 1 0\le p(a_i) \le 1 0p(ai)1 ∑ i = 1 n p ( a i ) = 1 \sum_{i=1}^{n}p(a_i)=1 i=1np(ai)=1

编码步骤

  1. 将信源符号按概率从大到小的顺序排列。不妨令 p ( a 1 ) ≥ p ( a 2 ) ≥ … ≥ p ( a n ) p(a_1)\ge p(a_2)\ge\ldots\ge p(a_n) p(a1)p(a2)p(an)
  2. p ( a 0 ) = 0 p(a_0)=0 p(a0)=0,用 p a ( a j ) ( j = i + 1 ) p_a(a_j)(j=i+1) pa(aj)(j=i+1)表示第i个码字的累加概率,

p a ( a j ) = ∑ i = 0 j − 1 p ( a i ) , j = 1 , 2 , … , n p_a(a_j)=\sum_{i=0}^{j-1}p(a_i),j=1,2,\ldots,n pa(aj)=i=0j1p(ai),j=1,2,,n

  1. 确定满足下列不等式的整数 k i k_i ki,并令其为第i个码字的长度

− log ⁡ 2 p ( a i ) ≤ k i < 1 − log ⁡ 2 p ( a i ) -\log_2p(a_i)\le k_i\lt1-\log_2p(a_i) log2p(ai)ki<1log2p(ai)

  1. p a ( a j ) p_a(a_j) pa(aj)用二进制表示,并取小数点后 k i k_i ki位作为符号 a i a_i ai的编码。

举例

对单符号离散无记忆信源 ( X P ( X ) ) = ( a 1 a 2 a 3 a 4 a 5 a 6 a 7 0.2 0.19 0.18 0.17 0.15 0.1 0.01 ) \begin{pmatrix} X \\ P(X) \end{pmatrix}=\begin{pmatrix} a_1&a_2&a_3&a_4&a_5&a_6&a_7 \\ 0.2&0.19&0.18&0.17 &0.15&0.1&0.01 \end{pmatrix} (XP(X))=(a10.2a20.19a30.18a40.17a50.15a60.1a70.01)编香农码,计算编码效率。

a i a_i ai a 1 a_1 a1 a 2 a_2 a2 a 3 a_3 a3 a 4 a_4 a4 a 5 a_5 a5 a 6 a_6 a6 a 7 a_7 a7
p ( a i ) p(a_i) p(ai)0.20.190.180.170.150.10.01
p a ( a j ) p_a(a_j) pa(aj)00.20.390.570.740.890.99
k i k_i ki3333347
码字00000101110010111101111110

η = H ( X ) ∑ i = 1 7 p ( a i ) k i ≈ 83.08 % \eta=\frac{H(X)}{\sum_{i=1}^7 p(a_i)k_i}\approx 83.08 \% η=i=17p(ai)kiH(X)83.08%

由此可见,香农码的编码效率其实并不高。

小数十进制转二进制

由此可见,编香农码重要的一步是要把累加概率求二进制数。而这其实也不难,分别回顾一下小数和整数的转换方法。

  1. 整数部分:除以2取余数,直到商为0为止。
  2. 小数部分:乘以2取整数,直到小数为0(或到达要求精度)为止。

Python实现

学习完香农码和小数转二进制的方法后,就要开始写作业了。看着一大堆概率在我眼前等着我去算,就瞬间没劲了。

这个时候,我想起了大师Larry Wall的名言:

优秀程序员应该有三大美德:懒惰、急躁和傲慢。

其中,懒惰,是一种品质,它会使你花很大力气去规避过度的精力消耗,敦促你写出节省体力的程序。

没错,有计算机为什么我还要手算小数二进制,而且Python不是有bin()吗,直接用啊!

然后就报错了:image-20240317130743766

之前都没有试过,难道说bin()只能转换整型数吗?结果一查还真是这样。

怎么办呢?既然已经知道了原理,那当然是自己写啊!

def enhanced_bin(num: float, has_prefix=True) -> str:
    """
    加强版的bin()
    :param num: 转换为二进制的数
    :param has_prefix: 是否需要"0b"前缀
    :return: 返回二进制字符串
    """
    zheng = int(num)
    # 整数直接转换
    if zheng == num:
        if has_prefix:
            return bin(num)
        else:
            return bin(num)[2:]
    xiao = num - zheng
    zheng = bin(zheng)
    xiao_str = ''
    # 小数每次乘2取个位
    while xiao:
        xiao *= 2
        xiao_str += str(int(xiao))
        if xiao >= 1:
            xiao -= 1
    if not has_prefix:
        zheng = zheng[2:]
    return zheng + '.' + xiao_str

再来测试一下:

arr = [2, 32, 34453, 56675766567, 1.2, 0.5, 0.25, 0.24, 0.125, 3483939.22, 3483939, 0.22]
for i in arr:
    print(enhanced_bin(i, has_prefix=False))

测试结果:


成功完成任务!

  • 30
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Forgotten Legend

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值