当前位置: 首页 > news >正文

B+树删除和测试

在这里插入图片描述

B+树删除和测试

5.1 高级接口:B+ 树作为键值存储

在本章中,我们将实现 B+ 树的高级接口,使其能够作为键值存储(Key-Value Store)使用。这些接口包括插入和删除操作,并处理根节点的维护。


1. 插入接口

1.1 Insert 函数

Insert 函数用于插入新键或更新现有键。以下是其实现细节:

// 插入一个新键或更新现有键
func (tree *BTree) Insert(key []byte, val []byte) {if tree.root == 0 {// 如果树为空,则创建第一个根节点root := BNode(make([]byte, BTREE_PAGE_SIZE))root.setHeader(BNODE_LEAF, 2)// 插入一个哨兵值(空键),确保树覆盖整个键空间nodeAppendKV(root, 0, 0, nil, nil)// 插入实际的键值对nodeAppendKV(root, 1, 0, key, val)// 分配页号并设置为根节点tree.root = tree.new(root)return}// 插入键值对到当前树node := treeInsert(tree, tree.get(tree.root), key, val)// 检查是否需要分裂根节点nsplit, split := nodeSplit3(node)tree.del(tree.root) // 释放旧的根节点if nsplit > 1 {// 如果根节点被分裂,则创建一个新的根节点root := BNode(make([]byte, BTREE_PAGE_SIZE))root.setHeader(BNODE_NODE, nsplit)for i, knode := range split[:nsplit] {ptr, key := tree.new(knode), knode.getKey(0)nodeAppendKV(root, uint16(i), ptr, key, nil)}// 更新根节点tree.root = tree.new(root)} else {// 如果根节点未分裂,则直接更新根节点tree.root = tree.new(split[0])}
}
1.2 关键点分析
  1. 哨兵值

    • 在创建第一个根节点时,我们插入了一个空键(哨兵值)。这是为了确保树覆盖整个键空间。
    • 哨兵值是最小的键,因此 nodeLookupLE 函数始终能找到一个位置,避免了边界情况。
  2. 根节点分裂

    • 如果插入导致根节点过大,则需要分裂根节点。
    • 分裂后,创建一个新的内部节点作为新的根节点。
  3. 内存管理

    • 使用回调函数(tree.newtree.del)管理页面的分配和释放。

2. 删除接口

2.1 Delete 函数

Delete 函数用于删除指定的键,并返回该键是否存在。

// 删除一个键,并返回是否删除成功
func (tree *BTree) Delete(key []byte) bool {if tree.root == 0 {// 如果树为空,则直接返回 falsereturn false}// 查找并删除键node := tree.get(tree.root)idx := nodeLookupLE(node, key)if !bytes.Equal(node.getKey(idx), key) {// 如果键不存在,则返回 falsereturn false}// 执行删除操作new := leafDelete(node, idx)// 如果节点变为空,则需要合并或调整树结构if new.nkeys() == 1 && tree.root != tree.new(new) {// 如果只剩下一个哨兵值,则清空树tree.del(tree.root)tree.root = 0return true}// 更新根节点tree.root = tree.new(new)return true
}
2.2 删除叶节点中的键
// 从叶节点中删除指定索引的键值对
func leafDelete(old BNode, idx uint16) BNode {// 创建一个新的节点new := BNode(make([]byte, BTREE_PAGE_SIZE))new.setHeader(BNODE_LEAF, old.nkeys()-1)// 复制 [0, idx) 范围内的键值对nodeAppendRange(new, old, 0, 0, idx)// 复制 [idx+1, nkeys) 范围内的键值对nodeAppendRange(new, old, idx, idx+1, old.nkeys()-idx-1)return new
}
2.3 关键点分析
  1. 删除后的调整

    • 如果删除导致节点变为空,则需要合并或调整树结构。
    • 如果只剩下一个哨兵值,则清空树。
  2. 边界情况

    • 如果键不存在,则直接返回 false
    • 如果树为空,则无需执行任何操作。

3. 哨兵值的作用

哨兵值是一个技巧,用于简化查找逻辑。以下是其作用的详细说明:

  1. 覆盖整个键空间

    • 哨兵值是最小的键,确保树始终覆盖整个键空间。
    • 即使插入的键小于当前树中的所有键,nodeLookupLE 函数仍然能找到一个位置。
  2. 避免边界情况

    • 如果没有哨兵值,当查找的键小于树中的最小键时,nodeLookupLE 函数可能会失败。
    • 哨兵值的存在使得查找逻辑更加健壮。

4. 示例用法

以下是一个简单的例子,展示如何使用 B+ 树的高级接口:

func ExampleBTreeUsage() {// 初始化 B+ 树tree := &BTree{get: func(pageNum uint64) []byte {// 模拟从磁盘读取节点return loadFromDisk(pageNum)},new: func(data []byte) uint64 {// 模拟分配新页面return allocatePage(data)},del: func(pageNum uint64) {// 模拟释放页面deallocatePage(pageNum)},}// 插入键值对tree.Insert([]byte("key1"), []byte("value1"))tree.Insert([]byte("key2"), []byte("value2"))// 删除键success := tree.Delete([]byte("key1"))fmt.Printf("Deleted key1: %v\n", success)// 尝试删除不存在的键success = tree.Delete([]byte("key3"))fmt.Printf("Deleted key3: %v\n", success)
}

5.2 合并节点

在 B+ 树中,删除操作可能导致某些节点变得几乎为空。为了优化存储空间,我们可以通过合并相邻节点来减少节点数量。以下是实现合并逻辑的详细设计和代码。


1. 合并条件

在删除键值对后,需要检查是否满足合并条件。以下是一些关键点:

  1. 合并阈值

    • 如果一个节点的大小小于 BTREE_PAGE_SIZE / 4(即页面大小的四分之一),则认为该节点可以被合并。
    • 这是一个软限制,用于尽早触发合并操作,避免树中存在大量几乎空的节点。
  2. 选择兄弟节点

    • 如果目标节点有左兄弟节点或右兄弟节点,且合并后的节点大小不超过 BTREE_PAGE_SIZE,则可以选择合并。
  3. 优先级

    • 通常优先选择左兄弟节点进行合并(如果可用)。
    • 如果左兄弟节点不可用,则尝试与右兄弟节点合并。

2. 实现细节

2.1 shouldMerge 函数

该函数用于判断是否需要合并,并返回应该合并的兄弟节点(左或右)。

// 判断更新后的子节点是否应与其兄弟节点合并
func shouldMerge(tree *BTree, node BNode,idx uint16, updated BNode,
) (int, BNode) {// 如果更新后的节点大小大于阈值,则无需合并if updated.nbytes() > BTREE_PAGE_SIZE/4 {return 0, BNode{}}// 检查左兄弟节点if idx > 0 {sibling := BNode(tree.get(node.getPtr(idx - 1)))merged := sibling.nbytes() + updated.nbytes() - HEADERif merged <= BTREE_PAGE_SIZE {return -1, sibling // 左兄弟节点}}// 检查右兄弟节点if idx+1 < node.nkeys() {sibling := BNode(tree.get(node.getPtr(idx + 1)))merged := sibling.nbytes() + updated.nbytes() - HEADERif merged <= BTREE_PAGE_SIZE {return +1, sibling // 右兄弟节点}}// 不满足合并条件return 0, BNode{}
}

2.2 leafDelete 函数

从叶节点中删除指定索引的键值对。

// 从叶节点中删除指定索引的键值对
func leafDelete(new BNode, old BNode, idx uint16) {// 设置新节点的头部信息new.setHeader(BNODE_LEAF, old.nkeys()-1)// 复制 [0, idx) 范围内的键值对nodeAppendRange(new, old, 0, 0, idx)// 复制 [idx+1, nkeys) 范围内的键值对nodeAppendRange(new, old, idx, idx+1, old.nkeys()-idx-1)
}

2.3 nodeMerge 函数

将两个节点合并为一个节点。

// 将两个节点合并为一个节点
func nodeMerge(new BNode, left BNode, right BNode) {// 设置新节点的头部信息new.setHeader(left.NodeType(), left.nkeys()+right.nkeys())// 复制左节点的内容nodeAppendRange(new, left, 0, 0, left.nkeys())// 复制右节点的内容nodeAppendRange(new, right, left.nkeys(), 0, right.nkeys())
}

2.4 nodeReplace2Kid 函数

将两个相邻的链接替换为一个链接。

// 将两个相邻链接替换为一个链接
func nodeReplace2Kid(new BNode, old BNode, idx uint16, ptr uint64, key []byte,
) {// 设置新节点的头部信息new.setHeader(old.NodeType(), old.nkeys()-1)// 复制 [0, idx) 范围内的键值对nodeAppendRange(new, old, 0, 0, idx)// 插入新的键值对nodeAppendKV(new, idx, ptr, key, nil)// 复制 [idx+2, nkeys) 范围内的键值对nodeAppendRange(new, old, idx+1, idx+2, old.nkeys()-idx-2)
}

3. 合并逻辑的应用

在删除操作中,当检测到某个节点满足合并条件时,执行以下步骤:

  1. 选择兄弟节点

    • 使用 shouldMerge 函数判断是否需要合并以及选择哪个兄弟节点。
  2. 执行合并

    • 使用 nodeMerge 函数将目标节点与其兄弟节点合并。
  3. 更新父节点

    • 使用 nodeReplace2Kid 函数更新父节点中的链接。

4. 示例用法

以下是一个简单的例子,展示如何在删除操作中应用合并逻辑:

func ExampleNodeMerge() {// 初始化 B+ 树tree := &BTree{get: func(pageNum uint64) []byte {// 模拟从磁盘读取节点return loadFromDisk(pageNum)},new: func(data []byte) uint64 {// 模拟分配新页面return allocatePage(data)},del: func(pageNum uint64) {// 模拟释放页面deallocatePage(pageNum)},}// 删除键值对node := tree.get(tree.root)idx := nodeLookupLE(node, []byte("key1"))// 执行删除操作updated := leafDelete(BNode{}, node, idx)// 检查是否需要合并direction, sibling := shouldMerge(tree, node, idx, updated)if direction != 0 {merged := BNode(make([]byte, BTREE_PAGE_SIZE))if direction == -1 {// 合并左兄弟节点nodeMerge(merged, sibling, updated)} else {// 合并右兄弟节点nodeMerge(merged, updated, sibling)}// 更新父节点nodeReplace2Kid(BNode{}, node, idx, tree.new(merged), merged.getKey(0))}fmt.Println("Node merge completed.")
}

5. 总结

通过实现合并逻辑,我们可以优化 B+ 树的存储空间利用率。关键点包括:

  1. 合并条件

    • 使用 BTREE_PAGE_SIZE / 4 作为软限制,尽早触发合并操作。
  2. 合并操作

    • 使用 nodeMergenodeReplace2Kid 函数完成节点合并和父节点更新。
  3. 性能优化

    • 避免树中存在大量几乎空的节点,提高查询和插入效率。

这种设计使得 B+ 树能够动态调整结构,适应频繁的插入和删除操作,同时保持高效的存储和查询性能。

5.3 B+ 树删除操作

B+ 树的删除操作与插入操作类似,但核心区别在于:插入可能会导致节点分裂,而删除可能会导致节点合并。以下是完整的删除逻辑实现。


1. 删除逻辑概述

1.1 删除流程
  1. 递归查找

    • 从根节点开始,递归查找目标键所在的叶节点。
    • 如果找到目标键,则执行删除操作。
  2. 处理删除后的节点

    • 如果删除后节点变为空或过小(小于 BTREE_PAGE_SIZE / 4),则检查是否需要与其兄弟节点合并。
    • 如果没有兄弟节点可供合并,则将空节点传播到父节点。
  3. 更新父节点

    • 如果发生合并,则更新父节点以反映子节点的变化。
  4. 边界情况

    • 如果树中只剩下一个空的根节点,则清空整个树。

2. 实现细节

2.1 treeDelete 函数

该函数是 B+ 树删除的核心入口,负责递归查找和删除目标键。

// 从树中删除一个键
func treeDelete(tree *BTree, node BNode, key []byte) BNode {// 创建一个新的临时节点new := BNode(make([]byte, BTREE_PAGE_SIZE))// 查找目标键的位置idx := nodeLookupLE(node, key)// 根据节点类型执行不同的操作switch node.btype() {case BNODE_LEAF:// 叶节点if !bytes.Equal(key, node.getKey(idx)) {return BNode{} // 键不存在}// 删除键值对leafDelete(new, node, idx)case BNODE_NODE:// 内部节点nodeDelete(tree, new, idx, key)default:panic("bad node!")}return new
}

2.2 nodeDelete 函数

对于内部节点,删除操作是递归的。删除完成后,需要检查是否需要合并子节点。

// 从内部节点删除一个键
func nodeDelete(tree *BTree, node BNode, idx uint16, key []byte) BNode {// 获取子节点的指针kptr := node.getPtr(idx)// 递归删除子节点中的键updated := treeDelete(tree, tree.get(kptr), key)if len(updated) == 0 {return BNode{} // 键未找到}// 释放旧的子节点tree.del(kptr)// 创建一个新的临时节点new := BNode(make([]byte, BTREE_PAGE_SIZE))// 检查是否需要合并mergeDir, sibling := shouldMerge(tree, node, idx, updated)switch {case mergeDir < 0: // 合并左兄弟节点merged := BNode(make([]byte, BTREE_PAGE_SIZE))nodeMerge(merged, sibling, updated)tree.del(node.getPtr(idx - 1))nodeReplace2Kid(new, node, idx-1, tree.new(merged), merged.getKey(0))case mergeDir > 0: // 合并右兄弟节点merged := BNode(make([]byte, BTREE_PAGE_SIZE))nodeMerge(merged, updated, sibling)tree.del(node.getPtr(idx + 1))nodeReplace2Kid(new, node, idx, tree.new(merged), merged.getKey(0))case mergeDir == 0 && updated.nkeys() == 0:// 子节点为空且没有兄弟节点assert(node.nkeys() == 1 && idx == 0) // 父节点只有一个子节点new.setHeader(BNODE_NODE, 0)          // 父节点也变为空case mergeDir == 0 && updated.nkeys() > 0:// 不需要合并,直接更新父节点nodeReplaceKidN(tree, new, node, idx, updated)}return new
}

2.3 关键点分析
  1. 递归删除

    • 删除操作从根节点开始,递归查找目标叶节点。
    • 如果目标键存在于叶节点中,则直接删除。
  2. 合并条件

    • 使用 shouldMerge 函数判断是否需要合并以及选择哪个兄弟节点。
    • 如果合并后的节点大小不超过 BTREE_PAGE_SIZE,则执行合并。
  3. 空节点传播

    • 如果一个节点变为空且没有兄弟节点,则将其传播到父节点。
    • 在极端情况下,如果根节点变为空,则清空整个树。
  4. 边界情况

    • 如果树中只剩下一个空的根节点,则清空树。

3. 示例用法

以下是一个简单的例子,展示如何使用 B+ 树的删除接口:

func ExampleTreeDelete() {// 初始化 B+ 树tree := &BTree{get: func(pageNum uint64) []byte {// 模拟从磁盘读取节点return loadFromDisk(pageNum)},new: func(data []byte) uint64 {// 模拟分配新页面return allocatePage(data)},del: func(pageNum uint64) {// 模拟释放页面deallocatePage(pageNum)},}// 删除键值对key := []byte("example_key")node := tree.get(tree.root)// 执行删除操作updatedRoot := treeDelete(tree, node, key)// 更新根节点if len(updatedRoot) == 0 {fmt.Println("Key not found.")} else {tree.root = tree.new(updatedRoot)fmt.Println("Key deleted successfully.")}
}

4. 总结

通过上述设计和实现,我们完成了 B+ 树的删除操作。关键点包括:

  1. 递归删除

    • 从根节点开始递归查找目标键,直到找到目标叶节点。
  2. 合并机制

    • 使用 nodeMergenodeReplace2Kid 函数完成节点合并和父节点更新。
  3. 空节点处理

    • 如果一个节点变为空且没有兄弟节点,则将其传播到父节点,最终可能导致根节点变为空。
  4. 性能优化

    • 通过尽早触发合并操作(使用 BTREE_PAGE_SIZE / 4 作为软限制),避免树中存在大量几乎空的节点。

这种设计使得 B+ 树能够高效地支持动态数据集的删除操作,同时保持高效的存储和查询性能。

5.4 测试 B+ 树

为了测试 B+ 树的正确性和性能,我们需要模拟页面管理回调(getnewdel),并验证树结构和数据的一致性。以下是详细的测试设计。


1. 模拟内存中的页面管理

为了测试 B+ 树的功能,我们可以在内存中模拟页面管理系统。以下是实现的关键部分:

1.1 定义测试上下文 C
type C struct {tree  BTree                // B+ 树实例ref   map[string]string    // 参考数据(用于验证)pages map[uint64]BNode     // 内存中的页面
}
  • tree:B+ 树实例,使用自定义的页面管理回调。
  • ref:参考数据,用一个 map[string]string 来存储键值对,用于验证 B+ 树的数据一致性。
  • pages:内存中的页面,用于验证指针的有效性和读取页面内容。
1.2 初始化测试上下文
func newC() *C {pages := map[uint64]BNode{}return &C{tree: BTree{get: func(ptr uint64) []byte {node, ok := pages[ptr]assert(ok) // 确保页面存在return node},new: func(node []byte) uint64 {assert(BNode(node).nbytes() <= BTREE_PAGE_SIZE) // 确保节点大小符合限制ptr := uint64(uintptr(unsafe.Pointer(&node[0]))) // 使用内存地址作为页面指针assert(pages[ptr] == nil) // 确保页面尚未分配pages[ptr] = nodereturn ptr},del: func(ptr uint64) {assert(pages[ptr] != nil) // 确保页面存在delete(pages, ptr)},},ref:   map[string]string{},pages: pages,}
}
  • get:从内存中读取页面,确保页面指针有效。
  • new:分配新页面,并将其存储在 pages 中。
  • del:释放页面,确保页面指针有效后删除。

2. 测试逻辑

2.1 添加键值对
func (c *C) add(key string, val string) {c.tree.Insert([]byte(key), []byte(val)) // 插入到 B+ 树中c.ref[key] = val                       // 更新参考数据
}
  • 功能:向 B+ 树插入键值对,同时更新参考数据。
2.2 删除键值对
func (c *C) remove(key string) {success := c.tree.Delete([]byte(key)) // 从 B+ 树中删除键if success {delete(c.ref, key) // 更新参考数据}
}
  • 功能:从 B+ 树删除键值对,同时更新参考数据。
2.3 验证树结构

验证 B+ 树的结构是否有效:

  1. 键排序

    • 叶节点中的键必须按升序排列。
    • 内部节点中的分隔键也必须按升序排列。
  2. 节点大小限制

    • 每个节点的大小不能超过 BTREE_PAGE_SIZE
func (c *C) validateStructure(node BNode) {switch node.btype() {case BNODE_LEAF:// 验证叶节点for i := uint16(1); i < node.nkeys(); i++ {assert(bytes.Compare(node.getKey(i-1), node.getKey(i)) < 0) // 键必须递增}case BNODE_NODE:// 验证内部节点for i := uint16(1); i < node.nkeys(); i++ {assert(bytes.Compare(node.getKey(i-1), node.getKey(i)) < 0) // 分隔键必须递增}// 递归验证子节点for i := uint16(0); i < node.nkeys(); i++ {child := c.tree.get(node.getPtr(i))c.validateStructure(child)}default:panic("bad node!")}assert(node.nbytes() <= BTREE_PAGE_SIZE) // 节点大小不能超过限制
}
2.4 验证数据一致性

验证 B+ 树中的数据是否与参考数据一致:

func (c *C) validateData() {for key, val := range c.ref {result := c.tree.Lookup([]byte(key)) // 查找键assert(result != nil && bytes.Equal(result, []byte(val))) // 数据必须匹配}
}

3. 测试用例

以下是一些典型的测试场景:

3.1 基本插入和查找
func TestBasicInsertAndLookup(t *testing.T) {c := newC()// 插入键值对c.add("key1", "value1")c.add("key2", "value2")c.add("key3", "value3")// 验证结构和数据c.validateStructure(c.tree.get(c.tree.root))c.validateData()
}
3.2 插入后分裂
func TestSplitOnInsert(t *testing.T) {c := newC()// 插入大量键值对以触发分裂for i := 0; i < 100; i++ {key := fmt.Sprintf("key%d", i)val := fmt.Sprintf("value%d", i)c.add(key, val)}// 验证结构和数据c.validateStructure(c.tree.get(c.tree.root))c.validateData()
}
3.3 删除后合并
func TestMergeOnDelete(t *testing.T) {c := newC()// 插入键值对for i := 0; i < 10; i++ {key := fmt.Sprintf("key%d", i)val := fmt.Sprintf("value%d", i)c.add(key, val)}// 删除部分键值对以触发合并for i := 0; i < 5; i++ {key := fmt.Sprintf("key%d", i)c.remove(key)}// 验证结构和数据c.validateStructure(c.tree.get(c.tree.root))c.validateData()
}

4. 总结

通过上述设计和实现,我们可以全面测试 B+ 树的功能和性能。关键点包括:

  1. 模拟页面管理

    • 在内存中模拟页面分配、读取和释放操作。
  2. 验证结构

    • 确保树结构有效(键排序、节点大小限制)。
  3. 验证数据一致性

    • 确保 B+ 树中的数据与参考数据一致。
  4. 典型测试场景

    • 基本插入和查找。
    • 插入后分裂。
    • 删除后合并。

这种测试框架为验证 B+ 树的正确性和性能提供了强大的工具,同时也为进一步优化(如磁盘存储支持)奠定了基础。

代码仓库地址:database-go

相关文章:

B+树删除和测试

B树删除和测试 5.1 高级接口&#xff1a;B 树作为键值存储 在本章中&#xff0c;我们将实现 B 树的高级接口&#xff0c;使其能够作为键值存储&#xff08;Key-Value Store&#xff09;使用。这些接口包括插入和删除操作&#xff0c;并处理根节点的维护。 1. 插入接口 1.1 I…...

常用算法解析:从基础排序到图论应用

一、算法基础与设计原则 算法是计算机解决问题的核心工具&#xff0c;其五大基本特性决定了程序的可靠性&#xff1a; 有穷性&#xff1a;算法必须能在有限步骤内终止确定性&#xff1a;每步操作无歧义可行性&#xff1a;可被计算机执行输入输出&#xff1a;具备数据交互能力…...

Java Web项目(一)

框架 java web项目总工分为两部分&#xff1a;客户端&#xff08;前端&#xff09;和服务端&#xff08;后端&#xff09; 客户端发起请求&#xff0c;服务端接受请求并进行处理 发起请求的方式&#xff1a;from表单、jQuery ajax from表单 造成全局的变化&#xff0c;在发…...

兴达易控DP主站网关数据映射快速配置案例

兴达易控DP主站网关数据映射快速配置案例 在工业自动化的领域&#xff0c;不同通讯协议之间的转换是常见的需求。特别是Profibus DP与Modbus-RTU这两种广泛应用于不同系统和设备的通讯协议&#xff0c;它们之间的数据转换显得尤为重要。本文将详细探讨兴达易控Profibus DP主站…...

Tailwindcss 入门 v4.1

以 react 为例&#xff0c;步骤如下&#xff1a; npm create vitelatest my-app -- --template react 选择 React 和 JavaScript 根据上述命令的输出提示&#xff0c;运行以下命令 cd my-app npm install npm run dev 一个 React App 初始化完成。 安装 Tailwindcss theme …...

通过 WebSocket 接收和播放 WSS 协议视频流

1.创建wss协议视频 1.1必备包 npm install ws ffmpeg-installer/ffmpeg fluent-ffmpeg 说明&#xff1a;安装以下三个包。 1.2代码实现 说明&#xff1a;创建WebSocket服务器&#xff0c;端口为8080 import { WebSocket, WebSocketServer } from ws; // 导入 WebSocket 和 W…...

HTML 如何改变字体颜色?深入解析与实践指南

网页上的字体颜色是网页设计中至关重要的元素之一&#xff0c;它像字体大小一样&#xff0c;对于提升用户体验起着举足轻重的作用。精心选择和运用字体颜色&#xff0c;能够增强页面的可读性、突出重点信息、营造特定的情感氛围&#xff0c;甚至直接影响用户的视觉感受和品牌认…...

tigase源码学习杂记-组件化设计

前言 tigase官方号称高度抽象和组件化。这篇文章就记录一下我研究组件化的相关设计 概述 我的理解tigase高度组件化是所有的关键的功能的类&#xff0c;它都称之为组件&#xff0c;即只要继承于BasicComponent&#xff0c;它都可以成为组件&#xff0c;BasicComponent类实现…...

十二、人工神经网络及其应用

写在前面 这部分内容老师说很重要,不管是实验还是考试占比都非常大 AIGC的全称是“Artificial Intelligence Generated Content”,即人工智能生成内容。这一术语通常用于指代通过人工智能技术自动生成的各种类型的内容,如文本、图像、音频和视频等。随着AI技术的发展,AIG…...

vscode使用技巧

一、符号定位技巧 ‌跳转到定义‌ F12 或右键「Go to Definition」跳转到符号定义位置‌CtrlClick 直接点击符号跳转&#xff08;支持变量/函数/类&#xff09; ‌符号大纲视图‌ CtrlShiftO 打开文件符号大纲&#xff0c;支持模糊搜索符号名‌输入: 分类显示符号&#xff08;…...

测试基础笔记第七天

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 一、cat命令二、ls -al命令三、>重定向符号四、>>追加重定向符号五、less/more命令六、grep命令七、|管道符八、clear命令九、head命令十、tail命令十一、…...

FOC控制中的正弦PWM和空间矢量PWM对比与理解

参考&#xff1a; simple foc&#xff1a;https://docs.simplefoc.com/docs_chinese/foc_theory博客&#xff1a;https://blog.csdn.net/qq_43332314/article/details/126449398 一、无刷电机基础原理 1.&#xff0c; 原理图&#xff1a;至少三个绕组线圈&#xff08;定子&…...

【Oracle专栏】函数中SQL拼接参数 报错处理

Oracle相关文档,希望互相学习,共同进步 风123456789~-CSDN博客 1.背景 最近同事反馈了一个很奇怪的问题,即有一个函数,入参是当前年月,主要作用是通过SQL语句将不合规的数据插入到指定表中,插入数据时带上入参的年月参数。当前问题:单独测试SQL没有问题可以执行成功,…...

无意间发现的宝藏项目:开源世界中的演示项目精选合集

&#x1f31f;无意间发现的宝藏项目&#xff1a;开源世界中的演示项目精选合集 最近在 GitHub 上随手翻了翻 Spring 官方代码仓库&#xff0c;意外发现一个超有趣的演示项目 —— spring-petclinic。一个轻量但结构完整的 Spring 全家桶演示&#xff0c;让人忍不住一探究竟。 这…...

OpenCSG AutoHub v0.5.0 版本发布

OpenCSG AutoHub v0.5.0 版本发布 作为一款智能化自动化操作的浏览器插件&#xff0c;AutoHub不断致力于为用户提供更加高效、便捷的网页浏览体验。本次 v0.5.0版本 的发布&#xff0c;不仅进一步强化了核心功能&#xff0c;还引入了一些创新特性&#xff0c;旨在帮助用户更智…...

基于Python智能体API的Word自动化排版系统:从零构建全流程模块化工作流与版本控制研究

基于Python智能体API的Word自动化排版系统:从零构建全流程模块化工作流与版本控制实践研究 1. 引言2. 研究背景与意义3. 自动排版工作流的设计原理3.1 文档内容提取与解析3.2 样式参数与格式化规则3.3 智能体API接口调用3.4 自动生成与批量处理3.5 与生成式AI的协同4. 系统架构…...

在 Node.js 中设置响应的 MIME 类型

在 Node.js 中设置响应的 MIME 类型是为了让浏览器正确解析服务器返回的内容&#xff0c;比如 HTML、CSS、图片、JSON 等。我们通常通过设置响应头中的 Content-Type 字段来完成。 ✅ 一、什么是 MIME 类型&#xff08;Content-Type&#xff09;&#xff1f; MIME&#xff08;…...

jsch(shell终端Java版)

学习笔记 Java SSH库使用简介&#xff1a;Apache sshd和JSch&#xff08;Java Secure Channel&#xff09; github - fork of the popular jsch library JSch学习笔记 web-shell - gitee代码 - 纯Java实现一个web shell登录Linux远程主机&#xff0c;技术选型 SpringBoot …...

Redis分布式锁RedLock机制详解

一、RedLock机制解决的问题 核心场景&#xff1a;解决传统Redis单节点/主从架构下分布式锁的不可靠问题。当主节点故障时&#xff0c;若从节点未同步锁信息&#xff0c;可能导致多个客户端同时持有锁&#xff0c;破坏互斥性。 典型问题案例&#xff1a; 主从切换锁丢失&…...

Vivado中Tri_mode_ethernet_mac的时序约束、分析、调整——(五)调试注意的问题

一、几个注意点 1、每个bank中IO的组织形式 1Bank的52Pins分4 Byte Group&#xff0c;每Byte Group 13PinsNibble_up 7Pins Nibble_low 6Pins。 每个nibble一个bitslice_control管理自己的6~7个pins 。 每个pin对应一个bitslice&#xff0c;它内部又包含多个component&#…...

MFC文件-写MP4

下载本文件 本文件将创作MP4视频文件代码整合到两个文件中&#xff08;Mp4Writer.h和Mp4Writer.cpp)&#xff0c;将IYUV视频流&#xff0c;PCM音频流写入MP4文件。本文件仅适用于MFC程序。 使用方法 1.创建MFC项目。 2.将Mp4Writer.h和Mp4Writer.cpp文件复制到项目目录下。 3…...

PyTorch 深度学习实战(39):归一化技术对比(BN/LN/IN/GN)

在上一篇文章中&#xff0c;我们全面解析了注意力机制的发展历程。本文将深入探讨深度学习中的归一化技术&#xff0c;对比分析BatchNorm、LayerNorm、InstanceNorm和GroupNorm四种主流方法&#xff0c;并通过PyTorch实现它们在图像分类和生成任务中的应用效果。 一、归一化技术…...

C#/.NET/.NET Core技术前沿周刊 | 第 35 期(2025年4.14-4.20)

前言 C#/.NET/.NET Core技术前沿周刊&#xff0c;你的每周技术指南针&#xff01;记录、追踪C#/.NET/.NET Core领域、生态的每周最新、最实用、最有价值的技术文章、社区动态、优质项目和学习资源等。让你时刻站在技术前沿&#xff0c;助力技术成长与视野拓宽。 欢迎投稿、推荐…...

柱状图QCPBars

一、QCPBars 概述 QCPBars 是 QCustomPlot 中用于绘制柱状图/条形图的类&#xff0c;支持单组或多组柱状图显示&#xff0c;可自定义宽度、颜色和间距等属性。 二、主要属性 属性类型描述widthdouble柱子的宽度&#xff08;坐标轴单位&#xff09;widthTypeWidthType宽度计算…...

2025-04-20 李沐深度学习4 —— 自动求导

文章目录 1 导数拓展1.1 标量导数1.2 梯度&#xff1a;向量的导数1.3 扩展到矩阵1.4 链式法则 2 自动求导2.1 计算图2.2 正向模式2.3 反向模式 3 实战&#xff1a;自动求导3.1 简单示例3.2 非标量的反向传播3.3 分离计算3.4 Python 控制流 硬件配置&#xff1a; Windows 11Inte…...

Nginx在微服务架构项目(Spring Cloud)中的强大作用

文章目录 一、Nginx是什么&#xff1f;二、Nginx在微服务架构&#xff08;Spring Cloud&#xff09;项目中的作用1.前端静态资源托管2.反向代理后端 API3.负载均衡4.SSL 证书与 HTTPS 支持5.缓存与压缩优化6.安全防护7.灰度发布与流量控制8.跨域处理&#xff08;CORS&#xff0…...

Mysql相关知识2:Mysql隔离级别、MVCC、锁

文章目录 MySQL的隔离级别可重复读的实现原理Mysql锁按锁的粒度分类按锁的使用方式分类按锁的状态分类 MySQL的隔离级别 在 MySQL 中&#xff0c;隔离级别定义了事务之间相互隔离的程度&#xff0c;用于控制一个事务对数据的修改在何时以及如何被其他事务可见。MySQL 支持四种…...

解决IDEA创建SpringBoot项目没有Java版本8

问题&#xff1a;idea2023版本创建springboot的过程中&#xff0c;选择java版本时发现没有java8版本&#xff0c;只有java17和java20 原因&#xff1a;spring2.X版本在2023年11月24日停止维护了&#xff0c;因此创建spring项目时不再有2.X版本的选项&#xff0c;只能从3.1.X版本…...

第十章:Agent 的评估、调试与可观测性:确保可靠与高效

引言 随着我们一步步构建出越来越复杂的 AI Agent&#xff0c;赋予它们高级工具和更智能的策略&#xff0c;一个至关重要的问题浮出水面&#xff1a;我们如何知道这些 Agent 是否真的有效、可靠&#xff1f;当它们行为不符合预期时&#xff0c;我们又该如何诊断和修复问题&…...

8节串联锂离子电池组可重构buck-boost均衡拓扑结构 simulink模型仿真

8节串联锂离子电池组 极具创新性 动态分组均衡策略&#xff0c;支持3种均衡模式 1.最高SOC电池给最低SOC电池均衡 2.高能电池组电池给最低SOC电池均衡 3.高能电池组电池给低能电池组电池均衡 支持手动设置均衡开启阈值和终止阈值 均衡效果非常好...

Oracle EBS COGS Recognition重复生成(一借一贷)

背景 月结用户反馈“发出商品”(实际为递延销货成本)不平,本月都是正常操作月结程序,如正常操作步骤如下: 记录订单管理事务处理 (Record Order Management Transactions)收集收入确认信息 (Collect Revenue Recognition Information)生成销货成本确认事件 (Generate COGS …...

Linux命令--将控制台的输入写入文件

原文网址&#xff1a;Linux命令--将控制台的输入写入文件-CSDN博客 简介 本文介绍Linux将控制台的输入写入文件的方法。 方案1&#xff1a;cat > file1&#xff08;推荐&#xff09; 普通用法 cat > file1 输入结束后&#xff0c;用CtrlD退出。 示例 使用root权限…...

使用BQ76PL455和STM32的SAE电动方程式电动汽车智能BMS

BMS对任何电动汽车来说都是必不可少的&#xff0c;它可以监控电池的行为&#xff0c;确保安全行驶。 该项目旨在降低成本&#xff0c;同时为每个电池模块提供可扩展的BMS。BQ76PL455具有监测6-16个单元的能力&#xff0c;8通道辅助输入(用于温度监测)和多达15个其他ic用于Daisy…...

OpenCV 模板与多个对象匹配方法详解(继OpenCV 模板匹配方法详解)

文章目录 前言1.导入库2.图片预处理3.输出模板图片的宽和高4.模板匹配5.获取匹配结果中所有符合阈值的点的坐标5.1 threshold 0.9&#xff1a;5.2 loc np.where(res > threshold)&#xff1a; 6.遍历所有匹配点6.1 loc 的结构回顾6.2 loc[::-1] 的作用6.2.1 为什么需要反转…...

7.0/Q1,Charls最新文章解读

文章题目&#xff1a;Anti-hypertensive medication adherence, socioeconomic status, and cognitive aging in the Chinese community-dwelling middle-aged and older adults ≥ 45 years: a population-based longitudinal study DOI&#xff1a;10.1186/s12916-025-03949-…...

【第三十二周】CLIP 论文阅读笔记

CLIP 摘要Abstract文章信息引言方法预训练推理Q&A 关键代码实验结果总结 摘要 本篇博客介绍了CLIP&#xff08;Contrastive Language-Image Pre-training&#xff09;&#xff0c;这是OpenAI于2021年提出的多模态预训练模型&#xff0c;其核心思想是通过对比学习将图像与文…...

在 Ubuntu 系统上安装 PostgreSQL

在 Ubuntu 系统上安装 PostgreSQL 的完整指南&#xff1a; 一、安装 PostgreSQL&#xff08;最新版本&#xff09; 1. 更新软件包列表&#xff1a; bash sudo apt update 2. 安装 PostgreSQL 和客户端工具&#xff1a; bash sudo apt install postgresql po…...

【MySQL】数据类型

&#x1f3e0;个人主页&#xff1a;Yui_ &#x1f351;操作环境&#xff1a;Centos7 &#x1f680;所属专栏&#xff1a;MySQL 文章目录 前言1. bit类型2.tinyint类型3. float类型4. decimal5. char类型6. varchar5&6 char和varchar的比较7.日期和时间类型8.enum和set总结 …...

Mac上Cursor无法安装插件解决方法

可能是微软的vscode被cursor这些新晋的AI-IDE白嫖够了&#xff0c;所以现在被制裁了&#xff0c;cursor下载不了vscode插件了。​需要自己修改扩展商店源。 近期微软调整了 API 鉴权策略或限制了非官方客户端的访问权限。 解决方案 一、找到 product.json 文件 打开终端&…...

PI0 Openpi 部署(仅测试虚拟环境)

https://github.com/Physical-Intelligence/openpi/tree/main 我使用4070tisuper, 14900k,完全使用官方默认设置&#xff0c;没有出现其他问题。 目前只对examples/aloha_sim进行测试&#xff0c;使用docker进行部署, 默认使用pi0_aloha_sim模型(但是文档上没找到对应的&…...

NumPy数组和二维列表的区别

在 Python 中&#xff0c;NumPy 数组和二维列表在性能方面存在诸多不同&#xff0c;下面从存储方式、内存占用、操作速度、缓存局部性这几个角度详细分析。 存储方式 二维列表&#xff1a;它是 Python 内置的数据结构&#xff0c;列表中的每个元素实际上是一个引用&#xff0…...

学习设计模式《四》——单例模式

一、基础概念 单例模式的本质【控制实例数目】&#xff1b; 单例模式的定义&#xff1a;是用来保证这个类在运行期间只会被创建一个类实例&#xff1b;单例模式还提供了一个全局唯一访问这个类实例的访问点&#xff08;即GetInstance方法&#xff09;单例模式只关心类实例的创建…...

构建具备推理与反思能力的高级 Prompt:LLM 智能代理设计指南

在构建强大的 AI 系统&#xff0c;尤其是基于大语言模型&#xff08;LLM&#xff09;的智能代理&#xff08;Agent&#xff09;时&#xff0c;Prompt 设计的质量决定了系统的智能程度。传统 Prompt 通常是简单的问答或填空式指令&#xff0c;而高级任务需要更具结构性、策略性和…...

NLP 梳理03 — 停用词删除和规范化

一、说明 前文我们介绍了标点符号删除、文本的大小写统一&#xff0c;本文介绍英文文章的另一些删除内容&#xff0c;停用词删除。还有规范化处理。 二、什么是停用词&#xff0c;为什么删除它们&#xff1f; 2.1 停用词的定义 停用词是语言中的常用词&#xff0c;通常语义…...

算法—插入排序—js(小数据或基本有序数据)

插入排序原理&#xff1a;&#xff08;适合小规模数据&#xff09; 将数组分为“已排序”和“未排序”两部分&#xff0c;逐个将未排序元素插入到已排序部分的正确位置。 特点&#xff1a; 时间复杂度&#xff1a;平均 O(n)&#xff0c;最优&#xff08;已有序&#xff09;O(n…...

家庭电脑隐身后台自动截屏软件,可远程查看

7-4 本文介绍一个小软件&#xff0c;可以在电脑后台运行&#xff0c;并且记录电脑的屏幕画面保存下来&#xff0c;并且可以远程提取查看。 可以用于记录长时间运行的软件的执行画面过程&#xff0c;或者用于记录家庭中小孩使用电脑的过程&#xff0c;如果没有好好上网课&…...

【Agent】AI智能体评测基座AgentCLUE-General

note AgentCLUE-General将题目划分为“联网检索”、“数据分析”、“多模态理解”和“多场景组合”任务AgentCLUE-General为每个题目都提供一个标准答案&#xff0c;将Agent智能体的答案与标准答案进行规则匹配判断对错 文章目录 note一、任务划分和场景划分二、答案提取的pro…...

最新iOS性能测试方法与教程

一、工具instrument介绍 使用Xcode的instrument进行测试&#xff0c;instrument自带了很多性能方面的测试工具&#xff0c;如图所示&#xff1a; 二、常见性能测试内容 不管是安卓还是iOS的性能测试&#xff0c;常见的性能测试都要包含这五个方面&#xff1a; 1、内存&#xff…...

多模态大语言模型arxiv论文略读(三十)

Mastering Text-to-Image Diffusion: Recaptioning, Planning, and Generating with Multimodal LLMs ➡️ 论文标题&#xff1a;Mastering Text-to-Image Diffusion: Recaptioning, Planning, and Generating with Multimodal LLMs ➡️ 论文作者&#xff1a;Ling Yang, Zhao…...

【AI论文】CLIMB:基于聚类的迭代数据混合自举语言模型预训练

摘要&#xff1a;预训练数据集通常是从网络内容中收集的&#xff0c;缺乏固有的领域划分。 例如&#xff0c;像 Common Crawl 这样广泛使用的数据集并不包含明确的领域标签&#xff0c;而手动整理标记数据集&#xff08;如 The Pile&#xff09;则是一项劳动密集型工作。 因此&…...