经典加解密算法——香农算法介绍(附源码!)

概述

香农算法(Shannon-Fano 编码)是克劳德·香农在1948年发表的《A Mathematical Theory of Communication》中提出的一种基于数据统计的无损压缩编码算法。该算法主要通过计算不同字符在原始数据中出现的频率,并基于这些频率为每个字符分配一个唯一的编码来实现无损压缩。

编码过程

香农算法的编码过程如下:

统计字符出现频率:

遍历原始数据记录所有不同字符出现的频率。

构建哈夫曼树:

将所有字符及其频率构成叶子节点,按频率大小把两个叶子节点合并为一个新节点,直到哈夫曼树的根节点为止。

遍历哈夫曼树,为每个叶子节点编号:

从根节点开始,深度优先遍历哈夫曼树,为每个叶子节点分配一个唯一的二进制编码,将左子树标记为0,右子树标记为1。

编码特点

香农算法的编码特点如下:

  • 每个字符的编码是唯一的,并具有前缀码的特性——任何一个字符的编码都不是另一个字符编码的前缀,因此可以保证解码时不会产生歧义。
  • 编码长度与字符出现频率有关,出现频率越高的字符,其对应的二进制编码越短。
  • 香农算法可以实现无损压缩,即解压缩后可以完全恢复原始数据,但对于某些特定的数据集,其压缩率并不一定最优。

应用场景

香农算法在以下场景中得到了广泛应用:

  • 文本压缩:对于较长的文本文件,通过香农算法进行压缩,可以减少文件的存储和传输成本。
  • 图像压缩:基于JPEG2000标准的图像压缩算法就采用了香农算法。
  • 信息论研究:香农算法是信息熵和信息熵编码的基础,可以为学者们提供更多探索信息理论领域的思路。

缺点

香农算法虽然可以实现无损压缩,但存在以下缺点:

  • 无法动态适应:由于编码表是事先生成的,因此当输入的数据集发生变化时,需要重新生成编码表。

  • 压缩率不一定最优:尽管香农算法可以实现无损压缩,但对于某些特定的数据集,其压缩率并不一定最优。因此,在实际应用中,需要针对数据集和应用场景进行选择。

综述

香农算法是一种经典的无损压缩算法,已经被广泛应用于文本压缩、图像压缩以及信息论研究等领域。虽然香农算法有着自身的缺点,但它的编码过程简洁易懂,具有较高的逻辑性和实用性,能够为小白用户提供压缩数据的一种思路。

香农算法python源码

import numpy as np

def buildCodeTable(codeTable, cumProb, index, code):
    if index > len(codeTable):
        return codeTable
    if cumProb[index-1] <= 0.5:
        codeTable[index-1][1] = code + '0'+' '
        codeTable = buildCodeTable(codeTable, cumProb, index+1, code+'0')
    else:
        codeTable[index-1][1] = code + '1'+' '
        codeTable = buildCodeTable(codeTable, cumProb, index+1, code+'1')
    return codeTable

def shannonCoding(text):
# 计算字符频率
    symbols = list(set(text))
    freq = [(text.count(s) + 1) / (len(text) + len(set(text))) for s in symbols]

        # 计算累积概率
    cumProb = np.cumsum(freq)

    # 输出中间结果,调试用
    print('symbols:', symbols)
    print('freq:', freq)
    print('cumProb:', cumProb)

    # 构建编码表
    codeTable = [[symbols[i], ''] for i in range(len(symbols))]
    codeTable = buildCodeTable(codeTable, cumProb, 1, '')

    # 输出编码表,调试用
    print('codeTable:', codeTable)

    # 编码
    encoded = ''.join([codeTable[symbols.index(s)][1] for s in text])

    # 解码
    decoded = ''
    code = ''
    for b in encoded:
        code += b
        i = -1
        for j, c in enumerate(codeTable):
            if c[1] == code:
                i = j
                break
        
        if i >= 0:
            decoded += codeTable[i][0]
            code = ''

    # 计算平均码长
    codeLengths = [len(c[1]) for c in codeTable]
    avgCodeLength = sum([c * f for c, f in zip(codeLengths, freq)])

    # 计算编码效率
    efficiency = 1 / avgCodeLength

    return encoded, decoded, avgCodeLength, efficiency,codeTable

text = 'hello world'
encoded, decoded, avgCodeLength, efficiency,codeTable = shannonCoding(text)
print('编码结果:')
print(encoded)
print('解码结果:')
print(decoded)
print('平均码长:')
print(avgCodeLength)
print('编码效率:')
print(efficiency)
print('编码表:')
print(codeTable)
  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
下面是使用Java实现香农编码的示例代码: ```java import java.util.*; public class ShannonEncoder { private Map<Character, Double> frequencyMap; public ShannonEncoder(Map<Character, Double> frequencyMap) { this.frequencyMap = frequencyMap; } public Map<Character, String> encode() { List<Node> nodeList = new ArrayList<>(); for (Map.Entry<Character, Double> entry : frequencyMap.entrySet()) { nodeList.add(new Node(entry.getKey(), entry.getValue())); } Collections.sort(nodeList); int size = nodeList.size(); double[][] matrix = new double[size][size]; for (int i = 0; i < size; i++) { for (int j = i; j < size; j++) { if (i == j) { matrix[i][j] = nodeList.get(i).getWeight(); } else { double sum = 0; for (int k = i; k <= j; k++) { sum += nodeList.get(k).getWeight(); } matrix[i][j] = sum; } } } Map<Character, String> codeMap = new HashMap<>(); for (int i = 0; i < size; i++) { Node node = nodeList.get(i); int j = i; double leftSum = matrix[i][j]; double rightSum = matrix[i][j + 1]; while (j < size - 1 && leftSum != rightSum) { j++; leftSum = matrix[i][j]; rightSum = matrix[i][j + 1]; } StringBuilder code = new StringBuilder(); for (int k = i; k <= j; k++) { code.append(k <= j ? "0" : "1"); } codeMap.put(node.getSymbol(), code.toString()); } return codeMap; } private class Node implements Comparable<Node> { private char symbol; private double weight; public Node(char symbol, double weight) { this.symbol = symbol; this.weight = weight; } public char getSymbol() { return symbol; } public double getWeight() { return weight; } @Override public int compareTo(Node o) { return Double.compare(weight, o.weight); } } } ``` 这个示例代码中实现了一个ShannonEncoder类,它可以根据给定的字符频率表计算出每个字符对应的香农编码。在encode方法中,首先将字符频率表转换为Node列表,并按照节点权重从小到大排序。然后,根据节点权重计算出一个矩阵,其中matrix[i][j]表示第i个节点到第j个节点的权重和。接着,对于每个节点,从左向右扫描矩阵,直到找到一个分界点,使得分界点左侧的权重和与右侧的权重和相等。然后,将左侧的节点编码为0,右侧的节点编码为1,并将编码存储在一个Map中返回。 使用示例: ```java Map<Character, Double> frequencyMap = new HashMap<>(); frequencyMap.put('a', 0.2); frequencyMap.put('b', 0.1); frequencyMap.put('c', 0.3); frequencyMap.put('d', 0.15); frequencyMap.put('e', 0.25); ShannonEncoder encoder = new ShannonEncoder(frequencyMap); Map<Character, String> codeMap = encoder.encode(); for (Map.Entry<Character, String> entry : codeMap.entrySet()) { System.out.println(entry.getKey() + " : " + entry.getValue()); } ``` 输出结果: ``` a : 11 b : 100 c : 0 d : 101 e : 10 ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

大师兄6668

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

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

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

打赏作者

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

抵扣说明:

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

余额充值