【贪心算法】哈夫曼编码Python实现

news2024/11/17 7:31:54

文章目录

    • @[toc]
      • 哈夫曼编码
      • 不同编码方式对比
      • 前缀码
      • 构造哈夫曼编码
      • 哈夫曼算法的正确性
        • 贪心选择性质
          • 证明
        • 最优子结构性质
          • 证明
        • 总结
      • `Python`实现
      • 时间复杂性

哈夫曼编码

  • 哈夫曼编码是广泛用于数据文件压缩的十分有效的编码方法,其压缩率通常为 20 % 20\% 20% 90 % 90\% 90%
  • 哈夫曼编码算法使用字符在文件中出现的频率表来建立一个用 0 0 0 1 1 1串表示各字符的最优表示方式

不同编码方式对比

  • 假设有一个数据文件包含 100000 100000 100000个字符,要用压缩的方式存储它,该文件中共有 6 6 6个不同字符出现,各字符出现的频率如下表所示
a a a b b b c c c d d d e e e f f f
频率(千次) 45 45 45 13 13 13 12 12 12 16 16 16 9 9 9 5 5 5
  • 有多种方法表示文件中的信息,考察用 0 0 0 1 1 1码串表示字符的方法,即每个字符用唯一的一个 0 0 0 1 1 1串表示
  • 若使用定长码,则表示 6 6 6个不同的字符需要 3 3 3位: a = 000 a = 000 a=000 b = 001 b = 001 b=001 ⋯ \cdots f = 101 f = 101 f=101,用这种方法对整个文件进行编码需要 300000 300000 300000
  • 使用变长码要比使用定长码好得多,给出现频率高的字符较短的编码,出现频率较低的字符以较长的编码,可以大大缩短总码长,下表给出了一种变长码编码方案,其中 a a a用一位串 0 0 0表示,而字符 f f f 4 4 4位串 1100 1100 1100表示
a a a b b b c c c d d d e e e f f f
变长码 0 0 0 101 101 101 100 100 100 111 111 111 1101 1101 1101 1100 1100 1100
  • 用这种编码方案,整个文件的总码长为 ( 45 × 1 + 13 × 3 + 12 × 3 + 16 × 3 + 9 × 4 + 5 × 4 ) × 1000 = 224000 (45 \times 1 + 13 \times 3 + 12 \times 3 + 16 \times 3 + 9 \times 4 + 5 \times 4) \times 1000 = 224000 (45×1+13×3+12×3+16×3+9×4+5×4)×1000=224000位,比用定长码方案好,总码长减小约 25 % 25\% 25%,事实上,这是该文件的最优编码方案

前缀码

  • 对每一个字符规定一个 0 0 0 1 1 1串作为其代码,并要求任一字符的代码都不是其他字符代码的前缀,这种编码称为前缀码
  • 编码的前缀性质可以使译码方法非常简单,由于任一字符的代码都不是其他字符代码的前缀,从编码文件中不断取出代表某一字符的前缀码,转换为原始字符串,即可逐个译出文件中的所有字符
    • 例如上表中的变长码就是一种前缀码,对于给定的 0 0 0 1 1 1 001011101 001011101 001011101可以唯一地分解为 0 0 0 0 0 0 101 101 101 1101 1101 1101,因而其译码为 a a b e aabe aabe
  • 译码过程需要方便地取出编码的前缀,因此需要表示前缀码的合适的数据结构,为此可以用二叉树作为前缀编码的数据结构
  • 在表示前缀码的二叉树中,树叶代表给定的字符,并将每个字符的前缀码看作从树根到代表该字符的树叶的一条路径
  • 定长编码的二叉树表示

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  • 最优前缀编码的二叉树表示

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  • 最优前缀码的二叉树总是一棵完全二叉树,即树中任意结点都有 2 2 2个儿子,在一般情况下,若 C C C是字符集,表示其最优前缀码的二叉树中恰有 ∣ C ∣ |C| C个叶子,每个叶子对应字符集中的一个字符,该二叉树恰有 ∣ C ∣ − 1 |C| - 1 C1个内部结点
  • 给定编码字符集 C C C及其频率分布 f f f,即 C C C中任一字符 c c c以频率 f ( c ) f(c) f(c)在数据文件中出现, C C C的一个前缀码编码方案对应一棵二叉树 T T T,字符 c c c在树中的深度记为 d T ( c ) d_{T}(c) dT(c) d T ( c ) d_{T}{(c)} dT(c)也是字符 c c c的前缀码长,该编码方案的平均码长定义为 B ( T ) = ∑ c ∈ C f ( c ) d T ( c ) B(T) = \displaystyle\sum\limits_{c \in C}{f(c) d_{T}(c)} B(T)=cCf(c)dT(c),使平均码长达到最小的前缀码编码方案称为 C C C的最优前缀码

