AI Agent:基于大模型的自主智能体

什么是AI Agent AI Agent(人工智能代理)是一种能够感知环境、进行决策和执行动作的智能实体。不同于传统 的人工智能,AI Agent 具备通过独立思考、调用工具去逐步完成给定目标的能力。比如,告诉 AI Agent 帮忙下单一份外卖,它就可以直接调用 APP 选择外卖,再调用支付程序下单支付,无需人 类去指定每一步的操作。Agent 的概念由 Minsky 在其 1986 年出版的《思维的社会》一书中提出, Minsky 认为社会中的某些个体经过协商之后可求得问题的解,这些个体就是 Agent。他还认为 Agent 应具有社会交互性和智能性。Agent 的概念由此被引入人工智能和计算机领域,并迅速成为 研究热点。但苦于数据和算力限制,想要实现真正智能的 AI Agents 缺乏必要的现实条件。 Hyperwrite 研发的 AI Agent 个人助理插件实现自动预订航班机票 大语言模型和 AI Agent 的区别在于 AI Agent 可以独立思考并做出行动,和 RPA 的区别在于它能够处理未知环境信息。ChatGPT 诞生后,AI 从真正意义上具备了和人类进行多轮对话的能力,并且能针对相应问题给出具体回答与建议。随后各个领域的“Copilot”推出,如 Microsoft 365 Copilot、GitHub Copilot、Adobe Firefly 等,让 AI 成为了办公、代码、设计等场景的“智能副驾驶”。AI Agent 和大模型的区别在于,大模型与人类之间的交互是基于 prompt 实现的,用户 prompt 是否清晰明确会影响大模型回答的效果,例如ChatGPT 和这些 Copilot 都需要明确任务才能得到有用的回答。而 AI Agent 的工作仅需给定一个目标,它就能够针对目标独立思考并做出行动,它会根据给定任务详细拆解出每一步的计划步骤,依靠来自外界的反馈和自主思考,自己给自己创建 prompt,来实现目标。如果说 Copilot 是“副驾驶”,那么 Agent 则可以算得上一个初级的“主驾驶”。和传统的 RPA 相比,RPA 只能在给定的情况条件下,根据程序内预设好的流程来进行工作的处理,在出现大量未知信息、难以预测的环境中时,RPA 是无法进行工作的,AI Agent 则可以通过和环境进行交互,感知信息并做出对应的思考和行动。

【MySQL基础篇】多表查询

1、多表关系 概述:项目开发中,在进行数据库表结构操作设计时,会根据业务需求及业务模板之间的关系,分析并设计表结构,由于业务之间相互关联,所以各个表结构之间也存在着各种联系,基本上分为三种: 一对多(多对一) 多对多 一对一 · 一对多(多对一) 案例:部门与员工的关系(一个部门对应多个员工,一个员工对应一个部门) 实现:在多的一方建立外键,指向一的一方的主键 · 多对多 案例:学生与课程的关系(一个学生可以选修多门课程,一门课程可以共多个学生选择) 实现:建立第三张中间表,中间表至少包含两个外键,分别关联两方主键 create table student( id int primary key auto_increment comment '主键ID', name varchar(10) comment '姓名', no varchar(10) comment '学号' )comment '学生表'; insert into student values(null,'黛丽丝','2000100101'),(null,'谢逊','2000100102'),(null,'殷天正','2000100103'),(null,'韦一笑','2000100104'); create table course( id int primary key auto_increment comment '主键ID', name varchar(10) comment '课程名称' )comment '课程表'; insert into course values(null,'java'),(null,'php'),(null,'mysql'),(null,'hadoop'); create table student_course( id int primary key auto_increment comment '主键ID', studentid int not null comment '学生ID', courseid int not null comment '课程ID', constraint fk_courseid foreign key (courseid) references course (id), constraint fk_studentid foreign key (studentid) references student (id) )comment '课程中间表'; insert into student_course values(null,1,1),(null,1,2),(null,1,3),(null,2,2),(null,2,3),(null,3,4); · 一对一

如何在Vue3中使用Ref访问DOM元素

在 Vue 3 中,使用 ref 访问DOM元素是一个常见的需求,尤其当你需要操作原生 DOM 元素的时候。对熟悉 Vue 2 的开发者来说,这个概念并不陌生,但 Vue 3 在此基础上进行了改进和加强。本文将详细讲解如何在 Vue 3 中使用 ref 访问 DOM 元素,并通过具体的示例代码来说明其用法。 什么是 ref 在 Vue 3 中,ref 是一个函数,用来创建一个可响应的引用对象。这个对象可以在模板中绑定到 DOM 元素,从而在组件的逻辑代码中方便地访问和操作这些元素。 使用 ref 访问 DOM 元素的步骤 1. 引入 ref 函数 首先,你需要从 vue 包中引入 ref 函数。这个函数可以帮助你创建一个引用对象,用来绑定到 DOM 元素。 import { ref } from 'vue'; 2. 创建一个 ref 对象 然后,在你的 setup 函数中创建一个 ref 对象。这个对象是一个可响应的引用,初始值通常为 null,因为在模板渲染之前,DOM 元素还不存在。 const myElement = ref(null); 3. 绑定 ref 对象到模板中的 DOM 元素 接下来,你需要在模板中使用 ref 特性,把刚才创建的 ref 对象绑定到某个 DOM 元素上。当这个组件实例化并渲染完成后,ref 对象将自动更新,从而引用这个 DOM 元素。

深入分析 Android BroadcastReceiver (十)(完)

