编程计算字符串的信息熵

一、引入信息熵计算公式

信息论是现代世界非常重要的一种观念,你肯定听过“比特”“信息熵”之类的词,这些概念似乎都比较技术化。那不搞技术的人也需要了解吗?答案是非常需要。在我看来,信息论并不仅仅是技术理论,更是一种具有普世价值的思想。了解信息论,你就多了一种观察世界的眼光。甚至可以从信息论中推导出一种人生观来。

一段消息所包含的信息量,并不仅仅由这条消息的长短决定。这就好像人生一样,活了同样岁数的两个人,他们人生经历的丰富程度可能大不相同。

现代信息论的祖师爷。克劳德·香农有一个洞见:一个东西信息量的大小,取决于它克服了多少不确定性。

举个生活中的例子。有个人生活非常规律,平时会去的就是家里、公司、餐馆、健身房这四个地方。如果我雇你做特工,帮我观察这个人,随时向我汇报他的位置。那你每次给我的信息无非就是“家里/公司/餐馆/健身房”中的一个——即使你不告诉我,我猜对的概率也有14\displaystyle\frac{1}{4}。所以你给我的信息价值不大。

但如果这个人满世界跑,今天在土耳其,明天在沙特阿拉伯,我完全猜不到他在哪里,你给我的信息可就非常值钱了。你提供信息之前。这个人的位置对我来说具有不确定性。你的信息,克服了这个不确定性。原来的不确定性越大,你的信息就越有价值。

我们用一个简单的公式来量化这个思想。香龙从统计物理学中借鉴了一个概念——信息熵。这个概念看起来吓人,其实很简单,就是一段消息的平均信息量。一个东西信息量的大小,取决于它克服了多大的不确定性。香农对信息量的定义是,如果一个字符出现在这个位置的概率是pp,那么这个字符的信息量就是I=plog2p\displaystyle I=-plog_2p

香浓举例说。假如我们有一个完美公正的硬币,每次抛出正面朝上的概率都是12\displaystyle\frac{1}{2},如果这一次抛出的结果是正面朝上,这个消息的信息量就是12log212=1-\displaystyle \frac{1}{2}log_2\frac{1}{2}=1。而信息熵,就是把一条消息中出现的所有字符做信息量的加权平均。

还是用硬币的例子,11表示正面朝上,00表示反面朝上。一系列投掷结果可能是:00111001010011100101。如果正反面出现的概率都正好是12\displaystyle\frac{1}{2},那么这一串消息不管多长,信息熵都是12×1+12×1=1\displaystyle \frac{1}{2}\times1+\frac{1}{2}\times1=1, 香农规定信息量的单位是“比特”,这个信息熵就是11比特。这意味着,对消息中的每个字符,至少需要11比特的信息才能编码。

如果这个硬币“不公平”,出现11的次数比出现00更多,比如11011100111101110011,那么信息熵就不是11比特了。在这个例子中,00出现的概率是30%30\%11出现的概率是70%70\%,信息熵就变成了(0.3×log20.3+0.7×log20.7)=0.88-(0.3\times log_20.3+0.7\times log_20.7)=0.88比特。

信息熵跟消息的长度没有必然关系,它表示的是这段消息中字符的“不可预测性”。一段字符中出现的各种字符越是杂乱无章,越具有多样性,信息熵就越高。比如这样一个字符串——asdogrpfknasdogrpfkn。每个字母都不一样,它的信息熵是(i=1100.1×log20.1)=3.3\displaystyle -(\sum_{i=1}^{10}0.1\times log_20.1)=3.3比特。而如果字符串中有很多重复的字母,那它的“可预测性”就很高,信息商就会变低,比如字符串“asdfasdfooasopasdfasdfooasop”的信息熵只有2.52.5比特。

pa=314ps=314pd=214pf=214po=314pp=114\displaystyle p_a = \frac{3}{14},p_s =\frac{3}{14},p_d = \frac{2}{14},p_f = \frac{2}{14},p_o = \frac{3}{14},p_p =\frac{1}{14}
信息熵 = (palog2pa+pslog2ps+pdlog2pd+pflog2pf+polog2po+pplog2pp)=(314log2314+314log2314+214log2214+214log2214+314log2314+114log2114)=(0.47622694742923890.47622694742923890.40105070315108640.40105070315108640.47622694742923890.2719539230041146)=2.50273617159400442.5\displaystyle-(p_a\mathrm log_2p_a+p_s \mathrm log_2p_s+p_d\mathrm log_2p_d+p_f\mathrm log_2p_f+p_o\mathrm log_2p_o+p_p\mathrm log_2p_p)=-(\frac{3}{14}\mathrm log_2\frac{3}{14}+\frac{3}{14}\mathrm log_2\frac{3}{14}+\frac{2}{14}\mathrm log_2\frac{2}{14}+\frac{2}{14}\mathrm log_2\frac{2}{14}+\frac{3}{14}\mathrm log_2\frac{3}{14}+\frac{1}{14}\mathrm log_2\frac{1}{14})=-(-0.4762269474292389-0.4762269474292389-0.4010507031510864-0.4010507031510864-0.4762269474292389-0.2719539230041146)=2.5027361715940044\approx 2.5

