python基础-数据结构——hash表、线性探测、二重探测、双重哈希、闭散列(分离链接)(拉链法)Python代码实现

文章目录

  • 哈希表及其碰撞解决策略
    • 1. 引言
    • 2. 哈希表简介
    • 3. 哈希函数
    • 4. 碰撞解决策略
      • 4.1 分离链接法(拉链法)
      • 4.2 开放寻址法
        • 4.2.1 线性探测
        • 4.2.2 二次探测
        • 4.2.3 双重哈希
    • 5. 总结

哈希表及其碰撞解决策略

1. 引言

哈希表是一种高效的数据结构,用于将键映射到值(也称为表或映射抽象数据类型/ADT)。哈希表利用哈希函数将大的或非整数键映射到一个小的整数索引范围(通常是 [0..hash_table_size-1])。由于哈希函数可能会将不同的键映射到相同的索引,这就引发了碰撞问题。本文将介绍几种常见的碰撞解决策略,包括开放寻址法(线性探测、二次探测和双重哈希)和闭散列法(分离链接)。

2. 哈希表简介

哈希表通过哈希函数将键映射到数组中的索引,从而实现快速的数据存储和检索。这个过程包括两个主要步骤:

  1. 哈希函数:计算键的哈希值。
  2. 索引计算:将哈希值映射到数组的索引范围内。

公式表示为:

index = hash_function(key) % hash_table_size

哈希表在计算机软件中广泛应用,如关联数组、数据库索引、缓存和集合等。它们的主要优点是能够在平均情况下以常数时间复杂度(O(1))进行插入、删除和查找操作。

3. 哈希函数

哈希函数需要满足以下条件:

  1. 快速计算:时间复杂度为O(1)。
  2. 最小冲突:尽可能减少碰撞。
  3. 均匀分布:键均匀地分散到哈希表中。

一个常用的哈希函数是取模运算:

h(v) = v % M

其中,M是哈希表的大小,通常设为一个大素数。这样可以减少冲突并确保均匀分布。哈希函数的选择非常重要,因为一个好的哈希函数能够有效减少冲突,提高哈希表的性能。

如果负载因子α = N/M(N是键的数量,M是哈希表大小)保持在一个小常数范围内,所有操作的时间复杂度均为O(1)。负载因子越小,碰撞的概率就越低,链表的平均长度也就越短,从而提高了操作效率。

4. 碰撞解决策略

当两个不同的键通过哈希函数映射到相同的索引时,就会发生碰撞。为了解决这个问题,常见的碰撞解决策略包括分离链接法和开放寻址法。

4.1 分离链接法(拉链法)

分离链接法(Separate Chaining,简称SC)是最简单的碰撞解决方法之一。每个哈希表位置都包含一个链表,所有碰撞到同一位置的键值对都存储在这个链表中。这种方法将冲突键值对存储在链表中,避免了冲突带来的问题。

  • 搜索(v):遍历链表,检查是否存在v。
  • 插入(v):将v插入链表尾部。
  • 删除(v):遍历链表,删除v。
class Node:def __init__(self, value: int):self.value = valueself.next: Node = Noneclass SeparateChaining:def __init__(self, size: int, same: bool=True):self.size = sizeself.same = sameself.nums = 0self.table: List[Node] = [Node(None) for _ in range(self.size)] # 初始化哨兵节点def hash_func(self, value: int) -> int:return value % self.sizedef add(self, value: int) -> None:node = self._search(value, self.same)new_node = Node(value)node.next, new_node.next = new_node, node.nextdef remove(self, value: int) -> None:node = self._search(value, False)if node.next and node.next.value == value:node.next = node.next.nextdef search(self, value: int) -> bool:if self._search(value, False).next:return Truereturn Falsedef _search(self, value: int, same: bool=True) -> Node:index = self.hash_func(value)node = self.table[index]while node.next:next_node = node.nextif not same and next_node.value == value:breaknode = next_nodereturn nodedef __repr__(self) -> str:out = []for node in self.table:s = []while node:s.append(node.value)node = node.nextout.append(f"{s !r}")return f"{out !r}"if __name__ == "__main__":sc = SeparateChaining(25, True)for i in [72, 7, 53, 71, 40, 2, 45, 41, 42, 19, 42, 88, 42, 95, 38, 57, 16, 33, 89, 76]:sc.add(i)print(sc)for i in [72, 7, 53, 71, 40, 2, 45, 41, 42, 19, 42, 88, 42, 95, 38, 57, 16, 33, 89, 76]:sc.remove(i)print(sc)# print输出
['[None]', '[None, 76]', '[None, 2]', '[None, 53]', '[None]', '[None]', '[None]', '[None, 7, 57]', '[None, 33]', '[None]', '[None]', '[None]', '[None]', '[None, 88, 38]', '[None, 89]', '[None, 40]', '[None, 41, 16]', '[None, 42, 42, 42]', '[None]', '[None, 19]', '[None, 45, 95]', '[None, 71]', '[None, 72]', '[None]', '[None]']
['[None]', '[None]', '[None]', '[None]', '[None]', '[None]', '[None]', '[None]', '[None]', '[None]', '[None]', '[None]', '[None]', '[None]', '[None]', '[None]', '[None]', '[None]', '[None]', '[None]', '[None]', '[None]', '[None]', '[None]', '[None]']