构造哈夫曼编码

  • 哈夫曼提出了构造最优前缀码的贪心算法,由此产生的编码方案称为哈夫曼算法

  • 哈夫曼算法以自底向上的方式构造表示最优前缀码的二叉树 T T T

  • 算法以 ∣ C ∣ |C| C个叶节点开始,执行 ∣ C ∣ − 1 |C| - 1 C1次的“合并”运算后产生最终要求的树 T T T

  • 首先用字符集 C C C中每个字符 c c c的频率 f ( c ) f(c) f(c)初始化优先队列 Q Q Q,然后不断地从优先队列 Q Q Q中取出具有最小频率的两棵树 x x x y y y f ( x ) ≤ f ( y ) f(x) \leq f(y) f(x)f(y)),将它们合并为一棵新树 z z z z z z的频率是 x x x y y y的频率之和,新树 z z z x x x为其左儿子,以 y y y为其右儿子,经过 n − 1 n - 1 n1次的合并后,优先队列中只剩下一棵树,即所要求的树 T T T


哈夫曼算法的正确性

  • 要证明哈夫曼算法的正确性,只要证明最优前缀码问题具有贪心选择性质和最优子结构性质
贪心选择性质
  • C C C是编码字符集, C C C中字符 c c c的频率为 f ( c ) f(c) f(c),又设 x x x y y y C C C中具有最小频率的两个字符,则存在 C C C的最优前缀码使 x x x y y y具有相同码长且仅最后一位编码不同
