数据结构与算法-18算法专向(hash)

devtools/2024/9/19 10:19:08/ 标签: 哈希算法, 算法

话题引入:

给你N(1<N<10)个自然数,每个数的范围为(1~10000000000)。现在让你以最快的速度判断某一个数是否在这N个数内,不得使用已经封装好的类,该如何实现。 A[] = new int[N+1]?

散列表

散列表(Hash table),也被称为哈希表,是一种根据键(Key)而直接访问在内存存储位置的数据结构。它通过计算一个关于键值的函数(即散列函数),将所需查询的数据映射到表中一个位置来访问记录,从而加快查找速度。

1 简介

  • 定义:散列表是一种通过键来直接访问内存存储位置的数据结构,它利用散列函数将键映射到表中一个位置,以实现快速查找。
  • 核心组件
    • 散列函数:一个用于计算散列值的函数,通常表示为hash(key),其中key是元素的键值,hash(key)的值是经过散列函数计算得到的散列值。
    • 散列表:存放记录的数组,也称为哈希表。
  • 特性
    • 确定性:如果两个散列值是不相同的(根据同一函数),那么这两个散列值的原始输入也是不相同的。
    • 散列碰撞(Collision):散列函数的输入和输出不是唯一对应关系的。如果两个散列值相同,两个输入值很可能是相同的,但也可能不同。这种情况称为散列碰撞。
    • 不可逆性:一个哈希值对应无数个明文,理论上无法直接通过哈希值反推出原始输入。
    • 混淆特性:输入一些数据计算出散列值后,部分改变输入值,一个具有强混淆特性的散列函数会产生一个完全不同的散列值。

2 散列冲突解决方法

2.1 开放寻址法

开放寻址法(Open Addressing):通过探查序列来寻找空闲位置。

2.1.1 常见的开放寻址法:
  1. 线性探测法(Linear Probing)
    • 当发生冲突时,从冲突的地址开始,线性地向后探测,直到找到第一个空闲的地址。
    • 公式:𝐻(𝑘,𝑖)=(ℎ(𝑘)+𝑖)mod  𝑚H(k,i)=(h(k)+i)modm
    • 其中,ℎ(𝑘)h(k) 是原始哈希函数,𝑚m 是哈希表的大小,𝑖i 是探测序列中的步长,通常从1开始。
  2. 二次探测法(Quadratic Probing)
    • 与线性探测类似,但探测的步长是二次方的序列,即1, 4, 9, 16, …。
    • 公式:𝐻(𝑘,𝑖)=(ℎ(𝑘)+𝑐𝑖)mod  𝑚H(k,i)=(h(k)+c**i)modm
    • 其中,𝑐𝑖=𝑖2c**i=i2。
  3. 双哈希法(Double Hashing)
    • 使用两个哈希函数,当发生冲突时,使用第二个哈希函数计算下一个探测地址。
    • 公式:𝐻(𝑘,𝑖)=(ℎ1(𝑘)+𝑖⋅ℎ2(𝑘))mod  𝑚H(k,i)=(h1(k)+ih2(k))modm
    • 其中,ℎ1(𝑘)h1(k) 和 ℎ2(𝑘)h2(k) 是两个不同的哈希函数。
2.2.2 优缺点

优点

  • 空间利用率高:不需要额外的存储空间来存储冲突的元素。
  • 简单易实现算法实现相对简单。

缺点:

  • 聚集问题:特别是线性探测法,容易形成聚集,影响查找效率。
  • 删除操作复杂:删除元素时需要标记删除,而不是真正删除,以避免影响探测序列。