在这里插入图片描述

4.2 开放寻址法

开放寻址法(Open Addressing)通过探测序列来解决碰撞。常见的探测方法有线性探测、二次探测和双重哈希。
它们的差别是使用的hash函数不相同,下面是它们之间的公共的基类,其中包含了搜索、插入、和删除的公共方法:

from abc import abstractmethod
from typing import Listclass Probing:def __init__(self, size: int, max_collision: int = 10, same=True):self.size = size		# 最大的存储容量self.table: List[int] = [None] * size	# hash表self.max_collision = max_collision   # 最大的hash碰撞次数self.nums = 0		# 有效元素的个数self.same = same	# 在插入的时候是否容忍具有相同的元素存在@abstractmethoddef hash_func(self, value: int, step: int = 0) -> int:...def _search(self, value: int, same=True) -> int:# 搜寻该元素value可用的槽位,而不是搜寻该元素在不在step: int = 0first_none = -1while step <= self.max_collision:_hash = self.hash_func(value, step)if self.table[_hash] is None:# 可能这个位置的值已经被删除了,并不一定后续没有,所以需要继续查找,而不是返回None# 记录第一次为空的位置,如果要插入一个元素,就可以插入到这里if first_none == -1:first_none = _hashelse:# 不许相同,遇到相同值if not same and self.table[_hash] == value:return _hash#否则继续寻找,直到找到None或者超出hash碰撞最大值step += 1# first_none为-1, 说明超出hash碰撞最大值return first_nonedef search(self, value: int) -> int:# 搜寻元素,所以遇到该元素就返回return self._search(value, same=False)@propertydef alpha(self) -> float:return self.nums / self.sizedef add(self, value: int) -> None:# 搜寻槽位index = self._search(value, self.same)if index == -1:raise ValueError("超出hash碰撞的最大次数")if self.table[index] == value:returnself.table[index] = valueself.nums += 1def remove(self, value: int) -> None:# 搜寻该元素index = self._search(value, False)if index == -1 or self.table[index] is None:returnself.table[index] = Noneself.nums -= 1def __repr__(self) -> str:return f"Probing({self.table !r}), nums:{self.nums}, alpha:{self.alpha}"
4.2.1 线性探测

线性探测(Linear Probing)采用固定步长进行探测:

i = (base + step * 1) % M

其中base是初始哈希值,step为当前探测步骤。探测序列为:

  • h(v):基地址
  • (h(v) + 1 * 1) % M
  • (h(v) + 2 * 1) % M

  • 下面是线性探测的具体实现代码:
class LinearProbing(Probing):def __init__(self, size: int, max_collision: int = 10):super().__init__(size, max_collision)def hash_func(self, value: int, step: int = 0) -> int:return (value + step * 1) % self.sizedef __repr__(self) -> str:return f"LinearProbing({self.table !r}), nums:{self.nums}, alpha:{self.alpha}"
if __name__ == "__main__":lp = LinearProbing(25, 20)for i in [72, 7, 53, 71, 40, 2, 45, 41, 42, 19, 42, 88, 42, 95, 38, 57, 16, 33, 89, 76]:lp.add(i)print(lp)for i in [72, 7, 53, 71, 40, 2, 45, 41, 42, 19, 42, 88, 42, 95, 38, 57, 16, 33, 89, 76]:lp.remove(i)print(lp)
# print 输出
LinearProbing([16, 89, 2, 53, 76, None, None, 7, 57, 33, None, None, None, 88, 38, 40, 41, 42, 42, 19, 45, 71, 72, 42, 95]), nums:20, alpha:0.8
LinearProbing([None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None]), nums:0, alpha:0.0

