編程計算字符串的信息熵

一、引入信息熵計算公式

信息論是現代世界非常重要的一種觀念,你肯定聽過“比特”“信息熵”之類的詞,這些概念似乎都比較技術化。那不搞技術的人也需要了解嗎?答案是非常需要。在我看來,信息論並不僅僅是技術理論,更是一種具有普世價值的思想。瞭解信息論,你就多了一種觀察世界的眼光。甚至可以從信息論中推導出一種人生觀來。

一段消息所包含的信息量,並不僅僅由這條消息的長短決定。這就好像人生一樣,活了同樣歲數的兩個人,他們人生經歷的豐富程度可能大不相同。

現代信息論的祖師爺。克勞德·香農有一個洞見:一個東西信息量的大小,取決於它克服了多少不確定性。

舉個生活中的例子。有個人生活非常規律,平時會去的就是家裏、公司、餐館、健身房這四個地方。如果我僱你做特工,幫我觀察這個人,隨時向我彙報他的位置。那你每次給我的信息無非就是“家裏/公司/餐館/健身房”中的一個——即使你不告訴我,我猜對的概率也有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、運行程序,查看結果

在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述

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