2.2.3 示例说明
  • 线性探测法的工作原理

    • 线性探测法的基本思想是,当一个元素通过哈希函数计算得到的地址已被占用时,它会从这个地址开始,线性地向后(或向前,这取决于实现方式)探测,直到找到一个空闲的地址。探测的步长通常是1。
  • 线性探测法的步骤

    • 计算哈希地址:使用哈希函数 ℎ(𝑘)h(k) 计算键 𝑘k 的哈希地址。
    • 探测冲突:如果该地址已被占用,就从该地址开始,线性地向后探测。
    • 找到空闲地址:探测到第一个空闲地址,将元素插入该地址。
    • 插入元素:将元素插入到找到的空闲地址中。
  • 示例

    • 假设我们有一个哈希表大小为6,哈希函数定义为 ℎ(𝑘)=𝑘 mod  6 ,现在我们要插入以下元素:
      • 元素1:ℎ(1)= 1 mod  6=1 h(1)=1mod 6=1
      • 元素2:ℎ(2)=2 mod  6=2 h(2)=2mod 6=2
      • 元素3:ℎ(3)=3 mod  6=3 h(3)=3mod 6=3
      • 元素4:ℎ(4)=4 mod  6=4 h(4)=4mod 6=4
      • 元素6:ℎ(7)=7 mod  6=1 h(6)=6mod 6=1(与元素1冲突)
    • 下面是插入元素6的步骤:
      • 计算元素7的哈希地址:ℎ(7)=1。
      • 地址1已被元素1占用,发生冲突。
      • 从地址1开始线性探测,往下寻址 【 2(不为空) -> 3(不为空) -> 4(不为空) -> 5(空) 】
      • 将元素7插入到地址5。
    • 此时,哈希表的状态如下:
      • 地址0:空闲
      • 地址1:元素1
      • 地址2:元素2
      • 地址3:元素3
      • 地址4:元素4
      • 地址5:元素6

2.2 链表法

链表法(Chaining):在散列表中,每个位置对应一条链表,所有散列值相同的元素都放到相同位置对应的链表中。这种方法更加常用且简单。

2.2.1 工作原理
  1. 哈希函数:首先使用哈希函数 ℎ(𝑘)h(k) 计算键 𝑘k 的哈希地址。
  2. 链表存储:在哈希表的对应地址处,维护一个链表,所有哈希值相同的键都将被存储在这个链表中。
  3. 插入操作:当插入一个新元素时,根据其哈希值找到对应的链表,然后在链表的末尾添加新元素。
  4. 查找操作:当查找一个元素时,根据其哈希值找到对应的链表,然后在链表中顺序查找该元素。
  5. 删除操作:当删除一个元素时,同样需要根据其哈希值找到对应的链表,然后在链表中找到该元素并删除。
2.2.2 优缺点

优点

  • 冲突易于处理:冲突的元素可以简单地存储在同一哈希值的链表中。
  • 动态扩展:链表的长度可以根据需要动态变化,适应不同数量的冲突。
  • 删除方便:删除元素时不需要考虑其他元素的探测或重新散列。

缺点

  • 空间开销:每个槽都需要额外的空间来存储链表的节点。
  • 性能问题:如果哈希表的负载因子(即元素数量与槽数量的比值)过高,链表可能会变得很长,导致查找效率下降。
2.2.3 示例说明

假设我们有一个哈希表大小为3,哈希函数定义为 ℎ(𝑘)=𝑘mod  3h(k)=kmod3,现在我们要插入以下元素:

  • 元素1:ℎ(1)=1mod  3=1h(1)=1mod3=1
  • 元素4:ℎ(4)=4mod  3=1h(4)=4mod3=1(与元素1冲突)
  • 元素7:ℎ(7)=7mod  3=1h(7)=7mod3=1(与元素1和4冲突)

插入过程如下:

  1. 元素1插入到哈希表的地址1的链表中。
  2. 元素4也插入到地址1的链表中,因为它与元素1冲突。
  3. 元素7同样插入到地址1的链表中。

此时,哈希表的状态如下:

  • 地址0:空闲
  • 地址1:元素1 -> 元素4 -> 元素7
  • 地址2:空闲

3 散列表&hashMap

散列表与hashMap

HashMap 是 Java 中的一个非常重要的集合类,它基于散列表(Hash Table)实现,用于存储键值对(key-value pairs)。散列表是一种使用哈希函数组织数据,以支持快速插入和搜索的数据结构。在 HashMap 中,散列表的使用是其高效性的关键所在。

3.1 什么是hashmap

Java中的HashMap是Java集合框架中的一个非常重要的类,它实现了Map接口。HashMap存储键值对(key-value pairs),其中每个键都映射到最多一个值。一个键可以映射到最多一个值。HashMap不保证映射的顺序;特别是,它不保证该顺序会随着时间的推移保持不变(扩容)。

特点:

  1. 基于哈希表实现HashMap内部通过一个哈希表来存储键值对,这使得它在查找、插入和删除操作方面具有较高的效率,平均时间复杂度为O(1)。
  2. 允许null键和null值:与Hashtable不同,HashMap允许使用null键和null值。但最多只能有一个null键,因为键是唯一的。
  3. 不保证顺序HashMap不保证映射的顺序;特别是它不保证该顺序会随着时间的推移保持不变。
  4. 初始容量和负载因子HashMap的默认初始容量是16,默认负载因子是0.75。当HashMap中的元素数量超过容量乘以负载因子时,哈希表会进行扩容,即创建一个新的哈希表,并将原哈希表中的所有元素重新哈希后存储到新表中。扩容操作是代价较高的。

