引言

        斐波那契堆(Fibonacci Heap)是一种高效的可合并堆数据结构,由 Michael L. Fredman 和 Robert E. Tarjan 于 1984 年提出。它在许多优先队列操作中提供了极佳的 amortized(摊还)时间复杂度,尤其适用于需要频繁进行合并操作的场景。

19.1 斐波那契堆结构

19.1.1 基本结构

斐波那契堆由一组满足最小堆性质的树组成,这些树都是无序的多叉树。它包含以下关键组件:

  1. 一个指向具有最小关键字的节点的指针(min pointer)
  2. 所有树的根节点通过双向循环链表连接
  3. 每个节点包含:
    • 关键字值
    • 父节点指针
    • 子节点指针
    • 左、右兄弟节点指针
    • 度数(子节点数量)
    • 一个标记(用于删除操作)

19.1.2 节点结构定义

下面是斐波那契堆节点的 C++ 结构定义:

#ifndef FIBONACCI_HEAP_H
#define FIBONACCI_HEAP_H#include <iostream>
#include <vector>
#include <stdexcept>// 斐波那契堆节点结构
template <typename T>
struct FibonacciNode {T key;                  // 节点存储的值int degree;             // 节点的度数(子节点数量)bool mark;              // 标记,用于删除操作FibonacciNode* parent;  // 父节点指针FibonacciNode* child;   // 子节点指针FibonacciNode* left;    // 左兄弟节点指针FibonacciNode* right;   // 右兄弟节点指针// 构造函数FibonacciNode(const T& k) : key(k), degree(0), mark(false),parent(nullptr), child(nullptr),left(this), right(this) {}
};// 斐波那契堆类
template <typename T>
class FibonacciHeap {
private:FibonacciNode<T>* minNode;  // 指向最小节点的指针int nodeCount;              // 堆中节点的总数// 将节点y从其所在的双向链表中移除void removeNode(FibonacciNode<T>* y);// 将节点y插入到以节点x为头的双向链表中void addNode(FibonacciNode<T>* x, FibonacciNode<T>* y);// 将节点y设为节点x的子节点void link(FibonacciNode<T>* y, FibonacciNode<T>* x);// 合并相同度数的树void consolidate();// 切断节点y与其父节点x之间的连接void cut(FibonacciNode<T>* y, FibonacciNode<T>* x);// 级联切断操作void cascadingCut(FibonacciNode<T>* y);// 销毁堆的辅助函数void destroyHeap(FibonacciNode<T>* node);public:// 构造函数FibonacciHeap() : minNode(nullptr), nodeCount(0) {}// 析构函数~FibonacciHeap();// 插入元素FibonacciNode<T>* insert(const T& key);// 查找最小元素const T& findMin() const;// 抽取最小元素T extractMin();// 合并两个斐波那契堆void merge(FibonacciHeap<T>& other);// 减小关键字值void decreaseKey(FibonacciNode<T>* x, const T& k);// 删除节点void deleteNode(FibonacciNode<T>* x);// 检查堆是否为空bool isEmpty() const { return nodeCount == 0; }// 获取堆中节点数量int size() const { return nodeCount; }
};#endif // FIBONACCI_HEAP_H

19.2 可合并堆操作

        斐波那契堆支持可合并堆的所有操作,并且大部分操作都能在常数摊还时间内完成。

19.2.1 插入操作

        插入操作非常简单,只需创建一个新节点,将其添加到根链表中,并更新最小节点指针(如果需要)。

步骤

  1. 创建一个包含关键字的新节点
  2. 将新节点加入根链表
  3. 如果新节点的关键字小于当前最小节点的关键字,更新最小节点指针
  4. 节点计数加 1

19.2.2 查找最小元素

        由于斐波那契堆维护了一个指向最小元素的指针,因此查找最小元素可以在 O (1) 时间内完成。

19.2.3 合并操作

        合并两个斐波那契堆只需将它们的根链表合并成一个双向链表,并更新最小节点指针。

步骤

  1. 合并两个堆的根链表
  2. 更新最小节点指针,指向两个堆中较小的那个最小节点
  3. 更新节点计数

19.2.4 抽取最小元素

        抽取最小元素是斐波那契堆中最复杂的操作之一,需要进行 "合并"(consolidate)过程来维持斐波那契堆的性质。