这里为了简化,计算时只考虑了字符出现的频率,如果从语法和内容角度进一步考虑,每个字符的可预测性,信息熵就是另一种个数值了。

信息之所以叫“熵”,是因为它跟统计物理学中熵的公式几乎一样,物理学里“熵”大致描述了一个系统的混乱程度——信息熵也是如此。越是看上去杂乱无章的消息,信息熵就越高,信息量就越大。

如果一段消息只能从0011两个数字中选,它的信息熵最大也只有11比特;如果能从2626个字母中选,信息熵最大可以达到4.74.7比特。
i=126126log2126=4.7004397181410924.7\displaystyle \sum_{i=1}^{26}\frac{1}{26}\mathrm log_2\frac{1}{26}=4.700439718141092 \approx 4.7

如果是从25002500个汉字中选,信息熵则可以达到11.311.3比特。
i=1250012500log212500=11.28771237954888411.3\displaystyle \sum_{i=1}^{2500}\frac{1}{2500}\mathrm log_2\frac{1}{2500}=11.287712379548884 \approx 11.3

这就是为什么中文是一种更高效的语言。

你如果没有看懂上述数学部分不要紧,只要记住一句话:可供选择的范围越广,选择的信息量就越大。

——摘自万维钢《你有你的计划,世界另有计划》

信息熵公式:e=i=1npilog2pie = \displaystyle \sum_{i=1}^np_i \cdot log_{2}p_i

二、编程计算上文涉及的信息熵

  • 编写程序“计算信息熵.py”
"""
计算信息熵
"""

from math import *

s = "0011100101"
e = -(0.5 * log(0.5, 2) + 0.5 * log(0.5, 2))
print("[{}]的信息熵 = {}".format(s, e))

s = "1101110011"
e = -(0.3 * log(0.3, 2) + 0.7 * log(0.7, 2))
print("[{}]的信息熵 = {}".format(s, round(e, 2)))

s = "asdogrpfkn"
e = 0
for i in range(10):
	e = e - 0.1 * log(0.1, 2)
print("[{}]的信息熵 = {}".format(s, round(e, 2)))

s = "asdfasdfooasop"

pa = 3 / 14
ps = 3 / 14
pd = 2 / 14
pf = 2 / 14
po = 3 / 14
pp = 1 / 14

e = -(pa * log(pa, 2) + ps * log(ps, 2) + pd * log(pd, 2) + pf * log(pf, 2) + po * log(po, 2) + pp * log(pp, 2))
print("[{}]的信息熵 = {}".format(s, round(e, 2)))

# 从26个字母中选的信息熵
e = 0
for i in range(26):
    e = e - 1 / 26 * log(1 / 26, 2)
print("[从26个字母中选]的信息熵 = {}".format(round(e, 2)))

# 从2500个汉字中选的信息熵
e = 0
for i in range(2500):
    e = e - 1 / 2500 * log(1 / 2500, 2)
print("[从2500个汉字中选]的信息熵 = {}".format(round(e, 1)))
  • 运行程序,查看结果
    在这里插入图片描述

三、编写Python函数计算字符串的信息熵

1、编写程序“计算字符串的信息熵.py”

在这里插入图片描述

2、运行程序,查看结果

在这里插入图片描述
在这里插入图片描述

四、编写Java方法计算字符串的信息熵

1、创建CalculateEntropy类

在这里插入图片描述

package net.hw.lesson21;

import java.util.*;

/**
 * 功能:计算字符串信息熵
 * 作者:华卫
 * 日期:2020年05月22日
 */
public class CalculateEntropy {
    public static double entropy(String str) {
        int n = str.length();
        Set<String> set = new HashSet<>();
        for (int i = 0; i < n; i++) {
            set.add(String.valueOf(str.charAt(i)));
        }
        List<Double> p = new ArrayList<>();
        for (String x : set) {
            int count = 0;
            for (int i = 0; i < n; i++) {
                if (x.equalsIgnoreCase(String.valueOf(str.charAt(i)))) {
                    count++;
                }
            }
            p.add(count * 1.0 / n);
        }
        Double e = 0.0;
        for (int i = 0; i < p.size(); i++) {
            e = e - p.get(i) * Math.log(p.get(i)) / Math.log(2);
        }
        return e.doubleValue();
    }

    public static void main(String[] args) {
        String str;
        double e;
        Scanner sc = new Scanner(System.in);

        System.out.print("字符串:");
        str = sc.nextLine();

        e = entropy(str);
        System.out.println("信息熵:" + String.format("%.2f",e));
    }
}

2、运行程序,查看结果

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章