线性探测的优点是实现简单,但容易出现主聚类(Primary Clustering)问题,即连续的空闲槽位被逐渐填满,导致探测序列变长,性能下降。
在这里插入图片描述

4.2.2 二次探测

二次探测(Quadratic Probing)采用平方步长进行探测,step 为碰撞次数:

i = (base + step * step) % M

探测序列为:

  • h(v):基地址
  • (h(v) + 1 * 1) % M
  • (h(v) + 2 * 2) % M
  • (h(v) + 3 * 3) % M
class QuadraticProbing(Probing):def __init__(self, size: int, max_collision: int = 10):super().__init__(size, max_collision)def hash_func(self, value: int, step: int = 0) -> int:return (value + step * step) % self.sizedef __repr__(self) -> str:return f"QuadraticProbing({self.table !r}), nums:{self.nums}, alpha:{self.alpha}"
if __name__ == "__main__":qp = QuadraticProbing(25, 20)for i in [72, 7, 53, 71, 40, 2, 45, 41, 42, 19, 42, 88, 42, 95, 38, 57, 16, 33, 89, 76]:qp.add(i)print(qp)for i in [72, 7, 53, 71, 40, 2, 45, 41, 42, 19, 42, 88, 42, 95, 38, 57, 16, 33, 89, 76]:qp.remove(i)print(qp)#print输出
QuadraticProbing([16, 42, 2, 53, None, 76, None, 7, 57, 33, None, None, None, 88, 38, 40, 41, 42, 42, 19, 45, 71, 72, 89, 95]), nums:20, alpha:0.8
QuadraticProbing([None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None]), nums:0, alpha:0.0

在这里插入图片描述
也可以是用左右平方步长探测:

