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 = value
        self.next: Node = None

class SeparateChaining:
    def __init__(self, size: int, same: bool=True):
        self.size = size
        self.same = same
        self.nums = 0
        self.table: List[Node] = [Node(None) for _ in range(self.size)] # 初始化哨兵节点

    def hash_func(self, value: int) -> int:
        return value % self.size

    def add(self, value: int) -> None:
        node = self._search(value, self.same)
        new_node = Node(value)
        node.next, new_node.next = new_node, node.next

    def remove(self, value: int) -> None:
        node = self._search(value, False)
        if node.next and node.next.value == value:
            node.next = node.next.next

    def search(self, value: int) -> bool:
        if self._search(value, False).next:
            return True
        return False

    def _search(self, value: int, same: bool=True) -> Node:
        index = self.hash_func(value)
        node = self.table[index]
        while node.next:
            next_node = node.next
            if not same and next_node.value == value:
                break
            node = next_node
        return node

    def __repr__(self) -> str:
        out = []
        for node in self.table:
            s = []
            while node:
                s.append(node.value)
                node = node.next
            out.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 List


class 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	# 在插入的时候是否容忍具有相同的元素存在

    @abstractmethod
    def hash_func(self, value: int, step: int = 0) -> int:...

    def _search(self, value: int, same=True) -> int:
    	# 搜寻该元素value可用的槽位,而不是搜寻该元素在不在
        step: int = 0
        first_none = -1
        while step <= self.max_collision:
            _hash = self.hash_func(value, step)
            if self.table[_hash] is None:
                # 可能这个位置的值已经被删除了,并不一定后续没有,所以需要继续查找,而不是返回None
                # 记录第一次为空的位置,如果要插入一个元素,就可以插入到这里
                if first_none == -1:
                    first_none = _hash
            else:
                # 不许相同,遇到相同值
                if not same and self.table[_hash] == value:
                    return _hash
                #否则继续寻找,直到找到None或者超出hash碰撞最大值
            step += 1
        # first_none为-1, 说明超出hash碰撞最大值
        return first_none

    def search(self, value: int) -> int:
    	# 搜寻元素,所以遇到该元素就返回
        return self._search(value, same=False)

    @property
    def alpha(self) -> float:
        return self.nums / self.size

    def add(self, value: int) -> None:
    	# 搜寻槽位
        index = self._search(value, self.same)
        if index == -1:
            raise ValueError("超出hash碰撞的最大次数")
        if self.table[index] == value:
            return
        self.table[index] = value
        self.nums += 1

    def remove(self, value: int) -> None:
    	# 搜寻该元素
        index = self._search(value, False)
        if index == -1 or self.table[index] is None:
            return
        self.table[index] = None
        self.nums -= 1


    def __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.size
    
    def __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.size

    def __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.size

    def __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.size
        smaller_prime = 23
        h2 = smaller_prime - (value % smaller_prime)
        return (h1 + step * h2) % self.size

    def __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 List


class Probing:
    def __init__(self, size: int, max_collision: int = 10, same: bool=True):
        self.size = size
        self.table: List[int] = [None] * size
        self.max_collision = max_collision
        self.nums = 0
        self.same = same

    @abstractmethod
    def hash_func(self, value: int, step: int = 0) -> int:...

    def _search(self, value: int, same: bool=True) -> int:
        step: int = 0
        first_none = -1
        while step <= self.max_collision:
            _hash = self.hash_func(value, step)
            if self.table[_hash] is None:
                # 可能这个位置的值已经被删除了,并不一定后续没有需要查找的值
                # 记录第一次为空的位置
                if first_none == -1:
                    first_none = _hash
            else:
                # 不许相同,遇到相同值
                if not same and self.table[_hash] == value:
                    return _hash
                #否则继续寻找,直到找到None或者超出hash碰撞最大值
            step += 1
        # -1超出hash碰撞最大值
        return first_none

    def search(self, value: int) -> int:
        return self._search(value, same=False)

    @property
    def alpha(self) -> float:
        return self.nums / self.size

    def add(self, value: int) -> None:
        index = self._search(value, self.same)
        if index == -1:
            raise ValueError("超出hash碰撞的最大次数")
        if self.table[index] == value:
            return
        self.table[index] = value
        self.nums += 1

    def remove(self, value: int) -> None:
        index = self._search(value, False)
        if index == -1 or self.table[index] is None:
            return
        self.table[index] = None
        self.nums -= 1


    def __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.size

    def __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.size

    def __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.size

    def __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.size
        smaller_prime = 23
        h2 = smaller_prime - (value % smaller_prime)
        return (h1 + step * h2) % self.size

    def __repr__(self) -> str:
        return f"DoubleProbing({self.table !r}), nums:{self.nums}, alpha:{self.alpha}"