证明
  • 设二叉树 T T T表示 C C C的任意一个最优前缀码,下面证明可以对 T T T进行适当修改后,得到一棵新的二叉树 T ′ ′ T^{''} T′′,使得在新树中, x x x y y y是最深叶子且为兄弟,同时新树 T ′ ′ T^{''} T′′表示的前缀码也是 C C C的最优前缀码,如果能做到,则 x x x y y y T ′ ′ T^{''} T′′表示的最优前缀码中就具有相同的码长且仅最后一位编码不同
  • b b b c c c是二叉树 T T T的最深叶子且为兄弟,不失一般性,可设 f ( b ) ≤ f ( c ) f(b) \leq f(c) f(b)f(c) f ( x ) ≤ f ( y ) f(x) \leq f(y) f(x)f(y),由于 x x x y y y C C C中具有最小频率的两个字符,故 f ( x ) ≤ f ( b ) f(x) \leq f(b) f(x)f(b) f ( y ) ≤ f ( c ) f(y) \leq f(c) f(y)f(c)
  • 首先在树 T T T中交换叶子 b b b x x x的位置得到树 T ′ T^{'} T,然后在树 T ′ T^{'} T中再交换叶子 c c c y y y的位置,得到树 T ′ ′ T^{''} T′′,如下图所示

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  • 由此可知,树 T T T T ′ T^{'} T表示的前缀码的平均码长之差为

B ( T ) − B ( T ′ ) = ∑ c ∈ C f ( c ) d T ( c ) − ∑ c ∈ C f ( c ) d T ′ ( c ) = f ( x ) d T ( x ) + f ( b ) d T ( b ) − f ( x ) d T ′ ( x ) − f ( b ) d T ′ ( b ) = f ( x ) d T ( x ) + f ( b ) d T ( b ) − f ( x ) d T ( b ) − f ( b ) d T ( x ) = ( f ( b ) − f ( x ) ) ( d T ( b ) − d T ( x ) ) ≥ 0 \begin{aligned} B(T) - B(T^{'}) &= \displaystyle\sum\limits_{c \in C}{f(c) d_{T}(c)} - \displaystyle\sum\limits_{c \in C}{f(c) d_{T^{'}}(c)} \\ &= f(x) d_{T}{(x)} + f(b) d_{T}{(b)} - f(x) d_{T^{'}}(x) - f(b) d_{T^{'}}(b) \\ &= f(x) d_{T}{(x)} + f(b) d_{T}{(b)} - f(x) d_{T}(b) - f(b) d_{T}(x) \\ &= (f(b) - f(x))(d_{T}(b) - d_{T}(x)) \geq 0 \end{aligned} B(T)B(T)=cCf(c)dT(c)cCf(c)dT(c)=f(x)dT(x)+f(b)dT(b)f(x)dT(x)f(b)dT(b)=f(x)dT(x)+f(b)dT(b)f(x)dT(b)f(b)dT(x)=(f(b)f(x))(dT(b)dT(x))0

  • 类似地,可以证明在 T ′ T^{'} T中交换 y y y c c c的位置也不增加平均码长,即 B ( T ′ ) − B ( T ′ ′ ) B(T^{'}) - B(T^{''}) B(T)B(T′′)也是非负的
  • 由此可知, B ( T ′ ′ ) ≤ B ( T ′ ) ≤ B ( T ) B(T^{''}) \leq B(T^{'}) \leq B(T) B(T′′)B(T)B(T),另一方面, T T T表示的前缀码是最优的,故 B ( T ) ≤ B ( T ′ ′ ) B(T) \leq B(T^{''}) B(T)B(T′′),因此 B ( T ) = B ( T ′ ′ ) B(T) = B(T^{''}) B(T)=B(T′′),即 T ′ ′ T^{''} T′′表示的前缀码也是最优前缀码,且 x x x y y y具有最长的码长,同时仅最后一位编码不同
最优子结构性质
  • T T T是表示字符集 C C C的一个最优前缀码的完全二叉树, C C C中字符 c c c的出现频率为 f ( c ) f(c) f(c),设 x x x y y y是树 T T T中的两个叶子且为兄弟, z z z是它们的父亲,若将 z z z看作具有频率 f ( z ) = f ( x ) + f ( y ) f(z) = f(x) + f(y) f(z)=f(x)+f(y)的字符,则树 T ′ = T − {   x , y   } T^{'} = T - \set{x , y} T=T{x,y}表示字符集 C ′ = ( C − {   x , y   } ) ∪ {   z   } C^{'} = (C - \set{x , y}) \cup \set{z} C=(C{x,y}){z}的一个最优前缀码
证明
  • 首先证明 T T T的平均码长 B ( T ) B(T) B(T)可用 T ′ T^{'} T的平均码长 B ( T ′ ) B(T^{'}) B(T)来表示
  • 事实上,对任意 c ∈ C − {   x , y   } c \in C - \set{x , y} cC{x,y} d T ( c ) = d T ′ ( c ) d_{T}(c) = d_{T^{'}}(c) dT(c)=dT(c),故 f ( c ) d T ( c ) = f ( c ) d T ′ ( c ) f(c) d_{T}(c) = f(c) d_{T^{'}}(c) f(c)dT(c)=f(c)dT(c)
  • 另一方面, d T ( x ) = d T ( y ) = d T ′ ( z ) + 1 d_{T}(x) = d_{T}(y) = d_{T^{'}}(z) + 1 dT(x)=dT(y)=dT(z)+1,故 f ( x ) d T ( x ) + f ( y ) d T ( y ) = ( f ( x ) + f ( y ) ) ( d T ′ ( z ) + 1 ) = f ( x ) + f ( y ) + f ( z ) d T ′ ( z ) \begin{aligned} f(x) d_{T}(x) + f(y) d_{T}(y) &= (f(x) + f(y))(d_{T^{'}}(z) + 1) \\ &= f(x) + f(y) + f(z) d_{T^{'}}(z) \end{aligned} f(x)dT(x)+f(y)dT(y)=(f(x)+f(y))(dT(z)+1)=f(x)+f(y)+f(z)dT(z)
  • 由此可知, B ( T ) = B ( T ′ ) + f ( x ) + f ( y ) B(T) = B(T^{'}) + f(x) + f(y) B(T)=B(T)+f(x)+f(y)
  • T ′ T^{'} T表示的字符集 C ′ C^{'} C的前缀码不是最优的,则有 T ′ ′ T^{''} T′′表示的 C ′ C^{'} C的前缀码使得 B ( T ′ ′ ) < B ( T ′ ) B(T^{''}) < B(T^{'}) B(T′′)<B(T),由于 z z z被看作 C ′ C^{'} C中的一个字符,故 z z z T ′ ′ T^{''} T′′中是一树叶,若将 x x x y y y加入树 T ′ ′ T^{''} T′′中作为 z z z的儿子,则得到表示字符集 C C C的前缀码的二叉树 T ′ ′ ′ T^{'''} T′′′,且有 B ( T ′ ′ ′ ) = B ( T ′ ′ ) + f ( x ) + f ( y ) < B ( T ′ ) + f ( x ) + f ( y ) = B ( T ) B(T^{'''}) = B(T^{''}) + f(x) + f(y) < B({T^{'}}) + f(x) + f(y) = B(T) B(T′′′)=B(T′′)+f(x)+f(y)<B(T)+f(x)+f(y)=B(T),这与 T T T的最优性矛盾,故 T ′ T^{'} T表示的 C ′ C^{'} C的前缀码是最优的
总结
  • 由贪心选择性质和最优子结构性质立即推出哈夫曼算法是正确的,即哈夫曼算法产生 C C C的一棵最优前缀编码树

Python实现

from heapq import heappop, heappush
from collections import defaultdict


class HuffmanNode:
    def __init__(self, char, freq, left=None, right=None):
        self.char = char  # 节点代表的字符
        self.freq = freq  # 节点对应字符的频率
        self.left = left  # 左子节点
        self.right = right  # 右子节点

    def __lt__(self, other):
        return self.freq < other.freq


def build_frequency_table(text):
    frequency_table = defaultdict(int)  # 存储字符频率的字典, 默认值为 0

    for char in text:
        frequency_table[char] += 1  # 统计字符频率

    return frequency_table


def build_huffman_tree(frequency_table):
    priority_queue = []  # 存储 Huffman 节点的优先队列(最小堆)

    for char, freq in frequency_table.items():
        node = HuffmanNode(char, freq)

        heappush(priority_queue, node)  # 将每个字符的频率作为优先级, 构建最小堆

    while len(priority_queue) > 1:
        left_node = heappop(priority_queue)  # 弹出频率最小的节点作为左子节点
        right_node = heappop(priority_queue)  # 弹出频率次小的节点作为右子节点

        parent_freq = left_node.freq + right_node.freq  # 父节点的频率是左右子节点频率之和
        parent_node = HuffmanNode(None, parent_freq, left_node, right_node)

        heappush(priority_queue, parent_node)  # 将父节点插入优先队列

    return heappop(priority_queue)  # 返回最后剩余的根节点


def generate_codes(node, current_code, codes):
    if node.char:
        codes[node.char] = current_code  # 如果节点代表一个字符, 将字符和对应的编码存入字典
    else:
        generate_codes(node.left, current_code + '0', codes)  # 递归生成左子树编码, 将当前编码加上 '0'
        generate_codes(node.right, current_code + '1', codes)  # 递归生成右子树编码, 将当前编码加上 '1'


def huffman_encoding(text):
    frequency_table = build_frequency_table(text)  # 构建字符频率表
    huffman_tree = build_huffman_tree(frequency_table)  # 构建 Huffman 树

    codes = {}  # 存储字符和对应的 Huffman 编码的字典
    generate_codes(huffman_tree, '', codes)  # 生成 Huffman 编码

    encoded_text = ''.join(codes[char] for char in text)  # 将文本编码为 Huffman 编码

    return encoded_text, huffman_tree


def huffman_decoding(encoded_text, huffman_tree):
    decoded_text = ''
    current_node = huffman_tree

    for bit in encoded_text:
        if bit == '0':
            current_node = current_node.left  # 如果是 '0', 移动到左子节点
        else:
            current_node = current_node.right  # 如果是 '1', 移动到右子节点

        if current_node.char:  # 如果当前节点代表一个字符
            decoded_text += current_node.char  # 将字符添加到解码文本中

            current_node = huffman_tree  # 重置当前节点为根节点

    return decoded_text


text = 'Hello, Huffman!'
print(f'原始文本: {text}')

encoded_text, huffman_tree = huffman_encoding(text)
print(f'编码后的文本: {encoded_text}')

decoded_text = huffman_decoding(encoded_text, huffman_tree)
print(f'解码后的文本: {decoded_text}')
原始文本: Hello, Huffman!
编码后的文本: 01110100010010100010110110111000111111000110111001001
解码后的文本: Hello, Huffman!

时间复杂性

  • 算法用最小堆实现优先队列 Q Q Q,初始化优先队列需要 O ( n ) O(n) O(n)计算时间,由于最小堆的删除结点和插入结点运算均需 O ( log ⁡ n ) O(\log{n}) O(logn)时间, n − 1 n - 1 n1次的合并共需要 O ( n log ⁡ n ) O(n \log{n}) O(nlogn)计算时间
  • 因此,关于 n n n个字符的哈夫曼算法的计算时间为 O ( n log ⁡ n ) O(n \log{n}) O(nlogn)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1665335.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

RK3568 学习笔记 : Linux emmc 内核启动 rootfs 根文件系统无法正常挂载问题的分析

问题描述 平台 &#xff1a; NanoPi-R5C 开发板 RK3568 平台。 手动编译的 Linux 内核&#xff0c;结果发现大概率 emmc 无法正常初始化&#xff0c;导致 rootfs 根文件系统无法正常挂载 Linux 内核版本&#xff1a; 6.1 Linux 内核代码位置&#xff1a; https://github.com…

Github图片显示不出来?两步解决!

很多同学可能和我一样&#xff0c;在GitHub中找一些项目或者资料的时候&#xff1b;总是会看到一些图片显示不出来&#xff0c;或者数学公式乱码&#xff1a; 比如这样 还有这样 其实这个主要是因为DNS污染导致的&#xff0c;具体大家可以百度&#xff0c;这边不详细介绍。 解决…

Java入门基础学习笔记4——开发Helloworld入门程序

Java程序开发的三个步骤&#xff1a; 1&#xff09;编写代码 2&#xff09;编译代码 3&#xff09;运行代码 注意事项&#xff1a; 第一个java程序建议使用记事本来编写。 建议代码文件名全英文、首字母大写、满足驼峰模式&#xff0c;源代码文件的后缀必须是.java 注意&a…

37 | 什么时候会使用内部临时表?

union 执行流程 (select 1000 as f) union (select id from t1 order by id desc limit 2);key=PRIMARY,说明第二个子句用到了索引 id。 Extra 字段,表示在对子查询的结果集做 union 的时候,使用了临时表 (Using temporary)。 执行流程 创建一个内存临时表,这个临时表只有…

OpenAI 人工智能搜索产品即将推出,文本和图像都支持?!

OpenAI 人工智能搜索产品即将推出 OpenAI 计划于下周一(5 月 13 日)正式公布其人工智能搜索产品,不过报道中强调具体公告日期可能发生变化。OpenAI 拒绝对路透社的报道置评。外媒 The Information 在今年 2 月的报道中指出,OpenAI 一直在秘密开发其自家网络搜索服务,并将获…

LeetCode例题讲解:844.比较含退格的字符串

给定 s 和 t 两个字符串&#xff0c;当它们分别被输入到空白的文本编辑器后&#xff0c;如果两者相等&#xff0c;返回 true 。# 代表退格字符。 注意&#xff1a;如果对空文本输入退格字符&#xff0c;文本继续为空。 示例 1&#xff1a; 输入&#xff1a;s "ab#c&qu…

网站实现微信扫码登录(利用微信开放平台实现)

第一步&#xff1a;微信开放平台账户申请 网址&#xff1a;微信开放平台 1.首先我们要做的就是进入到微信开放平台申请一个开放平台账户&#xff0c;获得资质&#xff01; &#xff1a;注册需要准备营业执照、1-2个工作日审批、300元认证费 &#xff1a;注册之后&#xff0…

Window逆向基础之逆向工程介绍

逆向工程 以设计方法学为指导&#xff0c;以现代设计理论、方法、技术为基础&#xff0c;运用各种专业人员的工程设计经验、知识和创新思维&#xff0c;对已有产品进行解剖、深化和再创造。 逆向工程不仅仅在计算机行业、各行各业都存在逆向工程。 计算机行业逆向工程 计算…

裸金属服务器与云服务器有什么区别?

近年来&#xff0c;随着云计算技术的迅速发展&#xff0c;裸金属和云服务器已成为企业部署业务的两种主要方式。 裸金属是指提供基础设施硬件环境&#xff0c;但不包含操作系统和中间件的计算资源服务。 而云服务器则是通过虚拟化技术将计算资源划分为多个虚拟机&#xff0c;可…

JDK1.8 安装并配置环境变量

一、Windows 配置 1 安装文件 jdk-8u401-windows-i586.exe 2 环境变量 JAVA_HOME C:\Program Files (x86)\Java\jdk-1.8 CLASSPATH .;%JAVA_HOME%\lib\tools.jar;%JAVA_HOME%\lib\dt.jar; Path %JAVA_HOME%\bin 说明&#xff1a;Win7/Win8 中 Path 可能需要写成 ;%JAVA_HO…

【excel】统计单元格内数字/字符串的数量

文章目录 一、问题二、步骤&#xff08;一&#xff09;将A1中的数字分解出来&#xff0c;在不同的单元格中显示&#xff08;二&#xff09;统计每个数字出现的个数&#xff08;三&#xff09;去重 三、尾巴 一、问题 单元格中有如下数值&#xff1a;12345234534545&#xff0c…

01 背包(从二维数组到一维滚动数组)

问题描述&#xff1a; 有n件物品和一个最多能背重量为w 的背包。第i件物品的重量是weight[i]&#xff0c;得到的价值是value[i] 。每件物品只能用一次&#xff0c;求解将哪些)物品装入背包里物品价值总和最大。 二维dp数组01背包 1. 确定dp数组以及下标的含义 dp[i][j] 表…

Stable Diffusion写真完整教程

前言 最近自己对AI非常痴迷&#xff0c;并且今后也会一直在这个领域深耕&#xff0c;所以就想着先入门&#xff0c;因此花时间研究了一番&#xff0c;还好&#xff0c;出了点小成果&#xff0c;接下来给大家汇报一下。 AI绘画 提到AI绘画&#xff0c;大家可能立马会想到made…

男款内裤哪个品牌最好?男士内裤高能测评,选购攻略分享!

很多男性朋友的内裤都是穿到天荒地老的存在&#xff0c;但实际上如果一条内裤没有定期更换的话&#xff0c;存在的细菌就难以消除&#xff0c;从而可能导致出现健康问题&#xff0c;而且一条内裤没有定期更换&#xff0c;舒适性和透气性等方面都非常差&#xff01; 定期更换内裤…

AI 入门:从 ChatGPT 开始

在信息泛滥的时代&#xff0c;AI 技术已经渗透到生活的各个方面&#xff0c;学习 AI 成为个人发展的必然趋势。而 ChatGPT 作为 AI 领域的佼佼者&#xff0c;无疑是开启学习之旅的最佳起点。它不仅提供了一个便捷的交流平台&#xff0c;更能显著提升信息处理效率。 本文将带领…

在 Python 的哪个版本之后,字典的添加顺序与键的顺序是一致的?

&#x1f349; CSDN 叶庭云&#xff1a;https://yetingyun.blog.csdn.net/ 在 Python 的不同版本中&#xff0c;字典&#xff08;dict&#xff09;类型的行为发生了显著变化。在 Python 3.6 及之前的版本中&#xff0c;字典是无序的&#xff0c;这意味着字典在遍历时不能保证按…

硬盘架构原理及其算法RAID工作原理写惩罚

一、硬盘的架构以及寻址原理 硬盘工作原理&#xff1a; 硬盘寻址原理&#xff1a;逻辑顺序磁道、盘片、扇区&#xff08;顺序CHS&#xff09; 二、机械硬盘算法 读取算法 寻道算法 个人与企业适合的算法和寻道 个人使用的机械硬盘适合的寻道算法和读取算法是&#xff1a…

[Vision Board创客营]--使用openmv识别阿尼亚

文章目录 [Vision Board创客营]使用openmv识别阿尼亚介绍环境搭建训练模型上传图片生成模型 使用结语 [Vision Board创客营]使用openmv识别阿尼亚 &#x1f680;&#x1f680;五一和女朋友去看了《间谍过家家 代号&#xff1a;白》&#xff0c;入坑二刺螈&#xff08;QQ头像也换…

杰发科技AC7840——软件Sent_HAL39X

0. 序 使用PWM模拟Sent测试下7840的软件sent功能。 参考链接&#xff1a;SENT协议应用笔记 - TechPlus汽车工坊的文章 - 知乎 SENT协议 1. Sent功能测试 使用提供的软件Sent代码在7840上测试&#xff0c;接收数据OK 2. 参考资料 3. 数据解析 我们个根据上述参考资料尝试解析…

商务英语口语成人考级外语培训之BECkao考级口语篇

在口语考试中&#xff0c;不管实际内容你能说出多少&#xff0c;但准备一些套话&#xff0c;至少还能撑撑场子你们说是不是&#xff1f; 内容阐述 描述事实 1.Im going to describe/present/explain/give you some information about... 2.Id like to say a few words about...…