一、引入信息熵计算公式
信息论是现代世界非常重要的一种观念,你肯定听过“比特”“信息熵”之类的词,这些概念似乎都比较技术化。那不搞技术的人也需要了解吗?答案是非常需要。在我看来,信息论并不仅仅是技术理论,更是一种具有普世价值的思想。了解信息论,你就多了一种观察世界的眼光。甚至可以从信息论中推导出一种人生观来。
一段消息所包含的信息量,并不仅仅由这条消息的长短决定。这就好像人生一样,活了同样岁数的两个人,他们人生经历的丰富程度可能大不相同。
现代信息论的祖师爷。克劳德·香农有一个洞见:一个东西信息量的大小,取决于它克服了多少不确定性。
举个生活中的例子。有个人生活非常规律,平时会去的就是家里、公司、餐馆、健身房这四个地方。如果我雇你做特工,帮我观察这个人,随时向我汇报他的位置。那你每次给我的信息无非就是“家里/公司/餐馆/健身房”中的一个——即使你不告诉我,我猜对的概率也有。所以你给我的信息价值不大。
但如果这个人满世界跑,今天在土耳其,明天在沙特阿拉伯,我完全猜不到他在哪里,你给我的信息可就非常值钱了。你提供信息之前。这个人的位置对我来说具有不确定性。你的信息,克服了这个不确定性。原来的不确定性越大,你的信息就越有价值。
我们用一个简单的公式来量化这个思想。香龙从统计物理学中借鉴了一个概念——信息熵。这个概念看起来吓人,其实很简单,就是一段消息的平均信息量。一个东西信息量的大小,取决于它克服了多大的不确定性。香农对信息量的定义是,如果一个字符出现在这个位置的概率是,那么这个字符的信息量就是。
香浓举例说。假如我们有一个完美公正的硬币,每次抛出正面朝上的概率都是,如果这一次抛出的结果是正面朝上,这个消息的信息量就是。而信息熵,就是把一条消息中出现的所有字符做信息量的加权平均。
还是用硬币的例子,表示正面朝上,表示反面朝上。一系列投掷结果可能是:。如果正反面出现的概率都正好是,那么这一串消息不管多长,信息熵都是, 香农规定信息量的单位是“比特”,这个信息熵就是比特。这意味着,对消息中的每个字符,至少需要比特的信息才能编码。
如果这个硬币“不公平”,出现的次数比出现更多,比如,那么信息熵就不是比特了。在这个例子中,出现的概率是,出现的概率是,信息熵就变成了比特。
信息熵跟消息的长度没有必然关系,它表示的是这段消息中字符的“不可预测性”。一段字符中出现的各种字符越是杂乱无章,越具有多样性,信息熵就越高。比如这样一个字符串——。每个字母都不一样,它的信息熵是比特。而如果字符串中有很多重复的字母,那它的“可预测性”就很高,信息商就会变低,比如字符串“”的信息熵只有比特。
信息熵 =
这里为了简化,计算时只考虑了字符出现的频率,如果从语法和内容角度进一步考虑,每个字符的可预测性,信息熵就是另一种个数值了。
信息之所以叫“熵”,是因为它跟统计物理学中熵的公式几乎一样,物理学里“熵”大致描述了一个系统的混乱程度——信息熵也是如此。越是看上去杂乱无章的消息,信息熵就越高,信息量就越大。
如果一段消息只能从和两个数字中选,它的信息熵最大也只有比特;如果能从个字母中选,信息熵最大可以达到比特。
如果是从个汉字中选,信息熵则可以达到比特。
这就是为什么中文是一种更高效的语言。
你如果没有看懂上述数学部分不要紧,只要记住一句话:可供选择的范围越广,选择的信息量就越大。
——摘自万维钢《你有你的计划,世界另有计划》
信息熵公式:
二、编程计算上文涉及的信息熵
- 编写程序“计算信息熵.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、运行程序,查看结果