class Node:
    def __init__(self, value: int):
        self.value = value
        self.next: Node = None

class SeparateChaining:
    def __init__(self, size: int, same: bool=True):
        self.size = size
        self.same = same
        self.nums = 0
        self.table: List[Node] = [Node(None) for _ in range(self.size)] # 初始化哨兵节点

    def hash_func(self, value: int) -> int:
        return value % self.size

    def add(self, value: int) -> None:
        node = self._search(value, self.same)
        new_node = Node(value)
        node.next, new_node.next = new_node, node.next

    def remove(self, value: int) -> None:
        node = self._search(value, False)
        if node.next and node.next.value == value:
            node.next = node.next.next

    def search(self, value: int) -> bool:
        if self._search(value, False).next:
            return True
        return False

    def _search(self, value: int, same: bool=True) -> Node:
        index = self.hash_func(value)
        node = self.table[index]
        while node.next:
            next_node = node.next
            if not same and next_node.value == value:
                break
            node = next_node
        return node

    def __repr__(self) -> str:
        out = []
        for node in self.table:
            s = []
            while node:
                s.append(node.value)
                node = node.next
            out.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)

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

利用WK2168实现串口服务器

ESP32 SPI与WK2168实现串口服务器 概述系统组成代码 概述 一些老设备通过RS485采集数据&#xff0c;如果在一个系统中采用几个RS485设备可能是一个不错的选择&#xff0c;但要是使用46个RS485数据采集设备为一个PLC提供外部数据&#xff0c;系统的性能就很难有保障了。通过一个…

软考对进入事业编的人有帮助吗?

首先肯定的说&#xff0c;软考是可以用来评职称的。 但是&#xff0c; 这取决于你所在公司的性质和工作岗位。 软考全国统一实施后&#xff0c;不再进行计算机技术与软件相应专业和级别的专业技术职务任职资格评审工作。计算机软件资格考试既是职业资格考试&#xff0c;又是…

Element - UI <el-table-column>多选数据提交后禁用已提交的多选框

1. 通过 selection-change"selectionChange" 将已选择的数据存入selectData数组中 <el-table :data"tableData" class"my-5" selection-change"selectionChange" > //多选框已选择的数据 const selectData ref([]); const sel…

品致分档高频电流探头交直流电流探头的功能和应用领域

品致分档高频电流探头可以精确测量最大直流 DC-12MHz&#xff0c;具有高带宽&#xff0c;高精度的测量特点&#xff0c;且支持量程档位切换&#xff0c;满足用户一机多用的需求。探头自带归零和消磁功能&#xff0c;在包容用户的使用环境下&#xff0c;精度也高达 1%。标准的BN…

SpringBoot集成ireport打印,并解决PDF中文显示问题

1、相关jar包引入 <!-- ireport报表相关 start--><dependency><groupId>net.sf.jasperreports</groupId><artifactId>jasperreports</artifactId><version>4.5.1</version><exclusions><exclusion><groupId…

蚓链数字化营销生态的影响力分享!

​家人们&#xff0c;今天来给大家分享一些关于数字化平台生态化对数字营销影响的具体案例。 比如某电商平台&#xff0c;通过生态化的建设&#xff0c;实现了精准的推荐算法。根据用户的浏览历史和购买行为&#xff0c;为他们推荐最符合需求的商品&#xff0c;大大提高了购买…

toB市场 | 我们喜欢赞助这样的展会活动

过去的六一儿童节&#xff0c;刚去成都参加了个行业内的展会。受护网行动、儿童节等等的影响&#xff0c;这次去成都的客户并不算太多&#xff0c;但会议延续了一贯的高品质&#xff0c;让我们收货满满。 选择目标受众来得多的展会 不同厂商会视自己的产品和模式、目标客户来…

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

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

Django 传递额外参数给视图函数

本书1-7章样章及配套资源下载链接: https://pan.baidu.com/s/1OGmhHxEMf2ZdozkUnDkAkA?pwdnanc 源码、PPT课件、教学视频等&#xff0c;可以从前言给出的下载信息下载&#xff0c;大家可以评估一下。 在Django框架中&#xff0c;URLconf模块还支持一种传递额外参数给视图函…

深度文章 | 管理驾驶舱成摆设?如何激活核心数据?