文章目录 深入分析 Android BroadcastReceiver (十)1. 深入理解 Android 广播机制的高级应用与实践1.1 高级应用1.1.1 示例:广播启动服务1.1.2 示例:数据变化通知1.1.3 示例:下载完成通知 1.2 实践建议1.2.1 设置权限1.2.2 动态注册和注销广播接收器1.2.3 示例:使用 LocalBroadcastManager1.2.4 示例:合并事件 2. 总结 深入分析 Android BroadcastReceiver (十) 1. 深入理解 Android 广播机制的高级应用与实践 在前文中,我们深入探讨了 Android 广播机制的基本实现、扩展应用和高级优化。接下来,我们将进一步探讨广播机制的更多高级应用和实际开发中的一些实践建议。 1.1 高级应用 广播与服务的结合 在一些复杂应用场景中,广播和服务的结合使用可以实现更加灵活和强大的功能。例如,通过广播通知启动服务,或在服务中发送广播通知应用状态变化。 1.1.1 示例:广播启动服务 发送广播启动服务: Intent intent = new Intent("com.example.START_SERVICE_ACTION"); context.sendBroadcast(intent); 注册接收器并启动服务: public class StartServiceReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { if ("com.example.START_SERVICE_ACTION".equals(intent.getAction())) { Intent serviceIntent = new Intent(context, MyService.class); context.

深入分析 Android BroadcastReceiver (九)

文章目录 深入分析 Android BroadcastReceiver (九)1. Android 广播机制的扩展应用与高级优化1.1 广播机制的扩展应用1.1.1 示例:有序广播1.1.2 示例:粘性广播1.1.3 示例:局部广播 1.2 广播机制的高级优化1.2.1 示例:使用 PendingIntent 发送延迟广播1.2.2 示例:设置接收器优先级 2. 广播机制设计的改进建议3. 总结 深入分析 Android BroadcastReceiver (九) 1. Android 广播机制的扩展应用与高级优化 在前面我们详细介绍了系统广播和自定义广播的实现及其设计原理。接下来,我们将进一步探讨广播机制的扩展应用,以及一些高级优化策略和实践。 1.1 广播机制的扩展应用 有序广播(Ordered Broadcast) 有序广播允许多个接收器按优先级顺序依次处理广播,每个接收器可以选择中止广播的传播。有序广播在某些需要处理顺序的场景中非常有用,例如:安全检查、权限验证等。 1.1.1 示例:有序广播 发送有序广播: Intent intent = new Intent("com.example.ORDERED_ACTION"); context.sendOrderedBroadcast(intent, null); 注册有序广播接收器: IntentFilter filter = new IntentFilter("com.example.ORDERED_ACTION"); filter.setPriority(10); // 设置优先级 context.registerReceiver(new OrderedReceiver(), filter); 有序广播接收器处理: public class OrderedReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { // 处理广播 if (someCondition) { // 中止广播传播 abortBroadcast(); } } } 粘性广播(Sticky Broadcast) 粘性广播是指广播消息在发送后会一直存在,直到被新的消息替换,接收器在注册时如果有未处理的粘性广播会立即收到。粘性广播主要用于一些长期存在的状态通知。

深入分析 Android BroadcastReceiver (八)

文章目录 深入分析 Android BroadcastReceiver (八)1. 系统与自定义实现1.1 系统广播机制1.1.1 系统广播的实现原理1.1.2 系统广播的源码分析 1.2 自定义广播机制1.2.1 自定义广播的实现步骤1.2.2 自定义广播的源码分析 2. 广播机制设计的初衷与优势2.1 设计初衷2.2 优势 3. 总结 深入分析 Android BroadcastReceiver (八) 1. 系统与自定义实现 为了更全面地理解 Android 的广播机制,深入分析其底层实现原理和设计逻辑是非常重要的。这部分内容将探讨广播机制的系统实现以及自定义广播的内部工作机制。 1.1 系统广播机制 系统广播是 Android 操作系统中用于通知应用程序系统事件的重要机制。系统广播通常用于通知系统级别的事件,如网络变化、电量低、屏幕解锁等。 1.1.1 系统广播的实现原理 系统广播的实现主要涉及到 BroadcastReceiver、Intent、Context 和 ActivityManagerService (AMS) 等关键组件。以下是系统广播发送和接收的流程: 广播发送 应用或系统通过 Context.sendBroadcast() 方法发送广播。 Intent intent = new Intent("com.example.SOME_ACTION"); context.sendBroadcast(intent); 广播注册 应用通过 Context.registerReceiver() 方法注册广播接收器。 IntentFilter filter = new IntentFilter("com.example.SOME_ACTION"); context.registerReceiver(new MyReceiver(), filter); AMS 处理广播 广播发送后,ActivityManagerService (AMS) 负责广播的分发。AMS 会查找所有注册了相应广播的接收器,并将广播消息分发给这些接收器。

深入分析 Android BroadcastReceiver (六)

文章目录 深入分析 Android BroadcastReceiver (六)1. 广播机制的高级优化策略1.1 使用 Sticky Broadcast(粘性广播)示例:粘性广播(过时,不推荐) 1.2 使用 LiveData 和 ViewModel 进行组件通信示例:使用 LiveData 进行组件通信 1.3 使用 EventBus 进行事件总线模式通信示例:使用 EventBus 进行事件总线模式通信 2. 总结与高级优化策略 深入分析 Android BroadcastReceiver (六) 1. 广播机制的高级优化策略 在广播机制的实际应用中,还有一些高级优化策略和注意事项,可以进一步提升应用的性能和可靠性。 1.1 使用 Sticky Broadcast(粘性广播) 粘性广播(Sticky Broadcast)是一种特殊的广播,系统会保存最近一次的广播数据,即使在广播发送后再注册接收器,接收器也能接收到最近的广播消息。不过,粘性广播在 Android API 21 后被标记为过时(deprecated),因此不建议在新的开发中使用粘性广播,建议使用其他机制替代。 示例:粘性广播(过时,不推荐) Intent intent = new Intent("com.example.STICKY_ACTION"); intent.putExtra("data", "Sticky data"); sendStickyBroadcast(intent); // 在接收器中接收粘性广播 public class StickyReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { String data = intent.

Java---包装类与泛型

1.包装类 1.1 包装类 在Java中,由于基本数据类型不是继承Object类,为了在泛型代码中可以支持基本数据类型,Java给每个基本数据类型各自提供了·一个包装类。 如下图 除了char和int基本数据类型的包装类型有点特别,其他的都是首字母大写 1.2 装箱与拆箱 1. 装箱 装箱就是将基本数据类型的数据转化成包装类,装箱分为自动装箱和显示拆箱。 public static void main(String[] args) { int a=10; Integer b=Integer.valueOf(a);//显示拆箱 Integer c=a;//自动拆箱 } 其实自动拆箱和显示拆箱的底层原理是一样的,都是调用了Integer.valueOf()方法。 2. 拆箱 拆箱就是将包装类的数据类型转换转换成基本数据类型,拆箱也分为自动拆箱和显示拆箱。 public static void main1(String[] args) { Integer a=10; int b=a.intValue();//显示拆箱 int c=a;//自动拆箱 } 2.面试题 了解装箱与拆箱,我们来看一道面试题 public static void main(String[] args) { Integer a=100; Integer b=100; System.out.println(a == b);//打印true Integer c=200; Integer d=200; System.out.println(c == d);//打印false } 为什么会打印不同的结果呢? 我们来看Integer.ValueOf()方法的原码 我们发现,在进行装包操作的时候,会根据装包的数据的大小来返回不同类型的数据。 当要装包的数据的范围在 [-127~128] 之间时,valueOf 方法就会返回数组中的一个数据(整数)。

【Java】搜索引擎设计:信息搜索怎么避免大海捞针?

一、内容分析 我们准备开发一个针对全网内容的搜索引擎,产品名称为“Bingoo”。 Bingoo的主要技术挑战包括: 针对爬虫获取的海量数据,如何高效地进行数据管理;当用户输入搜索词的时候,如何快速查找包含搜索词的网页内容;如何对搜索结果的网页内容进行排序,使排在搜索结果列表前面的网页,正好是用户期望看到的内容。 12.1 概要设计 一个完整的搜索引擎包括分布式爬虫、索引构造器、网页排名算法、搜索器等组成部分,Bingoo的系统架构如下。 分布式爬虫通过存储服务器将爬取的网页存储到分布式文件集群HDFS,为了提高存储效率,网页将被压缩后存储。存储的时候,网页一个文件挨着一个文件地连续存储,存储格式如下。 每个网页被分配得到一个8字节长整型docID,docID之后用2个字节记录网页的URL的长度,之后4个字节记录压缩后网页内容数据的长度,所有存储的网页的头14个字节都是同样的格式。之后存储URL字符串和压缩后的网页内容数据。读取文件的时候,先读14个字节的头信息,根据头信息中记录的URL长度和数据长度,再读取对应长度的URL和网页内容数据。 搜索引擎能够快速查找的核心就是利用索引,根据用户的查询内容查找匹配的索引,根据索引列表构建结果页面。索引的构造主要通过索引构造器完成,索引构造器读取HDFS中的网页内容,解压缩后提取网页中的单词,构建一个“docID->单词列表”的正排索引。然后,索引构造器再根据这个正排索引构建一个“单词->docID列表”的倒排索引,“docID列表”就是包含了这个单词的所有网页列表。利用这个倒排索引,搜索器可以快速获得用户搜索词对应的所有网页。 网页中所有的单词构成了一个词典,实际上,词典就是一个Hash表,key就是单词,value就是倒排索引的网页列表。虽然互联网页的内容非常庞大,但是使用到的单词其实是非常有限的。根据Google的报告,256M内存可以存放1400万个单词,这差不多就是英文单词的全部了。 在构建索引的过程中,因为要不断修改索引列表,还要进行排序,所以,有很多操作是需要进行加锁同步完成的。对于海量的互联网页的计算,这样的索引构建速度太慢了。因此我们设计了64个索引桶,根据docID取模,将不同网页分配到不同的桶中,在每个桶中分别进行索引构建,通过并行计算来加快索引处理速度。 索引构造器在读取网页内容、构造索引的时候,还会调用URL提取器,将网页中包含的URL提取出来,构建一个链接关系表。链接关系表的格式是“docID->docID”,前一个docID是当前网页的docID,后一个docID是当前网页中包含的URL对应的docID。一个网页中会包含很多个URL,也就是会构建出很多个这样的链接关系。后面会利用这个链接关系表,使用PageRank排名算法对所有网页进行打分排名,当索引器得到查找的网页列表时,利用PageRank值进行排名,最终呈现给用户,保证用户最先看到的网页是最接近用户期望的结果页面。 12.2 详细设计 一个运行良好的搜索引擎的核心技术就是索引和排名,所以我们将分别说明这两种技术要点。 12.2.1 索引 索引构造器从HDFS读取网页内容后,解析每个页面,提取网页里的每个单词。如果是英文,那么每个单词都用空格分隔,比较容易;如果是中文,需要使用中文分词器才能提取到每个单词,比如“高并发架构”,使用中文分词器得到的就是“高并发”、“架构”两个词。 首先,索引构造器将所有的网页都读取完,构建出所有的“docID->单词列表”正排索引。 然后遍历所有的正排索引,再按照“单词→docID列表”的方式组织起来,就是倒排索引了。 我们这个例子中只有两个单词、7个网页。事实上,Bingoo数以千亿的网页就是这样通过倒排索引组织起来的,网页数量虽然庞大,但是单词数却是比较有限的。所以,整个倒排索引的大小相比于网页数量要小得多。Bingoo将每个单词对应的网页列表存储在硬盘中,而单词则存储在内存的Hash表,也就是词典中,词典示例: 对于部分热门的单词,整个网页列表也可以存储在内存中,相当于缓存。在词典中,每个单词记录下硬盘或者内存中的网页列表地址,这样只要搜索单词,就可以快速得到对应的网页地址列表。Bingoo根据列表中的网页编号docID,展示对应的网页信息摘要,就完成了海量数据的快速检索。 如果用户的搜索词正好是一个单词,比如“高并发”,那么直接查找词典,得到网页列表就完成查找了。但是如果用户输入的是一个句话,那么搜索器就需要将这句话拆分成几个单词,然后分别查找倒排索引。这样的话,得到的就是几个网页列表,还需要对这几个网页列表求交集,才能得到最终的结果列表。 比如,用户输入“高并发架构”进行搜索,那么搜索器就会拆分成两个词:“高并发”、“架构”,得到两个倒排索引: 高并发->2,3,5,7 架构->1,2,4 需要对这两个倒排索引求交集,也就是同时包含“高并发”和“架构”的网页才是符合搜索要求的结果,最终的交集结果应该是只有一篇网页,即docID为2的满足要求。 列表求交集最简单的实现就是双层for循环,但是这种算法的时间复杂度是O(n^2),我们的网页列表长度(n)可能有千万级甚至更高,这样的计算效率太低。 一个改进的算法是拉链法,我们将网页列表先按照docID的编号进行排序,得到的就是这样两个有序链表: 同时遍历两个链表,如果其中一个链表当前指向的元素小于另一个链表当前指向的元素,那么这个链表就继续向前遍历;如果两个链表当前指向的元素相同,该元素就是交集元素,记录在结果列表中;依此继续向前遍历,直到其中一个链表指向自己的尾部nil。 拉链法的时间复杂度是O(2n),远优于双层循环。但是对于千万级的数据而言,还是太慢。我们还可以采用数据分片的方式进行并行计算,以实现性能优化。 比如,我们的docID分布在[0, 1万亿)区间,而每个倒排索引链表平均包含1千万个docID。我们把所有的docID按照1千亿进行数据分片,就会得到10个区间[0, 1千亿)[1千亿,2千亿)……[9千亿,1万亿)。每个倒排索引链表大致均匀分布在这10个区间,我们就可以依照这10个区间范围,将每个要遍历的链表切分为10片,每片大约包含1百万个docID。两个链表只在自己对应的分片内求交集即可,因此我们可以启动10个线程对10个分片进行并行计算,速度可提高10倍。 事实上,两个1千万长度的链表求交集,最终的结果可能不过几万,也就是说,大部分的比较都是不相等的。比如下面的例子。 第一个链表遍历到自己的最后一个元素,才和第二个链表的第一个元素相同。那么第一个链表能不能跳过前面那些元素呢?很自然,我们想到可以用跳表来实现,如下图。 跳表实际上是在链表上构建多级索引,在索引上遍历可以跳过底层的部分数据,我们可以利用这个特性实现链表的跳跃式比较,加快计算速度。使用跳表的交集计算时间复杂度大约是O(log(n))。 此外,虽然搜索引擎利用倒排索引已经能很快得到搜索结果了,但搜索引擎应用还会使用缓存对搜索进行加速,将整个搜索词对应的搜索结果直接放入缓存,以减少倒排索引的访问压力,以及不必要的集合计算。 12.2.2 PageRank排名算法 Bingoo使用PageRank算法进行网页结果排名,以保证搜索结果更符合用户期待。 PageRank算法会根据网页的链接关系给网页打分。如果一个网页A包含另一个网页B的超链接,那么就认为A网页给B网页投了一票。一个网页得到的投票越多,说明自己越重要;越重要的网页给自己投票,自己也越重要。 PageRank算法就是计算每个网页的PageRank值,最终的搜索结果也是以网页的PageRank值排序,展示给用户。事实证明,这种排名方法非常有效,PageRank值更高的网页,确实更满足用户的搜索期望。 以下面四个网页A、B、C、D举例,带箭头的线条表示链接。 B网页包含了A、D两个页面的超链接,相当于B网页给A、D每个页面投了一票,如果初始的时候,所有页面都是1分,那么经过这次投票后,B给了A和D每个页面1/2分(B包含了A、D两个超链接,所以每个投票值1/2分),自己从C页面得到1/3分(C包含了A、B、D三个页面的超链接,每个投票值1/3分)。 而A页面则从B、C、D分别得到1/2,1/3,1分。用公式表示就是 \(\\small PR(A) = \\frac{PR(B)}{2}+\\frac{PR(C)}{3}+\\frac{PR(D)}{1}\) 等号左边是经过一次投票后,A页面的PageRank分值;等号右边每一项的分子是包含A页面超链接的页面的PageRank分值,分母是该页面包含的超链接数目。 这样经过一次计算后,每个页面的PageRank分值就会重新分配,重复同样的算法过程,经过几次计算后,根据每个页面PageRank分值进行排序,就得到一个页面重要程度的排名表。根据这个排名表,将用户搜索出来的网页结果排序,排在前面的通常也正是用户期待的结果。 但是这个算法还有个问题,如果某个页面只包含指向自己的超链接,其他页面不断给它送分,而自己一分不出,随着计算执行次数越多,它的分值也就越高,这显然是不合理的。这种情况就像下图所示的,A页面只包含指向自己的超链接。 解决方案是,设想浏览一个页面的时候,有一定概率不是点击超链接,而是在地址栏输入一个URL访问其他页面,表示在公式上,就是 \(\\small PR(A) = \\alpha(\\frac{PR(B)}{2}+\\frac{PR(C)}{3}+\\frac{PR(D)}{1})+\\frac{(1-\\alpha)}{4}\) 上面\(\\small (1-\\alpha)\)就是跳转到其他任何页面的概率,通常取经验值0.15(即\(\\small \\alpha\) 为0.85),因为有一定概率输入的URL是自己的,所以加上上面公式最后一项,其中分母4表示所有网页的总数。 那么对于N个网页,任何一个页面\(\\small P_{i}\)的PageRank计算公式如下: \(\\small PageRank(P_{i})=\\alpha \\sum_{P_{j}\\in M(P_{i})}^{}{\\frac{PageRank(P_{j})}{L(P_{j})}} + \\frac{1-\\alpha}{N}\)

数据结构——二叉树之c语言实现堆与堆排序

目录 前言: 1.二叉树的概念及结构 1.1 特殊的二叉树 1.2 二叉树的存储结构 1.顺序存储 2.链式存储 2. 二叉树的顺序结构及实现 2.1 堆的概念 ​编辑 2.2 堆的创建 3.堆的实现 3.1 堆的初始化和销毁 初始化: 销毁: 插入: 向上调整: 删除: 向下调整: 堆顶元素: 判空: 4.堆排序 4.1排序实现 前言: 在上一期我们介绍了有关于树的基础概念,了解了关于树的各名称的含义,然而在现实中树被用得最多的场景还是在我们计算机中的资源管理器的文件存储结构中,在其他场景被使用的情况很少,所以我们这一期要介绍一种被广泛使用的树型结构——二叉树。 1.二叉树的概念及结构 顾名思义,二叉树是由一个根结点和两棵子树构成,二叉树的每个结点最多只有两个结点: 从上图可以看出: 1. 二叉树不存在度大于2的结点 2. 二叉树的子树有左右之分,次序不能颠倒,因此二叉树是有序树 二叉树是由以下几种情况复合而成的: 现实中的二叉树: 1.1 特殊的二叉树 1. 满二叉树:一个二叉树,如果每一个层的结点数都达到最大值,则这个二叉树就是满二叉树。也就是说,如果一个二叉树的层数为K,且结点总数K次方-1,则它就是满二叉树。 2. 完全二叉树:完全二叉树是效率很高的数据结构,完全二叉树是由满二叉树而引出来的。对于深度为K 的,有n个结点的二叉树,当且仅当其每一个结点都与深度为K的满二叉树中编号从1至n的结点一一对应时称之为完全二叉树。 要注意的是满二叉树是一种特殊的完全二叉树。 1.2 二叉树的存储结构 二叉树一般可以使用两种结构存储,一种顺序结构,一种链式结构。 1.顺序存储 顺序结构存储就是使用数组来存储,一般使用数组只适合表示完全二叉树,因为不是完全二叉树会有空间的浪费。而现实中使用中只有堆才会使用数组来存储,关于堆我们后面的章节会专门讲解。二叉树顺 序存储在物理上是一个数组,在逻辑上是一颗二叉树。 2.链式存储 二叉树的链式存储结构是指,用链表来表示一棵二叉树,即用链来指示元素的逻辑关系。 通常的方法是 链表中每个结点由三个域组成,数据域和左右指针域,左右指针分别用来给出该结点左孩子和右孩子所 在的链结点的存储地址 。链式结构又分为二叉链和三叉链,当前我们学习中一般都是二叉链,后面课程 学到高阶数据结构如红黑树等会用到三叉链。 2. 二叉树的顺序结构及实现 普通的二叉树是不适合用数组来存储的,因为可能会存在大量的空间浪费。而完全二叉树更适合使用顺序结 构存储。现实中我们通常把堆(一种二叉树)使用顺序结构的数组来存储,需要注意的是这里的堆和操作系统虚拟进程地址空间中的堆是两回事,一个是数据结构,一个是操作系统中管理内存的一块区域分段。 2.1 堆的概念 堆的性质: 1.堆中某个结点的值总是不大于或不小于其父结点的值。

【Spring Boot】Spring AOP动态代理,以及静态代理

目录 Spring AOP代理一. 代理的概念二. 静态代理三. JDK代理3.1 重写 invoke 方法进⾏功能增强3.2 通过Proxy类随机生成代理对象 四. CGLIB代理4.1 自定义类来重写intercept方法4.2 通过Enhancer类的create方法来创建代理类 五. AOP源码剖析 总结(重中之重,精华) Spring AOP代理 一. 代理的概念 根据前面的学习想必大家都已经对Spring AOP有所了解了,接下来我们先来回忆一下什么是Spring AOP? AOP:一种对于集中的事情进行统一处理解决的思想; Spring AOP:Spring通过运用AOP统一解决的思想所诞生的产物; 例如:拦截器,适配器,统一结果返回,统一异常处理,以及统一通知处理,以上这些在我们前面的文章中都讲述过,已经有些遗忘的小伙伴可以翻看前面的文章进行稳固一下… 好!接下来我们进入正题… AOP的底层原理实现的代理模式,那么什么是代理模式呢? 通过举一个栗子~大家就应该能够了解了:有些 小伙伴可能通过一些线上平台租过房子,那么这个线上平台就是我们说的中介,是在我们跟房子房东之间的纽带,为啥我们要找中介呢? 中介能够帮我们提前去验收要出租房子的质量来进行出租价格的评定中介能够在我们住房期间能够对房子的进行一个改造升级 通过上面的栗子中的 中介就是代理,我们通过中介能够达到我们最终的要求,这就是代理模式,简单来说就是通过一个代理类能够间接的调用目标方法。 然而代理模式又分为两种: 静态代理动态代理(两种) 静态代理和动态代理的主要区别就是:静态代理的代理对象的一开始就定好的,而动态代理就跟他的命名一样,是动态化的,是由系统随机调度生成的一个代理对象 按照上面的例子来说就是,静态代理A的房子,那么A房子的中介人一直是这个人,而动态代理是中介公司看现在哪一个中介在摸鱼,就让哪一个中介去干活~ 当然了,在面试中主要考查的是动态代理; 动态代理(主要是通过反射来完成的代理模式): JDK代理CGLIB代理 在接下来的讲解中我们将围绕以上几种代理进行展开,由于JDK代理和CGLIB代理是面试中的重中之重,篇幅较长,我们后面慢慢讲述,先就简单的,软的柿子——静态代理来捏~~ 二. 静态代理 静态代理:由程序员创建代理类或特定⼯具⾃动⽣成源代码再对其编译,在程序运⾏前代理类的 .class⽂件就已经存在了 什么意思呢?简单来理解就是它的代理对象已经定死了,不会在修改了。 代理(中介,帮房东出租房⼦) public class HouseProxy implements HouseSubject{ //将被代理对象声明为成员变量 private HouseSubject houseSubject; public HouseProxy(HouseSubject houseSubject) { this.houseSubject = houseSubject; } @Override public void rentHouse() { //开始代理 System.

Git秘籍大公开:从基础概念到高级技巧的全面解析

文章目录 前言一、Git基础介绍1. 作用2. 为什么要进行源代码管理?3. Git的诞生4. Git管理源代码特点5. Git操作流程图解 二、工作区暂存区和仓库区介绍1. 工作区2. 暂存区3. 仓库区 三、Git单人本地仓库操作1. 安装git2. 查看git安装结果3. 创建项目4. 创建本地仓库5. 配置个人信息6. 新建py文件7. 查看文件状态8.将工作区文件添加到暂存区9. 将暂存区文件提交到仓库区10. 接下来就可以在`testa.py`文件中编辑代码11.查看历史版本12. 回退版本方案一:使用HEAD进行回退方案二:当版本非常多时可选择的方案 13. 撤销修改 四、Git远程仓库Gitee1. 创建远程仓库2. 配置SSH3. 克隆项目经理的工作张三的工作 4. 多人协同开发5. 代码冲突代码冲突演练补充: 6. 标签7. 分支 前言 在软件开发的征途中,Git如同导航明灯,以其分布式、高效的特性引领着团队前行。本篇博客将带您走进Git的世界,从诞生背景到核心操作流程,一一揭秘。我们将深入讲解工作区、暂存区、仓库区的概念,并详述Git单人本地仓库的操作步骤,包括创建、配置、提交、版本管理等。此外,还将展示Git远程仓库(如Github、Gitee)的协作魅力,通过实例演示项目克隆、多人协作、冲突解决及分支管理等高级技巧。 一、Git基础介绍 Git 是目前世界上最先进的分布式版本控制系统(没有之一) 1. 作用 源代码管理 Git是一个开源的分布式版本控制系统,可以有效、高速地处理从很小到非常大的项目版本管理。 也是Linus Torvalds为了帮助管理Linux内核开发而开发的一个开放源码的版本控制软件。 2. 为什么要进行源代码管理? 方便多人协同开发方便版本控制 3. Git的诞生 作者是 Linux 之父:Linus Benedict Torvalds当初开发 Git 仅仅是为了辅助 Linux 内核的开发(管理源代码) git 开发时间表: 2005 年 4 月3 日开始开发 git2005 年 4 月 6 日项目发布2005 年 4 月 7 日 Git 开始作为自身的版本控制工具2005 年 4 月 18 日发生第一个多分支合并2005 年 4 月 29 日 Git 的性能达到 Linux 预期2005年 7 月 26 日 Linux 功成身退,将 Git 维护权交给 Git 另一个主要贡献者 Junio C Hamano,直到现在 Git 迅速成为最流行的分布式版本控制系统,尤其是 2008 年,GitHub 网站上线了,它为开源项目免费提供 Git 存储,无数开源项目开始迁移至 GitHub,包括 jQuery,PHP,Ruby 等等。

【Go】常见的变量与常量

变量 常见的变量声明方式 一、声明单个变量的多种方式 1.声明一个变量初始化一个值 //声明变量 默认值是0, var a int //初始化一个值 a = 1 fmt.Println(a) 2. 在初始化的时候省去数据类型,通过值自动匹配当前的变量的数据类型 var b = 2 fmt.Println("初始化值:", b) 3.省去var关键字,直接自动匹配,(不能用于全局变量,只能用于函数体内) c := 3 fmt.Println("初始化值:", c) 如下,:= 不能声明全局变量 二、声明多个变量的方式 //声明多个变量 var h, i int = 10, 11 fmt.Printf("h=%d,i=%d\n", h, i) //多行的变量声明 var ( vv int = 100 jj bool = true ) fmt.Println("vv=", vv, "jj=", jj) 总的代码演示如下: package main import "fmt" var d int = 4 var e = 5 func main() { //变量声明方法 声明变量 默认值是0, var a int //初始化一个值 a = 1 fmt.

【C++初阶】List的模拟实现

1 List 的介绍 List 是带头双向循环链表,不支持 [] 的随机访问和没有扩容相关的函数。 看一下下面的模拟实现就懂了吧,懒 List 迭代器的失效 在list中进行插入时是不会导致list的迭代器失效的,只有在删除时才会失效,并且失效的只是指向被删除节点的迭代器,其他迭代器不会受到影响。 所以在进行删除操作后,要将迭代器往后迭代。 2. List 的模拟实现 未出现的函数都会在后面出现 2.1 List 的结点类 template<class T> struct ListNode { ListNode<T>* _next; ListNode<T>* _prev; T _data; ListNode(const T& x = T()) //缺省值不能直接给0,因为不知道T的类型,可能是string :_next(nullptr) , _prev(nullptr) , _data(x) {} }; 在构造函数中使用默认参数 val = T() 是为了提供一种灵活的方式来初始化节点: 默认构造函数:==T() 表示调用 T 类型的默认构造函数。如果没有提供参数 val,那么 _val 将被默认构造一个 T 类型的对象来初始化。==例如,如果 T 是 int 类型,T() 会生成一个值为 0 的整数;如果 T 是一个自定义的类,那么 T() 会调用这个类的默认构造函数。提供参数:如果提供了参数 val,那么节点的 _val 将被初始化为 val 的值。例如,ListNode<int> node(42); 将创建一个存储值 42 的节点。 2.

Springboot实战:AI大模型+亮数据代理助力短视频时代

目录 前言1.如何入门亮数据1.1、注册登录1.2、注册账号1.3、登录1.4、购买静态住宅代理1.5、展示购买的代理 2. 使用Springboot、AI大模型构建系统2.1 使用Springboot、AI大模型构建爬虫2.2、在Springboot项目添加工具 3、编写代码,爬取视频素材3.1、代码里使用代理3.2、核心业务代码3.2、运行代码并得到相关的视频素材3.2.1、运行效果3.2.2、目标站点脚本运行中 3.3、爬取的素材 4、视频编辑与发布5、 结论 前言 采集视频素材 短视频已成为当下最受欢迎的内容形式之一,无论是个人创作者还是企业品牌,都在积极拥抱这一趋势。然而,短视频的制作不仅需要创意和技巧,还需要大量的高质量素材作为支撑。本文将探讨如何利用现代技术手段Springboot集成AI大模型技术,结合亮数据代理服务,自动化地获取和处理短视频素材,以提高制作效率和视频质量。 1.如何入门亮数据 1.1、注册登录 亮数据是一个全球IP代理资源服务商,提供了大量的动态IP和静态住宅IP资源。通过使用其代理服务,我们可以模拟固定某个区域的真实用户访问,有效隐匿我们自己的IP,保护好我们自己的电脑和数据安全。 1.2、注册账号 输入邮箱的工作邮箱和电话,完善其他信息,就可以注册账号了。 1.3、登录 使用注册时的邮箱账号登录,不记得密码的,可以使用邮箱验证码登录。 1.4、购买静态住宅代理 选择购买静态住宅代理 1.5、展示购买的代理 回到个人中心首页,显示了我购买的代理。下面isp_proxy2就是刚购买的代理,类型是静态住宅,状态是运行,流量已使用311.31MB(刷刷刷的获取素材,可见后文)。 2. 使用Springboot、AI大模型构建系统 Springboot是一个开源的Java框架,用于创建独立、生产级的基于Spring框架的应用程序。而AI大模型能够理解和生成自然语言文本。通过将两者集成,我们可以构建一个自动化的短视频素材获取系统。 2.1 使用Springboot、AI大模型构建爬虫 Springboot提供了强大的开发功能,结合Selenium、chromedriver、Jsoup等工具,可以轻松实现对目标网站的GET请求,获取视频和图片素材。 2.2、在Springboot项目添加工具 在Springboot项目中添加Selenium、chromedriver、Jsoup工具。 不懂的话,可以在CSDN里可以通过大模型去搜索答案。 找到Springboot项目的pom.xml文件,把大模型里的对应答案复制进去(jar包版本号可以根据程序运行情况调整)。 <?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.7.7</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.kelvin</groupId> <artifactId>spiderX</artifactId> <version>0.0.1-SNAPSHOT</version> <name>spiderX</name> <description>spiderX</description> <properties> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <scope>runtime</scope> <optional>true</optional> </dependency> <dependency> <groupId>org.

Python从0到100(三十三):xpath和lxml类库

1. 为什么要学习xpath和lxml lxml是一款高性能的 Python HTML/XML 解析器,我们可以利用XPath,来快速的定位特定元素以及获取节点信息 2. 什么是xpath XPath,全称为XML Path Language,是一种用于在XML文档中进行导航和数据提取的语言,可用来在 HTML\XML 文档中对元素和属性进行遍历。 W3School官方文档:http://www.w3school.com.cn/xpath/index.asp 3. 认识xml 知识点: html和xml的区别xml中各个元素的的关系和属性 3.1 html和xml的区别 HTML(HyperText Markup Language)和XML(eXtensible Markup Language)都是用于描述数据的标记语言,但它们之间存在一些关键的区别: 目的: HTML:设计用来创建和展示网页内容。XML:设计用来存储和传输数据,不定义数据的显示方式。 标准性: HTML:有固定的标签集,如<p>、<div>、<a>等。XML:允许用户定义自己的标签,提供了极大的灵活性。 结构性: HTML:结构相对宽松,某些标签可以不闭合或不严格遵守嵌套规则。XML:非常严格,所有标签必须正确闭合,并且必须正确嵌套。 数据表示: HTML:主要用于展示数据,不关注数据的结构和语义。XML:强调数据的结构和语义,适合数据的存储和交换。 样式和行为: HTML:可以内嵌CSS和JavaScript来控制样式和行为。XML:不包含样式和行为的定义,通常需要与XSL(eXtensible Stylesheet Language)或XSLT(eXtensible Stylesheet Language Transformations)结合使用来定义样式和转换数据。 错误容忍度: HTML:浏览器对HTML的错误相对宽容,即使某些标签使用不当,页面仍可能显示。XML:对错误非常敏感,如果XML文档格式有误,通常无法被正确解析。 文档类型: HTML:通常不需要文档类型声明(DOCTYPE)或可以有简化的声明。XML:每个XML文档都需要一个文档类型声明来指示使用的是XML。 应用范围: HTML:主要用于网页设计和开发。XML:应用范围广泛,包括配置文件、数据交换格式、RSS feeds等。 命名空间: HTML:不支持命名空间。XML:支持命名空间,有助于解决不同XML文档中标签名称冲突的问题。 扩展性: HTML:扩展性有限,受限于其固定的标签集。XML:高度可扩展,用户可以根据需要创建新的标签和属性。 尽管HTML和XML在某些方面相似,但它们的设计目标和使用方式有着根本的不同。HTML专注于网页内容的展示,而XML则是一种更为通用的数据交换格式。 3.2 xml的树结构 <bookstore> <book category="COOKING"> <title lang="en">Everyday Italian</title> <author>Giada De Laurentiis</author> <year>2005</year> <price>30.00</price> </book> <book category="CHILDREN"> <title lang="

浅谈CSS属性:clip-path

目录 1.circle()2.ellipse()3.polygon()4.path()5.rect()6.xywh() CSS的 clip-path 属性可以使用裁剪的方式创建元素的 可显示区域。区域内的部分显示,区域外的隐藏。 本文主要讲的是clip-path的取值,也就是一种表现基础图形的 CSS 数据类型。该数据类型的常见取值有:circle()、ellipse()、polygon()、path()、rect()、xywh()。 首先写一段基础代码,定义一个矩形盒子: <style> * { margin: 0; padding: 0; box-sizing: border-box; } .box { width: 300px; height: 300px; margin: 50px auto; background-color: burlywood; } </style> <div class="box"></div> 1.circle() 该取值定义一个圆形。函数内可写px大小或者百分比,但不能超过举行盒子的宽度的一半,否则圆形区域会超过矩形,超过部分被隐藏。 如: .box{ clip-path: circle(50px); /* 圆形半径为50px */ } .box{ clip-path: circle(50%); /* 圆形半径为300x50%=150px */ } 2.ellipse() 该取值定义一个椭圆,函数内填写x,y方向的长度取值,以及图形的圆心在x,y方向的偏移量。 如: .box{ clip-path: ellipse(130px 140px at 10% 20%); } 基于此,我们也可以设置出圆形,也就是xy方向的长度为矩形的一半150px,圆心相对于矩形偏移50%。 3.polygon() 该取值定义一个多边形,可使用n组顶点填充。 如:定义一个三角形。则使用三组顶点,每组顶点确定一个xy方向的偏移量。每组顶点的xy的偏移量相对于矩形的左上角顶点进行确定。 .box{ /* 第一个顶点 */ clip-path: polygon(150px 0, 0 300px, 300px 300px); /* 或者使用百分比 */ clip-path: polygon(50% 0, 0 100%, 100% 100%); } 4.

【扩散模型】LCM LoRA:一个通用的Stable Diffusion加速模块

潜在一致性模型:[2310.04378] Latent Consistency Models: Synthesizing High-Resolution Images with Few-Step Inference (arxiv.org) 原文:Paper page - Latent Consistency Models: Synthesizing High-Resolution Images with Few-Step Inference (huggingface.co) 简介:LCM 只需 4,000 个训练步骤(约 32 个 A100 GPU/小时)即可从任何预训练的稳定扩散 (SD) 中提取出来,只需 2~4 个步骤甚至一步即可生成高质量的 768 x 768 分辨率图像,从而显着加速文本转换 -图像生成。 潜在一致性模型 介绍 潜在扩散模型(Latent Diffusion models, ldm)在高分辨率图像合成方面取得了显著的成果。然而,迭代采样过程计算量大,导致生成速度慢。受一致性模型的启发,我们提出了潜在一致性模型(Latent Consistency Models, lcm),能够在任何预训练的ldm上以最小的步骤进行快速推理,包括稳定扩散。 原理:将引导反向扩散过程视为求解增强概率流ODE (PF-ODE), lcm设计用于直接预测潜在空间中此类ODE的解,从而减少了多次迭代的需要,并允许快速,高保真采样。有效地从预训练的无分类器引导扩散模型中提取,高质量的768×768 2 ~ 4步LCM仅需32 A100 GPU小时即可进行训练。此外,引入了潜在一致性微调(LCF),这是一种针对自定义图像数据集微调LCF的新方法。 一致性模型(CMs):作为一种新型生成模型显示出巨大的潜力,可以在保持生成质量的同时加快采样速度。一致性模型采用一致性映射,直接将ODE轨迹中的任意点映射到原点,实现快速一步生成。可以通过提取预训练的扩散模型或作为独立的生成模型进行训练。 原理 潜在空间中的一致性蒸馏 在诸如稳定扩散(Stable Diffusion, SD)(Rombach et al, 2022)等大规模扩散模型中,利用图像的潜在空间有效地提高了图像生成质量并减少了计算负载。在SD中,首先训练一个自编码器(E, D)来将高维图像数据压缩为低维潜在向量 𝑧=𝐸(𝑥),然后解码以重建图像 𝑥ˆ=𝐷(𝑧)。在潜在空间中训练扩散模型与基于像素的模型相比,大大降低了计算成本并加快了推理过程;潜在扩散模型(LDMs)使得在笔记本电脑的GPU上生成高分辨率图像成为可能。

绝区伍--2024年AI发展路线图

2024 年将是人工智能具有里程碑意义的一年。随着新模式、融资轮次和进步以惊人的速度出现,很难跟上人工智能世界发生的一切。让我们深入了解 2024 年可能定义人工智能的关键事件、产品发布、研究突破和趋势。 2024 年第一季度 2024 年第一季度将推出一些主要车型并进行改进,有望进一步推动 AI 能力的发展。 双子座超级发射 我们可以预期谷歌将在第一季度推出 Gemini Ultra。得益于宪法提示和自我监督等宪法人工智能技术,他们的新版对话式人工智能助手可能会击败 GPT-4。虽然它可能不会在每个领域都击败 GPT-4,但 Gemini Ultra 的安全性和推理能力应该远远超出 OpenAI 在 GPT-3 和 GPT-3.5 上所展示的水平。 Gemini Ultra 的推出将给 OpenAI 带来巨大压力,迫使其提前发布 GPT-4.5。然而,GPT-4.5 可能要到 2024 年第二季度才会真正推出。 开源微调技术取得进展 随着研究人员分享更多微调技术,我们还应该在 2024 年第一季度看到开源 AI 模型的显著改进。在推理任务和数学/逻辑问题上进行微调的模型可能会在常识和避免虚假声明等领域缩小与 GPT-3 等专有模型的差距。 到第一季度末,一些开源模型甚至可能在复杂的数学/逻辑推理基准测试中达到人类水平(超过 75%)。当然,作弊风险仍然是基准测试的一个隐患,但自然语言任务也应该会取得令人印象深刻的进步。 机器人技术融资增长 随着人工智能软件的快速发展,更多的资金将流入机器人等商业应用。我们应该看到至少两轮数百万美元的融资,这些融资面向专注于将人工智能进步带入现实世界的机器人初创公司。仓库机器人、自动驾驶汽车,甚至家庭/办公室的通用辅助机器人应该会在 2024 年取得重大进展。 小型开源模型的兴起 尽管 OpenAI 等组织宣布了超过 100 万亿参数的巨型模型,但较小的开源模型在许多现实世界的用例中仍将越来越受欢迎。公司发现,针对小众数据集进行微调的 100 到 200 亿个参数模型非常有用,而且训练和部署成本低廉。 即使 GPT-4 等超级模型成为头条新闻,也要寻找这些“微模型”来为更具互动性的演示和基本聊天机器人提供支持。与依赖 GPT-3 等单一模型相比,初创公司会发现使用一组微模型更容易满足用户需求。 2024 年第二季度 随着高调的模型发布、大量用于人工智能安全工作的资金以及这些复杂模型实际工作原理的突破性研究,人工智能炒作周期将在 2024 年第二季度再次达到高峰。 LLama 3 和 GPT 4.

从Java开发者到.NET Core初级工程师学习路线:C#语言基础

1. C#语言基础 1.1 C#语法概览 欢迎来到C#的世界!对于刚从Java转过来的开发者来说,你会发现C#和Java有很多相似之处,但C#也有其独特的魅力和强大之处。让我们一起来探索C#的基本语法,并比较一下与Java的异同。 程序结构 C#程序的基本结构与Java非常相似。这里是一个简单的C#程序: using System; namespace HelloWorld { class Program { static void Main(string[] args) { Console.WriteLine("Hello, World!"); } } } 对比Java的版本: public class HelloWorld { public static void main(String[] args) { System.out.println("Hello, World!"); } } 你会发现,两者的结构非常相似。主要的区别在于: C#使用using关键字导入命名空间,而Java使用import。C#的Main方法是static void Main(string[] args),而Java是public static void main(String[] args)。C#使用Console.WriteLine()输出,Java使用System.out.println()。 在c# 9的最新语法上还可以更简洁,是的没错,只需要一行代码,不需要写命名空间,类,方法,直接编写代码,当然这个方式只存在c#9以上的版本。 Console.WriteLine("Hello, World!"); 命名约定 C#和Java的命名约定有些许不同: C#中,方法名和属性名通常使用PascalCase(如CalculateTotal)。局部变量和参数使用camelCase(如totalAmount)。接口名称以"I"开头(如IDisposable)。 而Java中: 方法名和变量名都使用camelCase。接口名称不需要特殊前缀。 数据类型 C#和Java的基本数据类型很相似,但也有一些区别: C#: int x = 10; long y = 100L; float f = 3.