i = (base + (-1)**step * ((step+1)//2)**2) % M

探测序列为:

  • h(v):基地址
  • (h(v) + 1 * 1) % M
  • (h(v) - 1 * 1) % M
  • (h(v) + 2 * 2) % M
  • (h(v) - 2 * 2) % M
class QuadraticProbing2(Probing):def __init__(self, size: int, max_collision: int = 10):super().__init__(size, max_collision)def hash_func(self, value: int, step: int = 0) -> int:return (value + ((-1) ** step) * (((step+1) // 2) ** 2)) % self.sizedef __repr__(self) -> str:return f"QuadraticProbing2({self.table !r}), nums:{self.nums}, alpha:{self.alpha}"if __name__ == "__main__":qp2 = QuadraticProbing2(25, 20)for i in [72, 7, 53, 71, 40, 2, 45, 41, 42, 19, 42, 88, 42, 95, 38, 57, 16, 33, 89, 76]:qp2.add(i)print(qp2)for i in [72, 7, 53, 71, 40, 2, 45, 41, 42, 19, 42, 88, 42, 95, 38, 57, 16, 33, 89, 76]:qp2.remove(i)print(qp2)# print输出
QuadraticProbing2([16, 76, 2, 53, None, None, 57, 7, 42, 33, None, None, 38, 88, 89, 40, 41, 42, 42, 19, 45, 71, 72, None, 95]), nums:20, alpha:0.8
QuadraticProbing2([None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None]), nums:0, alpha:0.0

如果负载因子α < 0.5M是一个质数,前M/2个二次探测索引都是唯一的。二次探测减少了主聚类问题,但仍可能出现次聚类(Secondary Clustering)问题,即哈希值相同但初始位置不同的键会形成新的聚类。

4.2.3 双重哈希

双重哈希(Double Hashing)采用两个哈希函数进行探测:

i = (base + step * secondary) % M

其中secondary = smaller_prime - key % smaller_primesmaller_prime是小于M的质数。探测序列为:

  • h(v):基地址
  • (h(v) + 1 * h2(v)) % M
  • (h(v) + 2 * h2(v)) % M
  • (h(v) + 3 * h2(v)) % M

双重哈希通过引入第二个哈希函数来决定探测步长,从而有效减少了主聚类和次聚类问题,使得探测序列更加分散。常见的第二哈希函数为:

h2(v) = smaller_prime - (v % smaller_prime)

其中smaller_prime通常设为小于哈希表大小的质数,这样可以确保h2(v)的值在[1..smaller_prime]范围内。

class DoubleProbing(Probing):def __init__(self, size: int, max_collision: int = 10):super().__init__(size, max_collision)def hash_func(self, value: int, step: int = 0) -> int:h1 = value % self.sizesmaller_prime = 23h2 = smaller_prime - (value % smaller_prime)return (h1 + step * h2) % self.sizedef __repr__(self) -> str:return f"DoubleProbing({self.table !r}), nums:{self.nums}, alpha:{self.alpha}"if __name__ == "__main__":dp = DoubleProbing(25, 20)for i in [72, 7, 53, 71, 40, 2, 45, 41, 42, 19, 42, 88, 42, 95, 38, 57, 16, 33, 89, 76]:dp.add(i)print(dp)for i in [72, 7, 53, 71, 40, 2, 45, 41, 42, 19, 42, 88, 42, 95, 38, 57, 16, 33, 89, 76]:dp.remove(i)print(dp)#print输出
DoubleProbing([42, 76, 2, 53, 42, None, 57, 7, 33, None, 95, None, 38, 88, 89, 40, 41, 42, None, 19, 45, 71, 72, 16, None]), nums:20, alpha:0.8
DoubleProbing([None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None, None]), nums:0, alpha:0.0

在这里插入图片描述

5. 总结

哈希表是一种高效的数据结构,通过哈希函数将键映射到值。碰撞是哈希表中的一个重要问题,常用的解决策略包括分离链接法和开放寻址法。选择合适的哈希函数和碰撞解决策略可以大大提高哈希表的性能和效率。本文详细介绍了几种常见的碰撞解决方法及其优缺点,希望能帮助读者更好地理解和应用哈希表。

在实际应用中,根据具体需求和数据特点选择合适的哈希表实现方式,可以有效提升程序的性能和稳定性。无论是分离链接法还是开放寻址法,都有其独特的优点和适用场景,合理运用这些技术可以使哈希表在各种情况下发挥最佳性能。
完整代码如下:

from abc import abstractmethod
from typing import Listclass Probing:def __init__(self, size: int, max_collision: int = 10, same: bool=True):self.size = sizeself.table: List[int] = [None] * sizeself.max_collision = max_collisionself.nums = 0self.same = same@abstractmethoddef hash_func(self, value: int, step: int = 0) -> int:...def _search(self, value: int, same: bool=True) -> int:step: int = 0first_none = -1while step <= self.max_collision:_hash = self.hash_func(value, step)if self.table[_hash] is None:# 可能这个位置的值已经被删除了,并不一定后续没有需要查找的值# 记录第一次为空的位置if first_none == -1:first_none = _hashelse:# 不许相同,遇到相同值if not same and self.table[_hash] == value:return _hash#否则继续寻找,直到找到None或者超出hash碰撞最大值step += 1# -1超出hash碰撞最大值return first_nonedef search(self, value: int) -> int:return self._search(value, same=False)@propertydef alpha(self) -> float:return self.nums / self.sizedef add(self, value: int) -> None:index = self._search(value, self.same)if index == -1:raise ValueError("超出hash碰撞的最大次数")if self.table[index] == value:returnself.table[index] = valueself.nums += 1def remove(self, value: int) -> None:index = self._search(value, False)if index == -1 or self.table[index] is None:returnself.table[index] = Noneself.nums -= 1def __repr__(self) -> str:return f"Probing({self.table !r}), nums:{self.nums}, alpha:{self.alpha}"class LinearProbing(Probing):def __init__(self, size: int, max_collision: int = 10):super().__init__(size, max_collision)def hash_func(self, value: int, step: int = 0) -> int:return (value + step * 1) % self.sizedef __repr__(self) -> str:return f"LinearProbing({self.table !r}), nums:{self.nums}, alpha:{self.alpha}"class QuadraticProbing(Probing):def __init__(self, size: int, max_collision: int = 10):super().__init__(size, max_collision)def hash_func(self, value: int, step: int = 0) -> int:return (value + step * step) % self.sizedef __repr__(self) -> str:return f"QuadraticProbing({self.table !r}), nums:{self.nums}, alpha:{self.alpha}"class QuadraticProbing2(Probing):def __init__(self, size: int, max_collision: int = 10):super().__init__(size, max_collision)def hash_func(self, value: int, step: int = 0) -> int:return (value + ((-1) ** step) * (((step+1) // 2) ** 2)) % self.sizedef __repr__(self) -> str:return f"QuadraticProbing2({self.table !r}), nums:{self.nums}, alpha:{self.alpha}"class DoubleProbing(Probing):def __init__(self, size: int, max_collision: int = 10):super().__init__(size, max_collision)def hash_func(self, value: int, step: int = 0) -> int:h1 = value % self.sizesmaller_prime = 23h2 = smaller_prime - (value % smaller_prime)return (h1 + step * h2) % self.sizedef __repr__(self) -> str:return f"DoubleProbing({self.table !r}), nums:{self.nums}, alpha:{self.alpha}"class Node:def __init__(self, value: int):self.value = valueself.next: Node = Noneclass SeparateChaining:def __init__(self, size: int, same: bool=True):self.size = sizeself.same = sameself.nums = 0self.table: List[Node] = [Node(None) for _ in range(self.size)] # 初始化哨兵节点def hash_func(self, value: int) -> int:return value % self.sizedef add(self, value: int) -> None:node = self._search(value, self.same)new_node = Node(value)node.next, new_node.next = new_node, node.nextdef remove(self, value: int) -> None:node = self._search(value, False)if node.next and node.next.value == value:node.next = node.next.nextdef search(self, value: int) -> bool:if self._search(value, False).next:return Truereturn Falsedef _search(self, value: int, same: bool=True) -> Node:index = self.hash_func(value)node = self.table[index]while node.next:next_node = node.nextif not same and next_node.value == value:breaknode = next_nodereturn nodedef __repr__(self) -> str:out = []for node in self.table:s = []while node:s.append(node.value)node = node.nextout.append(f"{s !r}")return f"{out !r}"if __name__ == "__main__":lp = LinearProbing(25, 20)for i in [72, 7, 53, 71, 40, 2, 45, 41, 42, 19, 42, 88, 42, 95, 38, 57, 16, 33, 89, 76]:lp.add(i)print(lp)for i in [72, 7, 53, 71, 40, 2, 45, 41, 42, 19, 42, 88, 42, 95, 38, 57, 16, 33, 89, 76]:lp.remove(i)print(lp)qp = QuadraticProbing(25, 20)for i in [72, 7, 53, 71, 40, 2, 45, 41, 42, 19, 42, 88, 42, 95, 38, 57, 16, 33, 89, 76]:qp.add(i)print(qp)for i in [72, 7, 53, 71, 40, 2, 45, 41, 42, 19, 42, 88, 42, 95, 38, 57, 16, 33, 89, 76]:qp.remove(i)print(qp)qp2 = QuadraticProbing2(25, 20)for i in [72, 7, 53, 71, 40, 2, 45, 41, 42, 19, 42, 88, 42, 95, 38, 57, 16, 33, 89, 76]:qp2.add(i)print(qp2)for i in [72, 7, 53, 71, 40, 2, 45, 41, 42, 19, 42, 88, 42, 95, 38, 57, 16, 33, 89, 76]:qp2.remove(i)print(qp2)dp = DoubleProbing(25, 20)for i in [72, 7, 53, 71, 40, 2, 45, 41, 42, 19, 42, 88, 42, 95, 38, 57, 16, 33, 89, 76]:dp.add(i)print(dp)for i in [72, 7, 53, 71, 40, 2, 45, 41, 42, 19, 42, 88, 42, 95, 38, 57, 16, 33, 89, 76]:dp.remove(i)print(dp)sc = SeparateChaining(25, True)for i in [72, 7, 53, 71, 40, 2, 45, 41, 42, 19, 42, 88, 42, 95, 38, 57, 16, 33, 89, 76]:sc.add(i)print(sc)for i in [72, 7, 53, 71, 40, 2, 45, 41, 42, 19, 42, 88, 42, 95, 38, 57, 16, 33, 89, 76]:sc.remove(i)print(sc)

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

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

相关文章

BugKu 哎,就是玩

说明&#xff1a;通过图片隐写找到迷宫压缩包解码密码&#xff0c;然后通过MG游戏得到井字棋游戏解压密码&#xff0c;最后通过完成井字棋得到flag. 打开实验包&#xff0c;解压后可以看到两个文件。 首先要通过TKR.png找到迷宫.zip的解压密码。 打开图片&#xff0c;发现图片…

【grafana】创建多变量table

这个普罗米修斯的指标啊&#xff0c;大多数都是键值对&#xff0c;而且笔者如果没记错&#xff0c;他这个值还必须是浮点。少数可以设成离散值&#xff08;Enum&#xff09;&#xff0c;但本质还是一个带翻译功能的键值对 这样的好处是&#xff0c;做起来非常简单&#xff0c;…

Websocket服务端结合内网穿透发布公网实现远程访问发送信息

文章目录 1. Java 服务端demo环境2. 在pom文件引入第三包封装的netty框架maven坐标3. 创建服务端,以接口模式调用,方便外部调用4. 启动服务,出现以下信息表示启动成功,暴露端口默认99995. 创建隧道映射内网端口6. 查看状态->在线隧道,复制所创建隧道的公网地址加端口号7. 以…

计算机毕业设计hadoop+spark+hive物流快递大数据分析平台 物流预测系统 物流信息爬虫 物流大数据 机器学习 深度学习 知识图谱 大数据

1.Python爬虫采集物流数据等存入mysql和.csv文件&#xff1b; 2.使用pandasnumpy或者MapReduce对上面的数据集进行数据清洗生成最终上传到hdfs&#xff1b; 3.使用hive数据仓库完成建库建表导入.csv数据集&#xff1b; 4.使用hive之hive_sql进行离线计算&#xff0c;使用spark之…

乡村振兴的乡村环境综合整治:加强农村环境综合整治,改善农村人居环境,打造干净整洁的美丽乡村

目录 一、引言 二、农村环境问题的现状与挑战 &#xff08;一&#xff09;农村环境问题的现状 &#xff08;二&#xff09;农村环境问题的挑战 三、加强农村环境综合整治的必要性 &#xff08;一&#xff09;提升农民生活质量 &#xff08;二&#xff09;促进农村经济发…

ClickHouse 实现用户画像(标签)系统实践

文章目录 前言用户画像概述用户画像系统介绍用户画像系统的需求描述用户画像系统的需求分析用户画像系统的架构 关键技术实现&#xff08;Clickhouse SQL&#xff09;分析阶段运营阶段 基于ClickHouse的用户画像系统的优点 前言 本文介绍一个ClickHouse应用案例—用户画像系统…

成功案例(IF=12.2)| 肠道代谢组、微生物组和脑功能的综合分析揭示了肠-脑轴在长寿中的作用

研究背景 人类长寿是一种受遗传、环境等多种因素影响的复杂表型。近年来&#xff0c;肠道微生物群被认为是长寿的一个重要因素&#xff0c;如Akkermansia、Alisipes和Parabacteroides已被报道与长寿有关。此外&#xff0c;最近的一项研究表明&#xff0c;百岁老人的肠道微生物群…

学习笔记——IP地址网络协议——网络掩码(Netmask)

三、网络掩码(Netmask) 1、网络掩码概述 网络掩码(Netmask)又称子网掩码(Subnet Mask)网络掩码为32 bit&#xff0c;与IP地址的位数一样&#xff0c;通常也以点分十进制数来表示。 子网掩码不能单独存在&#xff0c;它必须结合IP地址一起使用。子网掩码只有一个作用&#xf…

AI图片光影重塑 - IC Light独立安装

两个前提&#xff1a; 1.安装GIT &#xff08;https://blog.csdn.net/qq_42372031/article/details/130676236&#xff09; 2.安装ANACONDA&#xff08;https://blog.csdn.net/ViatorSun/article/details/118578818&#xff09; 来到IC-Light主页&#xff08;https://github.…

品牌控价的同时也要做好数据分析

品牌在进行电商价格监测时&#xff0c;确实不应仅停留在收集低价数据的层面。在数据量巨大的今天&#xff0c;如何深度分析和挖掘这些数据的价值&#xff0c;为品牌的决策和战略提供有力支持&#xff0c;显得尤为重要。 首先&#xff0c;电商数据的监测和分析有助于品牌更全面…

函数高级:函数的默认参数|函数的占位参数|函数重载

函数的默认参数 函数占位参数 函数重载 总结&#xff1a; 函数的形参&#xff0c;有自己的参数值就用参数值&#xff0c;没有就用形参列表的默认值。 参数列表中&#xff0c;某位置有自己的默认值&#xff0c;从该位置起&#xff0c;往后就都必须设有默认值。 函数声明和函数…

详解MyBatis(二)

目录 1.MyBatis的基本操作 1.1增&#xff08;Insert&#xff09; 1.1.1返回主键 1.2删&#xff08;Delete&#xff09; 1.3改&#xff08;Update&#xff09; 1.4查&#xff08;Select&#xff09; 1.4.1起别名 1.4.2结果映射 1.4.3开启驼峰命名(推荐) 2.MyBatis XML配…

AR眼镜定制开发_在AR眼镜中实现ChatGPT功能

AR眼镜定制方案中&#xff0c;需要考虑到强大的算力、轻巧的设计和更长的续航时间等基本要求。然而&#xff0c;AR眼镜的设计方案不仅仅需要在硬件和显示技术方面取得突破&#xff0c;还要在用户体验方面有所进展。 过去&#xff0c;由于造价较高&#xff0c;AR眼镜的普及和商业…

按钮组切换控制统计图显示【统计图切换渲染失败】

背景 需要实现点击左上角按钮组的按钮&#xff0c;切换对应的统计图 点击按钮1呈现的统计图。映射的实体类Vo1 点击按钮2呈现的统计图。映射的实体类Vo2 可能会出现的问题&#xff1a; &#xff08;1&#xff09; 空白&#xff1a;进入页面只渲染第一个统计图&#xff0c;点…

TypeScript核心类型概览与应用-1

文章目录 TypeScript入门1.TypeScript介绍2.编译并运行TS代码2.1.简化运行ts步骤 3.TS中的常用类型3.1.TS中的类型注解3.2.TS中的原始类型3.3.TS中的数组类型3.4.TS中的联合类型3.5.类型别名3.6.函数类型3.6.1.单独执行参数、返回值类型3.6.2.同时指定参数&#xff0c;返回值类…

OpenGL系列(四)Shader

通过VBO和VAO准备好数据后&#xff0c;接下来要指示GPU如何通过这些数据绘制图形。类似CPU可以通过执行程序来完成特定的任务&#xff0c;GPU也可以执行特定的程序来完成绘制任务&#xff0c;GPU执行的程序称为Shader&#xff0c;也叫着色器。 GPU绘制图形分为不同的处理阶段&a…

文章解读与仿真程序复现思路——电力系统自动化EI\CSCD\北大核心《考虑发用电相似性的海上风电中长期双边协商交易优化决策模》

本专栏栏目提供文章与程序复现思路&#xff0c;具体已有的论文与论文源程序可翻阅本博主免费的专栏栏目《论文与完整程序》 论文与完整源程序_电网论文源程序的博客-CSDN博客https://blog.csdn.net/liang674027206/category_12531414.html 电网论文源程序-CSDN博客电网论文源…

小鸡庄园智慧农场养殖游戏开发:科技与农业的完美结合

随着科技的进步&#xff0c;一种全新的游戏模式——智慧农场养殖游戏&#xff0c;正在逐渐崭露头角。本文将深入探讨小鸡庄园智慧农场养殖游戏的开发背景、特点、技术实现方式以及未来的发展趋势&#xff0c;以期为游戏产业创新和农业现代化提供新的思路和启示。 一、开发背景…

Rust 性能分析

都说Rust性能好,但是也得代码写得好,猜猜下面两个代码哪个快 . - 力扣&#xff08;LeetCode&#xff09; use std::collections::HashMap; use lazy_static::lazy_static;lazy_static! {static ref DIGIT: HashMap<char, usize> {let mut m HashMap::new();for c in …

【Nacos】docker-compose启动nacos v2.2.3,启动时修改默认密码不使用naocs

1. 背景 出于安全考虑&#xff0c;我司DevOps平台自动部署的容器化nacos密码不能是弱密码或默认值 但是nacos-v2.2.3官方镜像启动后会初始化nacos用户密码为nacos&#xff0c;修改启动时的变量并没有生效。 2. 部署验证 2.1 yml文件如下 注意将derby库的初始化文件挂载出来…