经营企业就跟驾驶车一样&#xff0c;小规模时像骑自行车&#xff0c;不需要仪表盘。而随着企业的成长&#xff0c;就像开摩托、汽车&#xff0c;需要仪表盘&#xff08;管理驾驶舱&#xff09;展示经营数据&#xff0c;指导管理者进行业务决策。 试想&#xff0c;领导面对一堆…

CLIP计算图片与文本相似度(多幅图片与一个文本)

一、CLIP介绍 CLIP&#xff08;Contrastive Language-Image Pre-training&#xff09;模型是一种通过自然语言监督来学习可迁移视觉模型的方法。CLIP模型利用海量的图像和相关文本数据对图像编码器和文本编码器进行联合训练&#xff0c;目标是最大化图像和文本对的余弦相似性。…

IP纯净度对跨境电商有影响吗?

当我们谈论代理IP时&#xff0c;通常会提到一个重要概念&#xff0c;那就是“IP纯净度”。 IP纯净度是指代理IP服务中所提供的IP地址的质量、干净程度和安全性&#xff0c;纯净度高的IP地址通常具备低恶意软件攻击的风险、良好的访问效果、稳定性和速度以及隐私保护等特点。在…

TEE的密码学算法都支持哪些?你们都是如何实现的?

先说TEE OS中都支持哪些密码学算法?需要支持哪些密码学算法? 我们从GP规范的角度来看。在一般的tee中,都号称支持GP规范。GP规范里要求的强制支持的算法,一般都会实现。对于GP规范里可选的算法,并不是每一家TEE都能支持的。 强制要求的算法 可选支持的算法有 再来看TEE O…

whistle手机抓包

环境&#xff1a;whistle&#xff1a;2.9.59 whistle手机抓包&#xff08;ios可以抓小程序的包&#xff1b;安卓机不能抓小程序的包&#xff0c;但是小程序的有开发者工具就够用了&#xff09; 以安卓手机为例&#xff08;手机跟电脑要连同一个wifi&#xff09; 1.电脑安装w…

Jmeter压测 —— 1秒发送1次请求

场景&#xff1a;有时候测试场景需要设置请求频率为一秒一次&#xff08;或几秒一次&#xff09;实现方法一&#xff1a;1、首先需要在线程组下设置循环次数&#xff08;可以理解为请求的次数&#xff09; 次数设置为请求300次&#xff0c;其中线程数跟时间自行设置 2、在设置…

git 大文件上传失败 Please remove the file from history and try again.

根据提示执行命令 --- 查找到当前文件 git rev-list --objects --all | grep b24e74b34e7d482e2bc687e017c8ab28cd1d24b6git filter-branch --tree-filter rm -f 文件名 --tag-name-filter cat -- --all git push origin --tags --force git push origin --all --force

11.爬虫---BeautifulSoup安装并解析爬取数据

11.BeautifulSoup安装并解析爬取数据 1.简介2.安装3.基本使用3.1 获取第一个div标签的html代码3.2 获取第一个li标签3.3 获取第一个li标签内容3.4 返回第一个li的字典&#xff0c;里面是多个属性和值3.5 查看第一个li返回的数据类型3.6 根据属性&#xff0c;获取标签的属性值&a…

Android 安装调试 TelephonyProvider不生效

直接安装TelephonyProvider的时候&#xff0c;&#xff08;没有重启&#xff09;发现数据库没有生效。 猜测应该是原本的数据库没有删除后重建更新。 解决方法&#xff1a;杀掉phone进程 adb shell am force-stop com.android.phone 查看device进程 adb shell ps | grep <…

AI视频教程下载:用LangChain开发 ChatGPT和 LLMs 应用

在这个快速变化的人工智能时代&#xff0c;我们为您带来了一场关于语言模型和生成式人工智能的革命性课程。这不仅仅是一个课程&#xff0c;而是一次探险&#xff0c;一次深入人工智能核心的奇妙之旅。 在这里&#xff0c;您将开启一段激动人心的旅程&#xff0c;探索语言模型…

【必会面试题】JVM内存分区?垃圾回收(GC)算法?如何处理OOM?

目录 JVM分区模型GC&#xff08;垃圾回收&#xff09;算法&#xff1a;处理OOM&#xff08;Out Of Memory&#xff09;&#xff1a; JVM分区模型 JVM&#xff08;Java虚拟机&#xff09;内存划分为多个区域&#xff0c;以支持其运行时环境和对象的生命周期管理。以下是主要的内…