步骤

  1. 记录最小节点的值
  2. 将最小节点的所有子节点添加到根链表
  3. 从根链表中移除最小节点
  4. 执行 consolidate 操作,合并度数相同的树
  5. 更新最小节点指针
  6. 节点计数减 1
  7. 返回记录的最小节点值

下面是上述操作的 C++ 实现:

#include "fibonacci_heap.h"// 将节点y从其所在的双向链表中移除
template <typename T>
void FibonacciHeap<T>::removeNode(FibonacciNode<T>* y) {y->left->right = y->right;y->right->left = y->left;
}// 将节点y插入到以节点x为头的双向链表中
template <typename T>
void FibonacciHeap<T>::addNode(FibonacciNode<T>* x, FibonacciNode<T>* y) {y->left = x->left;y->right = x;x->left->right = y;x->left = y;
}// 将节点y设为节点x的子节点
template <typename T>
void FibonacciHeap<T>::link(FibonacciNode<T>* y, FibonacciNode<T>* x) {// 从根链表中移除yremoveNode(y);y->parent = x;// 将y添加为x的子节点if (x->child == nullptr) {x->child = y;y->left = y->right = y;} else {addNode(x->child, y);}x->degree++;  // 增加x的度数y->mark = false;  // 重置y的标记
}// 合并相同度数的树
template <typename T>
void FibonacciHeap<T>::consolidate() {// 计算最大可能度数int maxDegree = 0;FibonacciNode<T>* current = minNode;if (current != nullptr) {do {if (current->degree > maxDegree) {maxDegree = current->degree;}current = current->right;} while (current != minNode);}// 创建度数表std::vector<FibonacciNode<T>*> degreeTable(maxDegree + 1, nullptr);// 遍历根链表中的所有节点while (minNode != nullptr) {FibonacciNode<T>* x = minNode;int d = x->degree;// 移除当前节点,以便后续处理removeNode(x);x->left = x->right = x;  // 暂时形成一个单节点链表// 合并度数相同的树while (degreeTable[d] != nullptr) {FibonacciNode<T>* y = degreeTable[d];// 确保x是关键字较小的节点if (x->key > y->key) {std::swap(x, y);}// 将y链接到xlink(y, x);degreeTable[d] = nullptr;d++;}degreeTable[d] = x;}// 重置最小节点指针minNode = nullptr;// 重建根链表并找到新的最小节点for (int i = 0; i <= maxDegree; i++) {if (degreeTable[i] != nullptr) {if (minNode == nullptr) {minNode = degreeTable[i];minNode->left = minNode->right = minNode;} else {addNode(minNode, degreeTable[i]);if (degreeTable[i]->key < minNode->key) {minNode = degreeTable[i];}}}}
}// 插入元素
template <typename T>
FibonacciNode<T>* FibonacciHeap<T>::insert(const T& key) {FibonacciNode<T>* newNode = new FibonacciNode<T>(key);if (minNode == nullptr) {// 堆为空,新节点成为根节点minNode = newNode;} else {// 将新节点添加到根链表addNode(minNode, newNode);// 更新最小节点if (newNode->key < minNode->key) {minNode = newNode;}}nodeCount++;return newNode;
}// 查找最小元素
template <typename T>
const T& FibonacciHeap<T>::findMin() const {if (minNode == nullptr) {throw std::runtime_error("Heap is empty");}return minNode->key;
}// 抽取最小元素
template <typename T>
T FibonacciHeap<T>::extractMin() {if (minNode == nullptr) {throw std::runtime_error("Heap is empty");}FibonacciNode<T>* z = minNode;T minKey = z->key;// 将z的所有子节点添加到根链表if (z->child != nullptr) {FibonacciNode<T>* child = z->child;do {FibonacciNode<T>* nextChild = child->right;// 移除子节点的父指针child->parent = nullptr;// 将子节点添加到根链表addNode(minNode, child);child = nextChild;} while (child != z->child);}// 从根链表中移除zremoveNode(z);// 如果z是堆中唯一的节点if (z == z->right) {minNode = nullptr;} else {minNode = z->right;// 合并相同度数的树consolidate();}nodeCount--;delete z;return minKey;
}// 合并两个斐波那契堆
template <typename T>
void FibonacciHeap<T>::merge(FibonacciHeap<T>& other) {if (other.minNode == nullptr) {return;  // 另一个堆为空,无需合并}if (minNode == nullptr) {// 当前堆为空,直接使用另一个堆minNode = other.minNode;nodeCount = other.nodeCount;} else {// 合并两个根链表FibonacciNode<T>* thisLeft = minNode->left;FibonacciNode<T>* otherLeft = other.minNode->left;minNode->left = otherLeft;otherLeft->right = minNode;thisLeft->right = other.minNode;other.minNode->left = thisLeft;// 更新最小节点if (other.minNode->key < minNode->key) {minNode = other.minNode;}nodeCount += other.nodeCount;}// 清空另一个堆other.minNode = nullptr;other.nodeCount = 0;
}// 析构函数
template <typename T>
FibonacciHeap<T>::~FibonacciHeap() {if (minNode != nullptr) {destroyHeap(minNode);}
}// 销毁堆的辅助函数
template <typename T>
void FibonacciHeap<T>::destroyHeap(FibonacciNode<T>* node) {if (node == nullptr) return;FibonacciNode<T>* current = node;bool isFirst = true;// 遍历当前链表中的所有节点while (isFirst || current != node) {isFirst = false;FibonacciNode<T>* next = current->right;// 递归销毁子节点destroyHeap(current->child);delete current;current = next;}
}

19.2.5 综合案例:优先级队列

        斐波那契堆非常适合实现优先级队列,特别是需要频繁合并操作的场景。以下是一个使用斐波那契堆实现优先级队列的示例:

#include "fibonacci_heap.h"
#include <iostream>
#include <string>// 任务结构体,表示一个优先级队列中的任务
struct Task {int priority;  // 优先级,值越小优先级越高std::string description;  // 任务描述// 重载小于运算符,用于斐波那契堆的比较bool operator<(const Task& other) const {return priority < other.priority;}// 重载大于运算符bool operator>(const Task& other) const {return priority > other.priority;}
};// 输出任务信息
std::ostream& operator<<(std::ostream& os, const Task& task) {os << "任务: " << task.description << ", 优先级: " << task.priority;return os;
}int main() {try {// 创建两个斐波那契堆作为优先级队列FibonacciHeap<Task> pq1, pq2;// 向第一个优先级队列添加任务pq1.insert({3, "完成算法作业"});pq1.insert({1, "紧急会议"});pq1.insert({5, "回复邮件"});// 向第二个优先级队列添加任务pq2.insert({2, "准备演示文稿"});pq2.insert({4, "整理文档"});std::cout << "合并前的优先级队列1大小: " << pq1.size() << std::endl;std::cout << "合并前的优先级队列2大小: " << pq2.size() << std::endl;// 合并两个优先级队列pq1.merge(pq2);std::cout << "合并后的优先级队列大小: " << pq1.size() << std::endl;std::cout << "合并后队列为空? " << (pq1.isEmpty() ? "是" : "否") << std::endl;std::cout << "被合并的队列为空? " << (pq2.isEmpty() ? "是" : "否") << std::endl;// 按优先级处理任务std::cout << "\n按优先级处理任务:" << std::endl;while (!pq1.isEmpty()) {Task highestPriority = pq1.extractMin();std::cout << "处理: " << highestPriority << std::endl;}std::cout << "\n所有任务处理完毕,队列为空? " << (pq1.isEmpty() ? "是" : "否") << std::endl;} catch (const std::exception& e) {std::cerr << "发生错误: " << e.what() << std::endl;return 1;}return 0;
}

19.3 关键字减值和删除一个结点

19.3.1 关键字减值

        关键字减值操作用于减小某个节点的关键字值,并可能需要调整堆的结构以维持最小堆性质。

步骤

  1. 如果新关键字大于当前关键字,抛出异常
  2. 更新节点的关键字值
  3. 如果节点的父节点关键字大于新关键字,切断该节点与父节点的连接
  4. 执行级联切断操作,确保斐波那契堆的性质

19.3.2 切断和级联切断

  • 切断(cut):将一个节点从其父节点的子链表中移除,并将其添加到根链表中
  • 级联切断(cascading cut):如果一个节点的子节点被切断,检查该节点是否也需要被切断,递归执行直到遇到一个未被标记的节点或根节点

19.3.3 删除一个节点

删除一个节点可以通过以下步骤实现:

  1. 将该节点的关键字减值为负无穷大(或最小可能值)
  2. 执行抽取最小元素操作,将该节点从堆中移除

下面是关键字减值和删除操作的 C++ 实现:

#include "fibonacci_heap.h"// 切断节点y与其父节点x之间的连接
template <typename T>
void FibonacciHeap<T>::cut(FibonacciNode<T>* y, FibonacciNode<T>* x) {// 从x的子链表中移除yremoveNode(y);x->degree--;// 如果x的子节点指针指向y,更新子节点指针if (x->child == y) {x->child = y->right;if (x->child == y) {  // 如果y是x唯一的子节点x->child = nullptr;}}// 将y添加到根链表addNode(minNode, y);y->parent = nullptr;y->mark = false;  // 重置标记
}// 级联切断操作
template <typename T>
void FibonacciHeap<T>::cascadingCut(FibonacciNode<T>* y) {FibonacciNode<T>* z = y->parent;if (z != nullptr) {if (!y->mark) {// 如果y未被标记,标记它y->mark = true;} else {// 如果y已被标记,切断y并继续级联切断cut(y, z);cascadingCut(z);}}
}// 减小关键字值
template <typename T>
void FibonacciHeap<T>::decreaseKey(FibonacciNode<T>* x, const T& k) {if (k > x->key) {throw std::invalid_argument("New key is larger than current key");}x->key = k;FibonacciNode<T>* y = x->parent;// 如果x的关键字小于其父节点的关键字,需要调整if (y != nullptr && x->key < y->key) {cut(x, y);cascadingCut(y);}// 更新最小节点if (x->key < minNode->key) {minNode = x;}
}// 删除节点
template <typename T>
void FibonacciHeap<T>::deleteNode(FibonacciNode<T>* x) {// 将x的关键字减小到负无穷大(这里使用一个非常小的值模拟)T minPossible = x->key;// 假设T是数值类型,这里简单地将其设置为一个极小值decreaseKey(x, minPossible - minPossible - minPossible);// 抽取最小元素(即x)extractMin();
}// 显式实例化常用类型,避免链接错误
template class FibonacciHeap<int>;
template class FibonacciHeap<double>;
template class FibonacciHeap<Task>;  // 为前面示例中的Task类型实例化

19.3.4 综合案例:动态优先级调整

        在许多实际应用中,我们需要能够动态调整任务的优先级。以下示例展示了如何使用斐波那契堆的关键字减值操作来实现这一功能:

#include "fibonacci_heap.h"
#include <iostream>
#include <string>
#include <vector>// 任务结构体
struct Task {int priority;std::string description;bool operator<(const Task& other) const {return priority < other.priority;}bool operator>(const Task& other) const {return priority > other.priority;}
};// 输出任务信息
std::ostream& operator<<(std::ostream& os, const Task& task) {os << "[" << task.priority << "] " << task.description;return os;
}int main() {try {FibonacciHeap<Task> taskHeap;std::vector<FibonacciNode<Task>*> taskNodes;// 添加一些初始任务taskNodes.push_back(taskHeap.insert({5, "编写文档"}));taskNodes.push_back(taskHeap.insert({3, "测试代码"}));taskNodes.push_back(taskHeap.insert({7, "修复低优先级bug"}));taskNodes.push_back(taskHeap.insert({2, "准备会议"}));taskNodes.push_back(taskHeap.insert({8, "优化性能"}));std::cout << "初始任务队列:" << std::endl;FibonacciHeap<Task> tempHeap;tempHeap.merge(taskHeap);  // 创建一个副本用于展示while (!tempHeap.isEmpty()) {std::cout << "  " << tempHeap.extractMin() << std::endl;}// 调整一些任务的优先级std::cout << "\n调整任务优先级:" << std::endl;std::cout << "  将\"修复低优先级bug\"的优先级从7调整为1" << std::endl;taskHeap.decreaseKey(taskNodes[2], {1, "修复紧急bug"});std::cout << "  将\"优化性能\"的优先级从8调整为4" << std::endl;taskHeap.decreaseKey(taskNodes[4], {4, "优化性能"});// 删除一个任务std::cout << "  删除任务\"编写文档\"" << std::endl;taskHeap.deleteNode(taskNodes[0]);// 按新的优先级处理任务std::cout << "\n按新优先级处理任务:" << std::endl;while (!taskHeap.isEmpty()) {std::cout << "  处理: " << taskHeap.extractMin() << std::endl;}} catch (const std::exception& e) {std::cerr << "发生错误: " << e.what() << std::endl;return 1;}return 0;
}

19.4 最大度数的界

        斐波那契堆的效率很大程度上依赖于其节点的最大度数有一个较小的上界。这个上界与黄金比例有关。

19.4.1 度数界的证明

        对于一个包含 n 个节点的斐波那契堆,任意节点的度数的上界为 O (log n)。更精确地说,如果 n ≥ 1,则任意节点的度数最多为⌊log_φ n⌋,其中 φ 是黄金比例(1+√5)/2 ≈ 1.618。

        这个界限的证明基于斐波那契数的性质:对于度数为 k 的节点,其包含的后代数量至少为第 k+2 个斐波那契数。

19.4.2 斐波那契数与度数的关系

斐波那契数定义为:

  • F₀ = 0
  • F₁ = 1
  • Fₖ = Fₖ₋₁ + Fₖ₋₂ (对于 k ≥ 2)

度数为 k 的节点至少有 Fₖ₊₂个后代(包括自身)。

        度数为0

                至少F₂=1个节点

        度数为1

                至少F₃=2个节点

        度数为2

                至少F₄=3个节点

        度数为3

                至少F₅=5个节点

        度数为k

                至少Fₖ₊₂个节点

19.4.3 实际意义

        这个度数界限保证了斐波那契堆的 consolidate 操作能在 O (log n) 时间内完成,从而确保了抽取最小元素和删除操作的摊还时间复杂度为 O (log n)

思考题

  1. 比较斐波那契堆与二叉堆、二项堆在各种操作上的时间复杂度,并分析各自的适用场景。

  2. 如何修改斐波那契堆的实现,使其支持最大堆而不是最小堆?

  3. 证明:在一个斐波那契堆中,包含 n 个节点的树的最大度数为 O (log n)。

  4. 实现一个基于斐波那契堆的 Dijkstra 算法,用于求解单源最短路径问题。

  5. 分析斐波那契堆在实际应用中的优缺点,为什么在很多编程语言的标准库中没有采用斐波那契堆?

本章注记

        斐波那契堆由 Fredman 和 Tarjan 于 1984 年提出,是数据结构设计中的一个重要里程碑。它展示了摊还分析技术如何用于设计高效的数据结构。

        尽管斐波那契堆在理论上具有优异的时间复杂度,但在实际应用中,由于其实现的复杂性和较高的常数因子,它并不总是最佳选择。在许多情况下,二项堆或甚至二叉堆可能表现得更好。

        斐波那契堆的思想启发了其他高效数据结构的设计,如配对堆(Pairing Heap),它在实践中往往比斐波那契堆表现更好,同时保持了简单性。

        斐波那契堆在算法设计中有着重要应用,特别是在图算法中,如用于实现高效的最小生成树算法和最短路径算法。

完整代码使用说明

为了使用本文实现的斐波那契堆,您需要:

  1. 将节点结构和类定义保存为 "fibonacci_heap.h"
  2. 将基本操作实现保存为 "fibonacci_heap.cpp"
  3. 将关键字减值和删除操作实现保存为 "fibonacci_heap_decrease_delete.cpp"
  4. 根据需要使用示例代码,如优先级队列或动态优先级调整示例

编译时,确保将所有源文件一起编译。例如,使用 g++:

g++ -std=c++11 fibonacci_heap.cpp fibonacci_heap_decrease_delete.cpp priority_queue_example.cpp -o fib_heap_example

总结

        斐波那契堆是一种理论上非常高效的可合并堆数据结构,它的设计展示了如何通过巧妙的数据结构组织和摊还分析来获得优异的时间复杂度。虽然实现复杂,但它为许多算法问题提供了最优解,特别是那些需要频繁合并操作的场景。

        通过本文的讲解和实现示例,希望读者能够深入理解斐波那契堆的工作原理,并能在实际应用中灵活运用这一强大的数据结构。

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

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

相关文章

MySQL-日志

MySQL-日志前言一、错误日志&#xff08;error log&#xff09;二、慢查询日志(slow query log)三 、一般查询日志(general log)四、 事务日志重做日志&#xff08;redo log&#xff09;回滚日志&#xff08;undo log&#xff09;五、 二进制日志(bin log)/归档日志 > 数据同…

嵌入式C语言编程:策略模式、状态模式和状态机的应用

概述 在没有面向对象语法的C语言中&#xff0c;策略&#xff08;Strategy&#xff09;模式和状态&#xff08;State&#xff09;模式都通过“上下文 接口”组合来模拟多态。 它们在代码结构上几乎一致&#xff0c;但设计意图和应用场景却差异很大。 本文分三部分深入剖析&…

人工智能、机器学习、深度学习:2025技术革命的深度解析

目录 人工智能、机器学习、深度学习&#xff1a;技术革命的深度解析 引言 第一部分&#xff1a;人工智能的起源与演进 1.1 人工智能的定义 1.2 人工智能的历史 1.3 人工智能的关键概念 a.知识表示&#xff08;Knowledge Representation&#xff09; b.搜索算法&#xf…

【Python】常用内置模块

1.os 文件目录 import os# 创建文件夹 os.mkdir(dir) # 判断文件是否存在 os.path.exists(path) # 列出文件夹下文件列表 os.listdir(dir)""" 常用 """ # 当前文件相对路径 os.getcwd()# 当前文件绝对路径 os.path.abspath(__file__)# 当前文…

(Python)爬虫进阶(Python爬虫教程)(CSS选择器)

源代码&#xff1a;#导入库 import requests from bs4 import BeautifulSoup import pandas as pd#爬虫函数 def scrape_books():#1.基本网址连接base_url "http://books.toscrape.com"#2.获取基本网址responserequests.get(base_url)#3.检查是否正常访问if respons…

第七节 自然语言处理与Bert

自然语言处理与BERT模型&#xff1a;从基础到实践入门 自然语言处理&#xff08;NLP&#xff09;的核心目标之一是让计算机理解人类语言的语义和上下文。本文将从基础的字词表示出发&#xff0c;逐步解析传统模型的局限性、Self-attention的突破性思想&#xff0c;以及BERT如何…

攻击者瞄准加密技术的基础:智能合约

虽然利用许多智能合约中的安全漏洞已经成为网络攻击者的长期目标&#xff0c;但越来越多的安全公司开始关注使用欺诈性或混淆的智能合约从加密货币账户中窃取资金的骗局。 根据网络安全公司 SentinelOne 本周发布的分析报告&#xff0c;在最近一次引人注目的攻击中&#xff0c…

基于开源AI大模型、AI智能名片与S2B2C商城小程序的零售智能化升级路径研究

摘要&#xff1a;在零售业数字化转型浪潮中&#xff0c;人工智能技术正从“辅助工具”向“核心生产力”演进。本文聚焦开源AI大模型、AI智能名片与S2B2C商城小程序的协同应用&#xff0c;提出“数据感知-关系重构-生态协同”的三维创新框架。通过分析智能传感、动态画像与供应链…

机器学习 朴素贝叶斯

目录 一.什么是朴素贝叶斯 1.1 从 “概率” 到 “分类” 二.朴素贝叶斯的数学基础&#xff1a;贝叶斯定理 2.1 贝叶斯定理公式 2.2 从贝叶斯定理到朴素贝叶斯分类 2.3 “朴素” 的关键&#xff1a;特征独立性假设 三、朴素贝叶斯的三种常见类型 3.1 高斯朴素贝叶斯&…

A Logical Calculus of the Ideas Immanent in Nervous Activity(神经网络早期的M-P模型)

哈喽&#xff0c;各位朋友大家上午好&#xff01;今天我们要一起啃下这篇神经科学与逻辑学交叉领域的奠基之作——McCulloch和Pitts的《A Logical Calculus of the Ideas Immanent in Nervous Activity》。这篇论文篇幅不长&#xff0c;但每一个定理、每一个假设都像精密齿轮&a…

大语言模型提示工程与应用:提示工程-提升模型准确性与减少偏见的方法

语言模型可靠性优化 学习目标 在本课程中&#xff0c;我们将学习通过提示工程提升模型事实准确性、减少偏见的有效方法。 相关知识点 语言模型可靠性优化 学习内容 1 语言模型可靠性优化 1.1 事实准确性增强 LLM可能生成看似合理但实际虚构的内容。优化策略包括&#x…

遇到前端导出 Excel 文件出现乱码或文件损坏的问题

1. 检查后端返回的数据格式确认接口响应&#xff1a;确保后端返回的是二进制流&#xff08;如 ArrayBuffer&#xff09;或 Base64 编码的 Excel 文件&#xff0c;而非 JSON 字符串。用浏览器开发者工具&#xff08;Network 标签&#xff09;检查接口响应类型&#xff1a;正确的…

2025年Cloudflare WAF防护机制深度剖析:5秒盾绕过完全指南

2025年Cloudflare WAF防护机制深度剖析&#xff1a;5秒盾绕过完全指南 技术概述 Cloudflare作为全球领先的CDN和网络安全服务提供商&#xff0c;其WAF&#xff08;Web Application Firewall&#xff09;防护系统已经成为现代Web安全的标杆。特别是其标志性的"5秒盾"…

【Android调用相册、拍照、录像】等功能的封装

关于调用Android项目 关于Android中调用相机拍照、录像&#xff0c;调用相册选图等是比较繁琐的&#xff0c;为了减少代码冗余&#xff0c;肯定需要封装成工具类&#xff0c;最终使用大概如下&#xff0c;大部分代码使用Java编写&#xff0c;因为需要照顾到不适用kotlin的伸手…

Git 分支管理:从新开发分支迁移为主分支的完整指南

问题背景 我在使用 Git 进行开发时&#xff0c;由于原有的主分支遭到了污染&#xff0c;不得已在多方尝试之后&#xff0c;决定替换原有的主分支。创建一个新分支并完成了重要修改&#xff1a; 基于提交 0fcb6df0f5e8caa3d853bb1f43f23cfe6d269b18 创建了 new-development 分支…

nginx常见问题(四):端口无权限

当 Nginx 日志报错 bind() to 80 failed (13: Permission denied) 时&#xff0c;这通常是由于权限不足导致 Nginx 无法绑定到 80 端口&#xff08;该端口为系统特权端口&#xff09;。以下是详细的问题分析与解决方案&#xff1a;一、问题原因分析80 端口属于 系统特权端口&am…

【线性代数】线性方程组与矩阵——(3)线性方程组解的结构

上一节&#xff1a;【线性代数】线性方程组与矩阵——&#xff08;2&#xff09;矩阵与线性方程组的解 总目录&#xff1a;【线性代数】目录 文章目录9. 向量组的线性相关性与线性方程组解的结构9.1. 向量组及其线性组合9.2. 向量组的线性相关性9.3. 向量组的秩9.4. 线性方程组…

机器学习-----K-means算法介绍

一、为什么需要 K-Means&#xff1f;在监督学习中&#xff0c;我们总把数据写成 (x, y)&#xff0c;让模型学习 x → y 的映射。 但现实中很多数据根本没有标签 y&#xff0c;例如&#xff1a;啤酒&#xff1a;热量、钠含量、酒精度、价格用户&#xff1a;访问时长、点击次数、…

Spring Security自动处理/login请求,后端控制层没有 @PostMapping(“/login“) 这样的 Controller 方法

一&#xff1a;前言 &#xff08;1&#xff09;Spring Security概念&#xff1a; Spring Security 是属于 Spring 生态下一个功能强大且高度可定制的认证和授权框架&#xff0c;它不仅限于 Web 应用程序的安全性&#xff0c;也可以用于保护任何类型的应用程序。 &#xff08…

idea开发工具中git如何忽略编译文件build、gradle的文件?

idea开发工具中&#xff1a; git显示下面这个文件有变更&#xff1a; ~/Documents/wwwroot-dev/wlxl-backend/java/hyh-apis/hyh-apis-springboot/build/resources/main/mapping/AccountRealnameMapper.xml 我git的根路径是&#xff1a; ~/Documents/wwwroot-dev/wlxl-backend/…