hashmap使用示例

import java.util.HashMap;  
import java.util.Map;  public class HashMapExample {  public static void main(String[] args) {  // 创建一个HashMap实例  Map<String, Integer> ageMap = new HashMap<>();  // 向HashMap中添加键值对  ageMap.put("Alice", 30);  ageMap.put("Bob", 25);  ageMap.put("Charlie", 35);  // 访问HashMap中的值  System.out.println("Alice's age: " + ageMap.get("Alice")); // 输出: Alice's age: 30  // 检查HashMap中是否包含某个键  if (ageMap.containsKey("Bob")) {  System.out.println("Bob is in the map.");  }  // 检查HashMap中是否包含某个值(注意,这可能会返回true,因为可能有多个键映射到相同的值)  if (ageMap.containsValue(30)) {  System.out.println("There is someone who is 30 years old.");  }  // 修改HashMap中已存在的键对应的值  ageMap.put("Alice", 31); // 将Alice的年龄改为31  // 移除HashMap中的键值对  ageMap.remove("Charlie");  // 遍历HashMap(使用entrySet()遍历键和值)  for (Map.Entry<String, Integer> entry : ageMap.entrySet()) {  System.out.println(entry.getKey() + ": " + entry.getValue());  }  // 或者,如果你只对键或值感兴趣,可以使用keySet()或values()  // 遍历HashMap中的所有键  for (String key : ageMap.keySet()) {  System.out.println(key);  }  // 遍历HashMap中的所有值  for (Integer value : ageMap.values()) {  System.out.println(value);  }  // 使用Java 8的forEach()方法和Lambda表达式遍历  ageMap.forEach((key, value) -> System.out.println(key + ": " + value));  }  
}
3.2 hashmap数据结构

jdk1.8之前(数组+链表)

在JDK 1.8之前,HashMap采用了数组+链表的方式去保存数据。通过计算Hash值将元素放到数组上的指定位置上。如果出现了Hash相同的情况(即哈希冲突),就会形成单向的链表,并且链表使用头插法,将最新插入的元素放到链表的头结点位置。

jdk1.8以后(数组+链表+红黑树)

从JDK 1.8开始,HashMap的数据结构进行了优化,引入了红黑树来解决链表过长导致的查找效率下降问题。当链表的长度超过8并且数组长度大于64时,为了避免查找搜索性能下降,该链表会转换成一个红黑树。红黑树是一种自平衡的二叉搜索树,它的插入、删除和查找的时间复杂度都是O(log n),相比于链表的线性时间复杂度O(n),可以大大提高操作的效率。
在这里插入图片描述

3.3 数据存储原理
  • 根据key计算hash值。
  • 判断数组是否存在,如果不存在则用resize方法创建默认长度为16的数组。
  • 确定要存入的Node在数组中的位置,根据hash值与数组最大索引进行按位与运算得到索引位置。
  • 判断该位置是否有元素,如果没有则直接创建一个Node存入;如果有元素,则进一步判断key是否相同,如果相同则覆盖,并将原来的值返回;如果key不相同,则在原Node基础上添加新的Node,并判断该位置是链表还是红黑树,进行相应的插入操作。

在这里插入图片描述

3.4 最后读读源码

备注很多认真看

3.4.1 get
public V get(Object key) {Node<K,V> e;return (e = getNode(hash(key), key)) == null ? null : e.value;//先计算hashcode
}
final Node<K,V> getNode(int hash, Object key) {Node<K,V>[] tab; Node<K,V> first, e; int n; K k;if ((tab = table) != null && (n = tab.length) > 0 &&(first = tab[(n - 1) & hash]) != null) {if (first.hash == hash && //  数组查找!!!!!!!((k = first.key) == key || (key != null && key.equals(k)))) // hash值相同 且 key相等return first;//返回查找的数据if ((e = first.next) != null) {  //桶中不止一个节点if (first instanceof TreeNode)return ((TreeNode<K,V>)first).getTreeNode(hash, key);//红黑查找!!!!!do {//链表查找!!!!!if (e.hash == hash &&((k = e.key) == key || (key != null && key.equals(k))))return e;} while ((e = e.next) != null);}}return null;//否则返回null
}
3.4.2 put
public V put(K key, V value) {return putVal(hash(key), key, value, false, true);//调用Map的putVal方法
}
final V putVal(int hash, K key, V value, boolean onlyIfAbsent,boolean evict) {//onlyIfAbsent:true不更改现有值;evict:false表示table为创建状态Node<K,V>[] tab; Node<K,V> p; int n, i;//临时变量if ((tab = table) == null || (n = tab.length) == 0)//数组是否null或者==0,第1次put为空n = (tab = resize()).length;//初始化数组(or扩容)!!!!!!!!!!!!!if ((p = tab[i = (n - 1) & hash]) == null)//寻址:(n - 1) & hash重要,16-1 按位与hash,为null表示没有值tab[i] = newNode(hash, key, value, null);//等空,直接插入else {Node<K,V> e; K k;if (p.hash == hash &&//key相等!!!!!!!!((k = p.key) == key || (key != null && key.equals(k))))e = p;//将第一个元素赋值给e,用e来记录;跳到646Lineelse if (p instanceof TreeNode)//判断是否红黑树!!!!!!!!!!!!!!!!e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);else { //生成链表(操作链表),开始遍历链表for (int binCount = 0; ; ++binCount) {if ((e = p.next) == null) {//p.next为空表明处于链表的尾部,1、生成链表  2、已经是链表!!!!  存储位置相等p.next = newNode(hash, key, value, null);// 直接创建if (binCount >= TREEIFY_THRESHOLD - 1) //链表长度如果>8转红黑树(or 扩容),-1是因为binCount从0开始treeifyBin(tab, hash);//树化;还需要判断是否大于64,否则扩容break;}if (e.hash == hash &&((k = e.key) == key || (key != null && key.equals(k))))//对链表节点中数据进行覆盖判断break;// 如果key相同,break跳出for循环,执行后面的逻辑p = e;}}if (e != null) { // key已经存在V oldValue = e.value;if (!onlyIfAbsent || oldValue == null)e.value = value;// 用新的value值去覆盖老的value值afterNodeAccess(e);return oldValue;// 返回覆盖前的value值,put的返回值}}++modCount;//用来记录HashMap的修改次数if (++size > threshold)//扩容resize();//如果size大于threshold,就需要进行扩容afterNodeInsertion(evict);return null;
}

4 hash函数的应用场景

  1. 加密与算法>哈希算法
  • MD5 算法>哈希算法
    • 广泛用于生成数据的128位二进制哈希值(指纹)。
    • 尽管理论上存在冲突可能(因为输出空间有限),但实际应用中冲突概率极低。
    • 用途:密码存储(注意:MD5已不推荐用于安全敏感场景,因其易受碰撞攻击)。
    • 示例md5(md5("password") + "salt") 用于密码加盐哈希。
    • 不可逆性:MD5生成的哈希值无法直接还原为原始数据。
  1. 视频重复检测
  • 方法:使用MD5算法对视频文件内容进行哈希,得到其唯一标识。
  • 优点:快速检测视频文件是否重复,只需比较哈希值。
  • 注意:大文件哈希计算可能耗时较长,且对于微小差异的视频文件可能无法区分。
  1. 相似性检测(如论文查重)
  • 指纹算法:将论文内容转化为特征向量(指纹),通常基于文本特征提取。
  • 汉明距离:用于衡量两个指纹之间的相似度,汉明距离越小表示相似度越高。
  • 应用:在学术查重系统中广泛使用,以检测论文的抄袭情况。
  1. 负载均衡(如Nginx配置)
  • 场景:在拥有多台服务器的环境中,合理分配请求以平衡负载。
  • 方法:根据请求的某些特征(如IP地址)计算哈希值,然后对服务器数量取模,决定请求发往哪台服务器。
  • 优点:简单有效,能快速实现请求的均衡分配。
  1. 分布式系统数据分库分表
  • 目的:处理大规模数据,将数据存储到多个数据库或表中以提高性能和可扩展性。
  • 方法:使用哈希函数对数据的唯一标识(如ID)进行哈希,然后对表数量取模,决定数据存储位置。
  • 示例id % 10 用于决定将ID分配到10个表中的哪一个。
  • 扩展性:当需要增加表数量时,需重新分配数据,可能导致数据迁移和性能影响。
  1. 分布式存储表扩展问题

可以了解了解(redis cluster集群的一致性哈希

  • 挑战:在增加新表后,需要重新计算并迁移旧数据到新结构,可能导致大量数据移动和服务中断。
  • 解决方案
    • 增量迁移:逐步迁移数据,减少一次性迁移压力。
    • 使用更灵活的算法>哈希算法:如一致性哈希,减少数据迁移量。
    • 动态扩容策略:设计能够动态调整数据分布的机制,减少手动干预。
  1. 查找算法:HashMap
  • 核心:基于哈希表的键值对存储结构,提供快速的查找、插入和删除操作。
  • 原理:通过哈希函数将键转换为数组索引,实现快速定位。
  • 冲突解决:通过链表、红黑树等方式处理哈希冲突。
  • 应用:广泛应用于缓存、数据库索引、编程语言数据结构等场景。

http://www.ppmy.cn/devtools/114111.html

相关文章

k8s1.27.7部署higress,代理非k8s集群业务

一、简介 Higress是基于阿里内部的Envoy Gateway实践沉淀、以开源Istio + Envoy为核心构建的云原生API网关,实现了流量网关 + 微服务网关 + 安全网关三合一的高集成能力,深度集成Dubbo、Nacos、Sentinel等微服务技术栈,能够帮助用户极大的降低网关的部署及运维成本且能力不…

使用llama.cpp 在推理MiniCPM-1.2B模型

llama.cpp 是一个开源项目&#xff0c;它允许用户在C中实现与LLaMA&#xff08;Large Language Model Meta AI&#xff09;模型的交互。LLaMA模型是由Meta Platforms开发的一种大型语言模型&#xff0c;虽然llama.cpp本身并不包含LLaMA模型的训练代码或模型权重&#xff0c;但它…

SQL数据库(MySQL)

一、在Ubuntu系统下安装MySQL数据库 1、更新软件源&#xff0c;在确保ubuntu系统能正常上网的情况下执行以下命令 sudo apt-get update 2、安装MySQL数据库及相关软件包 # 安装过程中设置root用户的密码 123456 sudo apt-get install mysql-server ​ # 安装访问数据库的客…

scanf()函数的介绍及基础用法

目录 scanf&#xff08;&#xff09;函数的介绍及基础用法 一&#xff1a;头文件 二&#xff1a;一般用法 三&#xff1a;返回值 1. 正整数的情况&#xff1a; 2. 0 的情况&#xff1a; 3. EOF的情况&#xff1a; 四&#xff1a;说明 scanf&#xff08;&#xff09;函数…

IP池对数据爬取工作的帮助

在数据爬取的过程中&#xff0c;IP池&#xff08;也称为代理IP池&#xff09;是一个极为重要的工具&#xff0c;它为数据抓取工作提供了多方面的支持和便利。本文将详细探讨IP池在数据爬取工作中的具体作用&#xff0c;以及它如何帮助提升数据抓取的效率、稳定性和合规性。 一…

新手教学系列——基于统一页面的管理后台设计(一)

在现代企业级应用中,后台管理系统往往是核心组成部分,特别是随着业务规模的扩展,如何在多个后端服务模块的基础上实现统一的登录验证、权限控制和页面管理,成为许多开发者面对的挑战。本文将以实际项目为例,详细讲解如何设计一个多模块的后台管理系统,满足不同服务模块的…

部署Prometheus+Grafana批量监控Linux服务器

在 Linux 服务器上使用 Docker 容器快速部署 Prometheus 和 Grafana 监控系统&#xff0c;同时通过 node_exporter 采集全面的系统性能数据。整个流程涵盖了从环境配置到搭建一个全面监控平台的每个步骤。 一键安装Node Exporter Node Exporter 是 Prometheus 生态系统中的一个…

上线跨境电商商城的步骤

上线一个跨境电商商城涉及多个步骤&#xff0c;从前期准备到上线后的维护。以下是一些关键步骤&#xff1a; 1. 市场调研与规划 目标市场分析&#xff1a;研究目标市场的需求、竞争对手和消费者行为。法律法规&#xff1a;了解并遵守目标市场的法律法规&#xff0c;包括税收、…

Unity携程Coroutine用法

一.携程概述 官方的解释是&#xff0c;携程允许你可以在多个帧中执行任务。在Unity中&#xff0c;携程是一个可以暂停并在后续帧中从暂停处继续执行的方法。 二.携程写法 下面示例使用携程和Update打印前5帧的时间间隔&#xff0c;展示了携程的基础写法 using System.Colle…

CentOS 入门必备基础知识

CentOS 是 Linux 发行版之一&#xff0c;基于 Red Hat Enterprise Linux&#xff08;RHEL&#xff09;&#xff0c;提供免费的企业级操作系统。对于初学者和系统管理员来说&#xff0c;了解 CentOS 的基础知识是必不可少的。本文将带你快速掌握 CentOS 的入门要点&#xff0c;帮…

vue-ts-demo

npm i -g vue/cli PS D:\kwai\vue3\project> vue create vue3-te-demo element-plus 一个 Vue 3 UI 框架 | Element Plus https://element-plus.org/zh-CN/guide/installation.html 安装&#xff1a; npm install element-plus --save 完整引入使用&#xff1a; 使用&…

STM32 芯片启动过程

目录 一、前言二、STM32 的启动模式三、STM32 启动文件分析1、栈 Stack2、堆 Heap3、中断向量表 Vectors3.1 中断响应流程 4、复位程序 Reset_Handler5、中断服务函数6、用户堆栈初始化 四、STM32 启动流程分析1、初始化 SP、PC 及中断向量表2、设置系统时钟3、初始化堆栈并进入…

判断变量是否为有限数字(非无穷大或NaN)math.isfinite() 判断变量是否为无穷大(正无穷大或负无穷大)math.isinf()

【小白从小学Python、C、Java】 【考研初试复试毕业设计】 【Python基础AI数据分析】 判断变量是否为有限数字&#xff08;非无穷大或NaN&#xff09; math.isfinite() 判断变量是否为无穷大&#xff08;正无穷大或负无穷大&#xff09; math.isinf() 请问关于以下代码表述错误…

qt信号与槽(自定义)

自定义信号与槽 在qt里&#xff0c;我们可以自己去定义信号与槽。 这里举个栗子&#xff1a; 信号的定义 在我们类里边定义一个信号&#xff0c;我们需要用signals&#xff1a;来声明&#xff0c;不用再去cpp文件里边定义。而且返回值必须是void&#xff0c;可以有参数。 槽…

构建自己的文生图工具:Python + Stable Diffusion + CUDA

构建自己的文生图工具&#xff1a;Python Stable Diffusion CUDA 前言概述环境搭建安装PyTorch安装Stable Diffusion编写Python代码结论结语 前言 在这个数字化和人工智能飞速发展的时代&#xff0c;图像生成技术正逐渐成为现实。想象一下&#xff0c;只需输入几个关键词&…

ffmpeg批量图片格式转换

参考别的地方的弄的 echo off::在下方设置要处理的视频或图片 set Ext*.png,*.bmpmd outputecho 开始视频转换::在下方设置输出格式&#xff0c;这里输出为mp4&#xff0c;可自行更改 for %%a in (%Ext%) do (echo 正在转换&#xff1a;%%a"../ffmpeg.exe" -logleve…

驱动开发知识点

裸机开发 ——————————————linux驱动 SOC&#xff1a; 定义&#xff1a;SOC&#xff0c;全称System on Chip&#xff0c;是一种集成了多个功能模块的芯片&#xff0c;包括处理器、内存、外设、接口等。它将原本分散在多个芯片上的功能集成到一个芯片上&#xff0…

Redis 底层数据结构,一文详解

Redis 底层用 C 语言实现&#xff0c;不同版本的数据类型使用的数据结构也不同&#xff0c;下面详细看 SDS 字符串在 Redis 中很常用&#xff0c;键值对的所有键都是字符串&#xff0c;值有时候也是字符串 Redis 是用 C 语言实现的&#xff0c;但是字符串没有直接用 C 语言的…

Photoshop使用方法大全

0、快捷键 1&#xff09;Alt Delete&#xff1a;填充前景色 2&#xff09;Ctrl D&#xff1a;取消选区 3&#xff09;Ctrl T&#xff1a;自由变换&#xff0c;等比缩放、旋转的效果 4&#xff09;Ctrl R&#xff1a;显示标尺 5&#xff09;Ctrl J&#xff1a;复制一个…

【LLM多模态】文生视频评测基准VBench

note VBench的16个维度自动化评估指标代码实践&#xff08;待完成&#xff09;16个维度的prompt举例人类偏好标注&#xff1a;计算VBench评估结果与人类偏好之间的相关性、用于DPO微调 文章目录 note一、相关背景二、VBench评测基准概述&#xff1a;论文如何解决这个问题&…