概述 我们接触的注解主要分为以下两类
运行时注解:通过反射在运行时动态处理注解的逻辑编译时注解:通过注解处理器在编译期动态处理相关逻辑 平时我们接触的框架大部分都是运行时注解,比如:@Autowire @Resoure @Bean 等等。
那么我们平时有接触过哪些编译期注解呢,@Lombok @AutoService 等等
像这些编译时注解的作用都是自动生成代码,一是为了提高编码的效率,二是避免在运行期大量使用反射,通过在编译期利用反射生成辅助类和方法以供运行时使用。
那这些编译器注解都是如何运行的呢? 又是怎么自动生成代码的呢?
我们今天来详细介绍一下,不过再介绍之前,可以先简单了解一下 Java 注解的基本概念
Java 注解
注解处理器 注解处理流程 注解编译期处理流程最关键的一个类就是 Processor ,它是注解处理器的接口类,我们所有需要对编译期处理注解的逻辑都需要实现这个 Processor 接口,当然,AbstractProcessor 抽象类帮我们写好了大部分都流程,所以我们只需要实现这个抽象类就可以很方便的定义一个注解处理器;
注解处理流程由多轮完成。每一轮都从编译器在源文件中搜索注解并选择适合这些注解的 注解处理器 (AbstractProcessor) 开始。每个注解处理器依次在相应的源上被调用。
如果在此过程中生成了任何文件,则将以生成的文件作为输入开始另一轮。这个过程一直持续到处理阶段没有新文件生成为止。
注解处理器的处理步骤:
在 java 编译器中构建;编译器开始执行未执行过的注解处理器;循环处理注解元素 (Element),找到被该注解所修饰的类,方法,或者属性;生成对应的类,并写入文件;判断是否所有的注解处理器都已执行完毕,如果没有,继续下一个注解处理器的执行 (回到步骤 1)。 AbstractProcessor 这是注解处理器的核心抽象类,我们主要来看看里面的方法
getSupportedOptions() 默认的实现是 从注解SupportedOptions获取值,该值是一个字符数组,例如
@SupportedOptions({"name","age"}) public class SzzTestProcessor extends AbstractProcessor { } 不过貌似该接口并没有什么用处。
有资料表示 该可选参数可以从 processingEnv 获取到参数。
String resultPath = processingEnv.getOptions().get(参数); 实际上这个获取的参数是编译期通过入参 -Akey=name 设置的,跟 getSupportedOptions 没有什么关系。
getSupportedAnnotationTypes 获取当前的注解处理类能够处理哪些注解类型,默认实现是从SupportedAnnotationTypes注解里面获取;
注解值是个字符串数组 String [] ;
Java高级编程——多线程 高级部分使用IDEA编写代码!!!!!!! 目录
Java高级编程之多线程
一、基本概念:程序、进程、线程
1.基本概念
2.使用多线程的优点
3.何时需要多线程
二、线程的创建和使用
1.线程的创建和启动
2.Thread类
3.API中创建线程的两种方式
代码演示
代码演示 4.线程的调度
三、线程的生命周期
1.JDK中用Thread.State类定义了线程的几种状态
四、线程的同步
1.问题的提出
2.Synchronized的使用方法
3.代码演示
使用同步代码解决实现Runnable接口的线程安全问题
使用同步代码解决实现继承的线程安全问题
使用同步方法解决实现Runnable接口的线程安全问题
使用同步方法解决实现继承的线程安全问题
解决单例设计模式之懒汉式(线程安全)
4.线程的死锁问题
代码演示
5.JDK 5.0新增解决线程安全方法(lock锁的方式)
代码演示
五、线程的通信
1.概念
2.代码演示
3.经典例题:生产者/消费者问题
代码演示
六、JDK5.0新增线程创建方式
1.新增方式一:实现Callable接口
代码演示
2.新增方式二:使用线程池
线程池相关API
一、基本概念:程序、进程、线程 1.基本概念 程序(program)是为完成特定任务、用某种语言编写的一组指令的集合。即指一 段静态的代码,静态对象。
进程(process)是程序的一次执行过程,或是正在运行的一个程序。是一个动态的过程:有它自身的产生、存在和消亡的过程。——生命周期
如:运行中的QQ,运行中的MP3播放器
程序是静态的,进程是动态的
进程作为资源分配的单位,系统在运行时会为每个进程分配不同的内存区域
线程(thread),进程可进一步细化为线程,是一个程序内部的一条执行路径。
若一个进程同一时间并行执行多个线程,就是支持多线程的
线程作为调度和执行的单位,每个线程拥有独立的运行栈和程序计数器(pc),线程切换的开 销小
一个进程中的多个线程共享相同的内存单元/内存地址空间——>它们从同一堆中分配对象,可以 访问相同的变量和对象。这就使得线程间通信更简便、高效。但多个线程操作共享的系统资 源可能就会带来安全的隐患。
单核CPU和多核CPU的理解
单核CPU,其实是一种假的多线程,因为在一个时间单元内,也只能执行一个线程 的任务。例如:虽然有多车道,但是收费站只有一个工作人员在收费,只有收了费才能通过,那么CPU就好比收费人员。如果有某个人不想交钱,那么收费人员可以 把他“挂起”(晾着他,等他想通了,准备好了钱,再去收费)。但是因为CPU时 间单元特别短,因此感觉不出来。
如果是多核的话,才能更好的发挥多线程的效率。(现在的服务器都是多核的)
一个Java应用程序java.exe,其实至少有三个线程:main()主线程,gc() 垃圾回收线程,异常处理线程。当然如果发生异常,会影响主线程。
并行与并发
并行:多个CPU同时执行多个任务。比如:多个人同时做不同的事。
参考:AndroidXRef (http://androidxref.com/)版本:Pie - 9.0.0_r3 整体结构 对于 Android OS 的源码目录来说,各个版本的结构大同小异,随不同版本特性会有个别目录差异。编译后会额外产生一个 out 文件夹用于存储编译产生的文件。对于 Android 9.0.0 系统来说,其源码根目录结构如下:
Android.bp:soong 编译配置文件,使用 soong 编译前会遍历源码目录树并找到所有 .dp 文件art/:ART 运行环境bionic/:针对 Android OS 定制的系统C库bootable/:Android OS 启动引导相关bootstrap.bashbuild/:Android OS 编译规则存放路径compatibility/:Android OS 兼容性计划cts/:Android OS 兼容性测试套件标准dalvik/:Android Dalvik 虚拟机相关developers/:Android 开发者相关文档development/:Android 应用开发基础设施相关device/:Android OS 设备相关配置external/:Android OS 开源模组相关frameworks/:应用程序框架,Android 系统核心hardware/:HAL 部分相关代码,硬件适配接口kernel/:Linux Kernel,默认不提供,需要自己单独下载导入libcore/:Android Java 核心类库libnativehelper/:Android 动态库,实现 JNI 基础Makefile:全局 Makefile 文件,指定编译规则packages/:应用程序包pdk/:Plug Development Kit,本地开发套件platform_testing/:Android OS 测试程序相关prebuilts/:x86 和 arm 架构下预编译资源sdk/:Android Java 层 SDK 和模拟器system/:Android 底层文件系统库、应用、组件相关test/:Android Vendor 测试框架相关toolchain/:Android 工具链相关tools/:Android 工具文件相关 系统架构实现 Android OS 的系统架构从上(直接与用户交互)到下(直接与硬件交互)分为好几层,其中每层的核心代码位于如下位置:
标题中的三种通信方式(协议)是比较常见的一些通信协议,对于它们有一定的了解对于我们学习嵌入式单片机的学习有着非常重要的作用。于是我们对此有一些信息给到各位读者,这也是笔者自己巩固知识点的方式。如果觉得有帮到各位,还希望不吝点赞转发;如果笔者有所纰漏,还望指正。
1.SPI(Serial Peripheral Interface 串行外设接口)
SPI 串行外设接口是最初由摩托罗拉公司制定的一种同步串行传输规范,也是一种芯片之间、外设芯片的串行扩展接口,一种高速、全双工、同步通信总线,可以同时发送接受信息。
在一次SPI通信中,可以有多个芯片,但是主芯片(master chip)只能有一个,从芯片(slave chip)可以有多个。SPI接口的读写操作,都是由主芯片发起,当存在多个从设备时,通过各自的片选信号进行管理。
完成一次SPI通信至少需要4根线(单向通信3根), 接下来介绍这4根线:
1. SCLK(Serial Clock):时钟信号,由主设备产生;
2. CS/SS(Chip Select/Slave Select):从设备使能信号,由主设备控制,一主多从时,CS/SS是从芯片是否被主芯片选中的控制信号,只有片选信号为预先规定的使能信号时(高电位或低电位),主芯片对此从芯片的操作才有效(一般使能信号是低电位);
3. MISO( Master Input Slave Output):主设备数据输入,从设备数据输出;
4. MOSI(Master Output Slave Input):主设备数据输出,从设备数据输入;
SPI有一主一从、一主多从的模式,不过两者的通信原理差不多,我们就以一主一从(向93C46芯片的0x01地址写入00001111)为例进行介绍:
PS:在93C46芯片的使用指南上指明了SCLK的使能信号为高电平,需要注意。然后在这个芯片中,有128个不同的地址,每个地址存储1个字节的数据,每个存储空间都有相应的寻址。这个芯片可以接受100w次擦写,并能存储100年。
首先,单片机给93C46芯片发送高电平使能信号,于是我们在两个芯片之间的通信就建立起来了。
接下来,我们先给93C46芯片发送起始位1表示操作开始,然后传输写数据的操作码01告诉芯片我们即将进行写入数据的操作,再发送7位二进制地址0000001告诉芯片我们写入数据的相应寻址,最后我们传输8位二进制的数据00001111,于是一次写数据操作就完成了。
不过,数据发送的原理并非这么简单。首先在SS/CS高电位使能信号起作用的时候,我们的通信才能建立。其次,只有在时钟信号SCLK处于上升沿触发的时候,MOSI输出相应的信号才能被读取。在这个案例中我们采集到的数据就是1 01 0000001 00001111。
在接下来,我们再看一下SPI数据是如何读取93C46芯片里的数据的。
在我们写入这些数据后,我们进行读数据的操作。和写数据一样,第一步我们发送起始位1信号,表示操作开始,然后输入10读数据信号,告诉芯片我们即将进行读数据操作,再发送要求读数据的相应地址0000001,接下来我们就能接收到来自93C46芯片的相应数据。
注意:在这两个操作过程中,我们的SS/CS和SCLK都在持续输出相应的信号。
2. I2C(Inter-Integrated Circuit 集成电路总线)
过去我们所知道的串口通信只能发生在两个串口之间,如果需要通信的芯片数量多起来,不仅会使通信线路变得很多,也会使这个线路变得很复杂很乱。而I2C简单的结构可以很好地解决这个问题。
I2C是由Philips公司开发的一种简单、双向二线制同步串行总线。它只需要两根线即可在连接于总线上的器件之间传送信息。主器件用于启动总线传送数据,并产生时钟以开放传送的器件,此时任何被寻址的器件均被认为是从器件。
和SPI通信类似,I2C同样采用一主多从地结构。同样我们先以主设备向从设备写入数据为例。 通信的两个数据线分别为SCL时钟线和SDA数据线。所有的数据操作都是在这两个数据线的输出的共同作用下产生的,类似于SPI的CS/SS、SCLK、MOSI之间的相互作用。
我们先说一下这个信号采集的原理,以图为例,只有在SCL的时钟信号和SDA的数据信号都处于高电平的时候才能读取高电平1;相反地,如果SCL处于高电平,而SDA处于低电平,那么就会读取低电平0;如果SCL时钟信号处于低电平,说明没有处在工作状态,不会被采集。
当我们开始传输数据的时候,我们先发送起始位1表示操作开始,然后发送7位设备地址来在设备群里寻找相应的设备,再输出操作位0表示写数据操作,然后给一个位的时间接收目标从设备的应答信号,之后我们再输出一个8位寄存器的地址,在相应设备中寻找相应的地址,再留出一个目标设备的一个应答讯号。最后输出8位数据,留一位接收目标设备的应答信号,再输出停止位表示停止操作。
接下来我们再演示一下读数据操作。我们先发送起始位1表示操作开始,然后发送7位设备地址来在设备群里寻找相应的设备,再输出操作位0表示写数据操作,然后给一个位的时间接收目标从设备的应答信号,之后我们再输出一个8位寄存器的地址,在相应设备中寻找相应的地址,再留出一个目标设备的一个应答讯号,第一部分的操作就完成了。第二部分中,再往起始位输出1表示操作开始,然后发送7位设备地址来在设备群里寻找相应的设备,再输出操作位1表示读数据操作,然后给一个位的时间接收目标从设备的应答信号,再输出8位数据,留一位接收目标设备的应答信号,再输出停止位表示停止操作。
这就是I2C通信。
3. CAN(Controller Area Network 控制器局域网)
我们所熟知的局域网中,我们可以把电脑连接到路由器上,这几台设备之间就可以交互通信了。在CAN里也是类似的。
我们的ECU之间的通信中,类似于串口通信,原来我们同样需要很多线路进行通信,不仅工作量大且复杂杂乱。而采用CAN总线通信之后,那就简化了电路,节约了线材资源和引脚接口资源,组成了相应的局域网通信。
要进行CAN通信的时候,需要专门的CAN通信芯片进行信号转换。比如我们的TTL芯片里3.3V~5V识别为高电平,0~0.4V识别为低电平,但我们需要在经过CAN信号转换芯片的转换之后,我们就能把这个高低电平分解为差分信号,传输给别的ECU。
差分信号和我们普通的高低电平信号不一样,它是利用不同的信号电压差来传输不同的信号。比如在这个利用TTL芯片的信号转换为差分信号后,原来的低电平输出为一个3.5V和1.5V两个信号,这个2V的电压差表示TTL信号的逻辑低电平信号;原来的高电平信号输出为2个2.5V信号,这个0V电压差表示TTL信号的逻辑高电平信号。使用差分信号的原因是它是利用电压差表示不同的信号的,而且它们两条线的连接方式一般是两绞线,即使收到干扰,电压差也不会有很大的变化。
因此,CAN总线通信可以经受更长的线材通信。
那我们以我们发送数据为例,我们先发送1位起始位,表示操作开始,这个起始位一定是0。再发送11位识别码,表示我们需要寻找的相应设备寻址。接下来输出RTR位,表示到底进行的是远程请求位(相应操作码为1)或数据位(相应操作位为0)。
接下来是6位控制码,表示相应的数据长度.控制码第一位IDE码是区别标准格式(IDE码为0)和拓展模式(IDE码为1)。标准格式有11位识别码,在11位识别码之后是0的RTR码和IDE码0;在拓展格式中有29位识别码,在29位识别码之后是0的RTR码和IDE码1。接下来再是一位空闲位。接下来是4位DLC(Data link control)码,用来表示数据长度代码,比如他是0001就表示有一个字节的数据,如果是1000,那就表示后面有8个字节的数据即64位。
接下来是CRC16位循环冗余校验位,是为了表示数据准确性设置的,首先是15位校验码,设备接收端会根据数据计算出它的CRC位,如果不同就表示数据有误,于是就会重新发送一遍数据帧;下一位是CRC的界定符,是逻辑1,用于间隔后面的信号。
然后是2位ACK码,第一位是确认槽,发送端发出1,接收端发出0表示应答,第二位是ACK界定符,一定是1。最后是7位结束符,这7位都是1。
你是否曾在 SELECT 查询中看到过 WHERE 1=1 条件。我在许多不同的查询和许多 SQL 引擎中都有看过。这条件显然意味着 WHERE TRUE,所以它只是返回与没有 WHERE 子句时相同的查询结果。此外,由于查询优化器几乎肯定会删除它,因此对查询执行时间没有影响。那么,WHERE 1=1 的作用是什么?这就是我们今天要在这里回答的问题!
WHERE 1=1 会改善查询执行吗? 正如前文中所述,我们预计查询优化器会删除硬编码的 WHERE 1=1 子句,因此我们不应看到查询执行时间减少。为了证实这个假设,让我们在 Navicat 中运行一个有和一个无 WHERE 1=1 子句的 SELECT 查询。
首先,以下是在 Sakila 示例数据库运行的查询,获取从 Lethbridge 商店租借电影的客户:
在信息选项卡的底部可以看到 0.004 秒的运行时间(用红色方框突出显示)。
现在,让我们运行相同的查询,但添加了 WHERE 1=1 子句:
同样,运行时间为 0.004 秒。尽管查询的运行时间可能因许多因素会略有波动,但可以肯定地说 WHERE 1=1 子句对其没有任何影响。
那么,为什么要使用它呢?简单来说,就是...
为方便而设 事实上,WHERE 1=1 子句只是一些开发人员采用的一种惯性做法,以简化静态和动态形式的 SQL 语句的使用。
在静态 SQL 中 向已经具有 WHERE 1=1 的查询添加条件时,此后的所有条件都将包含 AND,因此在注释掉试验查询的条件时更容易。
这类似于另一种在列名之前而不是之后加入逗号的技巧。同样,更容易注释:
在动态 SQL 中 这也是以编程方式构建 SQL 查询时的常见做法。从“WHERE 1=1”开始,然后附加其他条件,例如“ and customer.
很多时候,你会希望知道一行(或一组行)在总行数所占的比重。换句话说,即是一行占总计数的百分比是多少。为了说明这一点,让我们看看以下显示在 Navicat Premium 16 中的表:
通过结合 count() 函数和 Group By 子句,我们可以很容易地找出每种水果有多少订单:
那么现在我们如何查看每种水果的订单占订单总数的百分比?事实上,在 SQL 中计算行百分比有三种标准方法。他们是:
使用 OVER() 子句使用子查询使用公用表表达式,或 CTE 本文的其余部分将依次探讨其中的每一种。
OVER() 子句 OVER 子句主要与窗口函数一起使用,用于确定将查询中的哪些行应用于函数、该函数以什么顺序计算这些行以及何时重新开始函数的计算。
OVER 子句是 SQL 中计算行百分比的最有效方法,因此如果你最着重效率,它应该是你的首选。以下是获得百分比的公式:
count(*) * 100.0 / sum(count(*)) over() 将上述 SQL 添加到原本的查询中会产生以下结果:
看起来不错,但稍微四舍五入也没什么坏处。不幸的是,使用 over() 子句并不容易做到这一点。也许下一个选项会更适合你。
使用子查询 并非所有数据库都支持 OVER() 子句,因此子查询方法可能是非常有用的后备解决方案。它有时被称为“通用解决方案”,因为它适用于所有数据库。这种方法的另一个好处是它是最易与 Round() 等函数合并使用。以下是我们需要添加到查询中的内容:
count(*) * 100.0 / (select count(*) from <YourTable>) 以下是实行的通用解决方案:
使用公用表表达式(CTE) With common_table_expression 子句指定一个临时命名结果集,称为公用表表达式(CTE)。然后,我们可以从临时结果集中进行选择,以将更多函数应用于检索到的字段。在我们的例子中,我们可以将 sum() 函数应用于计数以得到百分比:
请记住,这种方法效率最低,因为 CTE 基本上针对内部(初始)查询的结果运行第二个查询。话虽如此,有时你可能需要使用 CTE 执行无法一次轻松完成的额外处理。
总结 在这篇文章中,我们学习了三种方法来表示一行(或一组行)在总行数所占的比重。每种方法都有自己的优点和缺点,因此你必须根据自己的具体要求选择。如果你想试用 Navicat 16,可以在这里下载 Navicat 的 14 天全功能免费试用版。
一.哈夫曼树的定义: 在许多应用中,树中结点常常被赋予一个表示某种意义的数值,称为该结点的权。从树的根到任意结点的路径长度(经过的边数)与该结点上权值的乘积,称为该结点的带权路径长度。树中所有叶结点的带权路径长度之和称为该树的带权路径长度.
在含有n个带权叶结点的二叉树中,其中带权路径长度(WPL)最小的二叉树称为哈夫曼树,也称最优二叉树。 例如,下图的3棵二叉树都有4个叶子结点a,b, c,d,分别带权7,5,2,4, 它们的带权路径长度分别为 (a)WPL = 7*2+5*2+2*2+4*2=36 (b)WPL = 4*2+7*3+5*3+2*1=46 (c)WPL = 7*1+5*2+2*3+4*3=35 (c)树的WPL最小.可以验证,它恰好是哈夫曼树. 二.哈夫曼树的构造: 给定n个权值分别为w1, w2,.., W{,的结点,构造哈夫曼树的算法描述如下: 1)将这n个结点分别作为n棵仅含一个结点的二叉树,构成森林F。
2)构造一个新结点,从F中选取两棵根结点权值最小的树作为新结点的左、右子树,并且
将新结点的权值置为左、右子树上根结点的权值之和。
3)从F中删除刚才选出的两棵树,同时将新得到的树加入F中。
4)重复步骤2)和3),直至F中只剩下一棵树为止。
从上述构造过程中可以看出哈夫曼树具有如下特点: 1)每个初始结点最终都成为叶结点,且权值越小的结点到根结点的路径长度越大。
2)构造过程中共新建了n-1个结点(双分支结点),因此哈夫曼树的结点总数为2n-1。
3)每次构造都选择2棵树作为新结点的孩子,因此哈夫曼树中不存在度为1的结点。
例如,权值(7,5,2,4)的哈夫曼树的构造过程如下图 : 三.代码实现: 1. 树中结点的结构体 typedef struct TreeNode { int weight;//权值 int parent;//父结点 int lchild;//左孩子 int rchild;//右孩子 }TreeNode; 2.哈夫曼树的结构体 typedef struct HFTree { TreeNode *data;//数据域 int length;//长度(结点个数) }HFTree; 3.初始化哈夫曼树 HFTree *initTree(int *weight,int length) {/*参数一:权值数组 参数二:数组长度*/ HFTree *T = (HFTree *)malloc(sizeof(HFTree));//申请一颗树的空间 T->data = (TreeNode *)malloc(sizeof(TreeNode)*(2*length-1));//申请一个结点的空间,长度由数组长度确定 T->length = length;//长度写入 int i; for(i=0;i<length;i++) {/*按序一次写入权值,父结点初始为0,左右孩子初始为-1*/ T->data[i].
产品概览
Keysight E8257D (Agilent) PSG 模拟信号发生器提供业界领先的输出功率、电平精度和高达 67 GHz 的相位噪声性能(工作频率可达 70 GHz)。Agilent PSG 模拟信号发生器的高输出功率和卓越的电平精度通常无需使用外部放大器来测试高功率设备,并最大限度地降低测试不确定性以在设计过程的早期识别错误。世界一流的相位噪声性能(通常为 -116 dBc/Hz @ 10 GHz 载波和 10 kHz 偏移)是本地振荡器、低抖动时钟替代和相邻信道选择性测试的理想选择。在测试高级射频和微波雷达、通信和控制系统时,PSG 模拟信号发生器可提供您所需的性能。
E8267D 信号发生器基于模块化微波信号发生器平台——可选择添加 AM、FM、ØM 和/或脉冲生成连续波 (CW) 信号,用于 LO 替代或扫描激励响应分析。可选择添加 AM、FM、ØM 和/或脉冲调制以准确表征射频和微波元件和设备。许多性能增强选项也可用于配置信号发生器以满足您的特定测试要求。为当今的射频和微波测试应用定制 PSG,并在未来根据您的需求变化轻松升级。
Keysight E8257D(安捷伦)的特性和规格包括:
广泛的频率范围选择(选项)- 250 kHz 至 20、31.8、40、50 或 67 GHz(工作至 70 GHz),分辨率为 0.001 Hz高输出功率 - 典型性能为 +23 dBm @ 20 GHz、+ 17 dBm @ 40 GHz、+ 14 dBm @ 67 GHz,具有出色的电平精度世界一流的 SSB 相位噪声 - Agilent 的超低相位噪声选件可提供最佳性能(带选件 UNX)灵活的模拟调制格式 - AM、FM、相位和脉冲双内部函数发生器 - 正弦波、方波、三角波、斜波和噪声波形轻松扩展频率 - 使用 OML, Inc.
正规式和正规文法的相互转换
(正规式即正则表达式,正规文法即3型文法)
1. 对形如A=>x*y的正规式产生式,重写为
A=>xB
A=>y
B=>xB
B=>y,B为一个新的非终结符
2. 对形如A=>x|y的正规式产生式,重写为
A=>x
A=>y
例如:将r=a(a|d)*转换为相应的正规文法
首先,令S为开始符号,则S=a(a|d)*,然后形成S=>aA和A=>(a|d)*
再变换成S=>aA A=>(a|d)B A=>ε B=>(a|d)B B=>ε 最后全部变为符合正规文法的表达式:
S=>aA A=>aB A=>dB B=>aB B=>dB A=>ε B=>ε 文法产生式
正规式
规则1
A=>xB B=>y
A=xy
规则2
A=>xA|y
A=x*y
规则3
A=>x A=>y
A=x|y
例如:有下列文法G[S]:
S=>aA
S=>a
A=>aA
A=>dA
A=>a
A=>d
首先,进行合并,S=>aA|a, A=(aA|dA)|(a|d)=(a|d)A(a|d)=(a|d)*(a|d)
再将A的右端带入S可得
S=>a(a|d)*(a|d)|a
即S=>a(a|d)*
正规文法转换成正规式正规式转换成正规文法 不确定的有穷自动机(NFA) 功能:准确地识别正规集
M = (K,Σ,f,S,Z)
其中:(1)K是一个有穷集,每一个元素是一个状态
(2)Σ是一个有穷字母表,每个元素代表一个输入符号。
(3)f是转换函数,是K*Σ=>K上的全体子集的映像。即K*Σ=>2k,2k表示k的幂集。
(4)S∈K,是唯一的一个初态
(5)Z包含于K,是一个终态集也称为可接受状态或结束状态。
也就是说,最大的不同点在于可以多值映射。例如书上
0状态在读取a之后,可能到0状态,也可能到3状态。是一种一对多的关系。
确定有穷自动机的化简
最小化问题,采用分割法
首先将子集,按终态和非终态划分为两个子集,然后看子集里的元素等不等价,把等价的元素分在一起。最后,这些等价的子集用其中一个元素表示即可。
描述 有一个有序数字序列,从小到大排序,将一个新输入的数插入到序列中,保证插入新数后,序列仍然是升序。
输入描述: 第一行输入一个整数N(0≤N≤50)。
第二行输入N个升序排列的整数,输入用空格分隔的N个整数。
第三行输入想要进行插入的一个整数。
输出描述: 输出为一行,N+1个有序排列的整数。
解法思想: 首先在 insertnum 函数中,使用一个循环遍历已排序的整数序列,找到第一个大于新数的位置。通过比较当前元素和新数的大小,如果当前元素大于新数,则跳出循环,得到插入位置 i。
然后使用另一个循环,从序列的最后一个元素开始,将每个元素后移一位,为新数腾出插入位置。
最后将新数插入到找到的位置 i。
在主函数 main 中,首先读取整数 n,表示待插入的整数序列的长度。然后声明一个整数数组 arr,用于存储输入的整数序列。接着使用循环逐个读取整数序列中的元素。
读取整数 num,表示要插入的新数。调用 insertnum 函数将新数插入到整数序列中。
最后使用循环打印整数序列,包括插入后的结果。
#include <stdio.h> void insertnum(int arr[], int n, int num) { int i, j; // 寻找插入位置 for (i = 0; i < n; i++) { if (arr[i] > num){ break; } } // 后移元素腾出插入位置 for (j = n - 1; j >= i; j--) { arr[j + 1] = arr[j]; } // 插入新数 arr[i] = num; } int main() { int n, num; scanf("
土壤数据是在环境、农业、生态等相关研究中都非常常用的数据!我们之前发表过一遍介绍土壤数据来源的文章(可查之前的文章获悉详情)!
土壤湿度是土壤的重要属性!本次我们给大家带来的是1982-2020年全国逐月的土壤湿度数据,数据格式为栅格(.tif)格式,空间分辨率为 0.25°,坐标系为GCS_WGS_1984,数据来源于科学数据银行(ScienceDB)平台!
该数据基于欧空局(ESA)提供的原始土壤湿度数据,但是欧空局提供的原始土壤湿度数据在全国范围内没有实现空间连续,需要进行间隙填充。学者孙灏和徐乾学者基于 XGBoost算法,利用土壤湿度协变量,包括CMA-RA/Land再分析数据中的降水、反射率、地表温度、空气温度和土壤湿度数据;GIMMS和 MODIS的 NDVI数据;GTOPO30数字高程数据;HWSD 提供的土壤质地信息以及时间变量等,填补空间缺失,生成了1982-2020年中国CCI土壤湿度逐月数据集。经检验与ISMN(192个站点)5厘米深度实测值的总体相关系数R和均方根误差(RMSE)分别为0.554和0.0977 cm3cm-3,数据质量略微优于原始CCI数据。
大家可以自己去官方网站上下载原始数据,网址在下文,以下为数据的详细介绍:
01 数据预览
我们具体以2020年12月的全国土壤湿度为例来预览一下:
2020年12月全国土壤湿度 02 数据详情
数据来源:
数据来源于孙灏学者在科学数据银行(ScienceDB)上分享的数据,网址为:
https://www.scidb.cn/en/detail?dataSetId=434a6fad0daa4c32a7396dc7db575fc8
数据格式:
栅格格式(.tif)
时间范围:
1982-2020年(逐月)
空间范围:
全国
数据引用:
Hao Sun, Xu Qian, Zhiyu Zhao (2023) . Monthly gap-filled CCI soil moisture over
region of China (Combined Product). V1. Science Data Bank.
https://doi.org/10.57760/sciencedb.07849.
如有数据使用需求请按照官方平台的要求进行引用,更多数据详情可以查看官网获悉!
文末下方是我们的公众号名片,我们将定期介绍各类城市数据以及数据的可视化和分析技术,有关1982-2020 年全国逐月土壤湿度数据的更多详情,欢迎大家多多关注我们进行了解~
不需要下载软件,直接电脑全选图片,再右键点打印输出成PDF 1、在一个文件夹中,先按自己的顺序,排列好图片(比如名称顺序)
2、全选所有图片后,鼠标放在第一张图片,右键重命名
3、重命名成随便一个数字(比如这里,重命名成 1 )
4、这样重命名操作后,其他图片会自动按之前的顺序变成 1 (xx)
5、【重点!重点!】全选图片,鼠标右键一定要先放在第一张图片,再点打印!(只有这样才会按照我们排好的顺序去进行打印)
最后,关于“如何把多张图片一键输出PDF?” 教程如下: 首先,我们打开"设备和打印机"确认自己电脑有这个玩意,然后选择打印默认设备是它
下次右键那些图片点"打印"的时候,就会弹出另存为PDF的界面,选择保存位置就可以了
以上说明就这么多,有什么不懂可以再问(可以看到刚刚那样操作的PDF成功保存到桌面)
PS:利用这个方法把图片转存成PDF可能速度看电脑配置,有些可能没那么快 【捂脸笑】
The Rust Programming Language Rust 编程语言笔记。
来源:The Rust Programming Language By Steve Klabnik, Carol Nichols 。
翻译参考:Rust 语言术语中英文对照表
Rustaceans Rustaceans 指的是使用过、贡献过或对 Rust 语言有兴趣的用户。
安装 使用 rustup 来安装 Rust。
Rust 源文件以 .rs 作为扩展名。
几个常用的命令:
编译:rustc .rs-file 运行:./compiled-file检查 Rust 编译器版本:rustc --version检查 rustup 版本:rustup --version更新 Rust:rustup update卸载 Rust 和 rustup:rustup self uninstall本地文档:rustup doc Rust 是预(ahead-of-time)编译语言,意思是可以把编译后生成的可执行文件发送给别人,他们可以在不安装 Rust 的前提下运行该文件。
Cargo Cargo 是 Rust 的构建系统和包管理工具。
几个常用的命令:
检查 Cargo 的版本:cargo --version新建项目:cargo new项目构建:cargo build运行项目:cargo run项目检查:cargo check:该命令确保项目可以编译,但不生成可执行文件,速度比 c--argo build 更快发布:cargo build --release 在运行 cargo build 后,Rust 会把编译后的二进制文件放在 target/debug 文件夹下。
一 什么是openGauss openGauss是一款开源的关系型数据库管理系统,它具有多核高性能、全链路安全性、智能运维等企业级特性。 openGauss内核早期源自开源数据库PostgreSQL,融合了华为在数据库领域多年的内核经验,在架构、事务、存储引擎、优化器及ARM架构上进行了适配与优化。作为一个开源数据库,期望与广泛的开发者共同构建一个多元化技术的开源数据库社区。
openGauss的安装 传统安装 https://gitee.com/opengauss/openGauss-server?_from=gitee_search
在上面的官网中给出了传统的安装步骤,具体操作比较繁琐,需要设置配置文件、准备安装环境等等,不适合想快速入手高斯数据库的同学,当然如果有同学想了解传统的安装步骤,我后续会出一个文章专门详细说明这个传统安装步骤。
docker安装 环境准备 第一步当然是要准备一台Linux服务器啦,这里作者用虚拟机来演示具体的安装,下面贴出系统的参数以供参考。
[root@192 ~]# cat /proc/version Linux version 3.10.0-1160.76.1.el7.x86_64 (mockbuild@kbuilder.bsys.centos.org) (gcc version 4.8.5 20150623 (Red Hat 4.8.5-44) (GCC) ) #1 SMP Wed Aug 10 16:21:17 UTC 2022 [root@192 ~]# cat /etc/centos-release CentOS Linux release 7.9.2009 (Core) 安装基础依赖
yum install gcc zlib* openssl* -y docker安装 docker安装
yum install docker docker版本检查与启动
[root@192 ~]# docker -v Docker version 20.10.18, build b40c2f6 [root@192 ~]# systemctl start docker 设置开机自启
背景介绍 墨西哥湾流和黑潮分别是北半球两支强大的西边界流,墨西哥湾流的流速还要强于黑潮,也是温盐环流的重要组成部分。
引入涡度的概念,将涡度分为两个部分:
1、行星涡度,记为 f f f,与地球自转有关,纬度越高行星涡度越大,2、相对涡度,记为 ζ \zeta ζ,逆时针旋转为正涡度,顺时针旋转为负涡度。 位涡守恒:正压理想流体满足 d d t ( f + ζ H ) = 0 \frac{d}{d t}\left(\frac{f+\zeta}{H}\right)=0 dtd(Hf+ζ)=0,其中 H H H表示流体柱高度。
在流体柱高度即水深不变的情况下,简化为 d f d t + d ζ d t = 0 \frac{d f}{d t}+\frac{d \zeta}{d t}=0 dtdf+dtdζ=0
赤道盛行东风,中纬盛行西风,风生环流顺时针旋转,相对涡度为负。
在东边界,洋流向低纬,行星涡度 f f f减小,则风生环流涡度 ζ \zeta ζ必须增加,产生一个逆时针的环流抵消原本顺时针的环流,故东边界洋流速度减小。
在西边界,洋流向高纬,行星涡度 f f f增大,则风生环流涡度 ζ \zeta ζ必须减小,产生一个顺时针的环流增强原本顺时针的环流,故西边界洋流速度增大。
旋转效应下独有的西边界流强化的现象,又由于连续方程被约束在狭窄的通道内,于是就有了这么一条流速可高达2 m/s,相当于全世界河流总流量x十倍的物质流。
海表面温度的快照 上图为墨西哥湾海表面温度的快照
# This script takes sea-surface temperature data from Oct 27th 2017 and makes a nice plot.
内容来自李宏毅-2021机器学习
##先说结论:同一个模型,大batch结果往往会较差。
上图中,横轴代表batch size,从左到右越来越大;纵轴代表准确率acc,越往上正确率越来越高。
在观察validation上的结果时,会发现随着batch size增加,acc结果越来越差。
但这个现象并不是overfitting,因为在training上的acc结果也是随着batch size增加而变差。
结论:同一个模型,大的batch size往往会带来比较差的结果。
由于使用的是同一个模型,可以排除model bias的问题,而是optimization的问题。使用大的batch size时,optimization可能会有问题,小的batch size的结果可能是比较好的。
##为什么小batch有更好的表现,有噪声的参数更新对训练更有帮助?
一种解释:
如果使用的是full match(上图左1),在更新参数时会沿着一个Loss函数来更新参数。可能会陷入一个局部极小值点或者是鞍部点,梯度变为0,不会再进行参数的更新。
但是如果使用small batch(上如左2),训练时每个batch会根据自己的Loss函数来算梯度,不同的batch间的Loss函数是有差异的。第一个batch用L1算梯度,第二个batch用L2算梯度。假如用L1在某一点算出来梯度为0卡住了,但在L2的这一点算出来梯度很可能并不是0,所以还是有办法让梯度不为0,进而可以继续训练,让Loss变小。
所以有噪声的参数更新对训练更有帮助。
小batch 和大batch有各自的特点,根据需要来选择合适的参数。
1,在vue项目中突然遇到“运行vue文件时发现npm无法加载文件”
2,这是因为你的电脑上没有权限,需要运行管理员身份进行授权
3, 在开始的搜索框内输入windows powerShell 右键它打开管理员终端
然后按着代码打,便可以打开权限了
1.背景 笔者删表喜欢直接drop table,最近遇到drop表时,表正在被写着或者其他操作,从而导致删表失败,
最好的方式是先下线表,让对表的操作停止掉,然后再去删除。
2.说明 下线:DETACH TABLE|VIEW|DICTIONARY [IF EXISTS] [db.]name [ON CLUSTER cluster] [PERMANENTLY]
上线:ATTACH TABLE|VIEW|DICTIONARY [IF EXISTS] [db.]name [ON CLUSTER cluster] [PERMANENTLY]
官网的翻译:
分离不会删除表、物化视图或字典的数据或元数据。如果一个实体没有被永久分离,在下一次服务器启动时,服务器将读取元数据并再次召回表/视图/字典。如果一个实体被永久分离,将不会自动召回。 无论表或字典是否被永久分离,在这两种情况下都可以使用ATTACH查询重新连接它们。系统日志表也可以附加到后面(例如query_log, text_log等)。无法重新连接其他系统表。在下次服务器启动时,服务器将再次召回这些表。 ATTACH物化视图不能使用短语法(没有SELECT),但是可以使用ATTACH TABLE查询附加它。 注意,不能永久分离已经分离的表(临时)。但你可以把它接回去,然后再永久分离。 你也不能DROP被分离的表,或者用与被分离的表同名的CREATE table,或者用RENAME table查询将它替换为另一个表。 3.操作 # 测试表 CREATE TABLE IF NOT EXISTS test.test_uid_20220803 ( `uid` Int32, `deviceId` String ) ENGINE = ReplicatedMergeTree('/clickhouse/test/tables/{shard}/test_uid_20220803', '{replica}') PARTITION BY uid ORDER BY uid SETTINGS index_granularity = 8192; # 测试数据 INSERT INTO test.test_uid_20220803 SELECT 1, 'abcd'; # 下线表 detach table test.
循环队列不同于非循环队列和链表,其操作不需要定义next指针,只是单纯的定义一个固定的结构体变量,此结构体中的数组变量已经可以包含全部的已定义的队列长度.(如果采用循环链表来实现队列也可行,但队列会成为可变长的) 首先需要两个头文件#include <stdio.h>和#include <stdlib.h> 1.定义结构体 typedef struct Queue //定义结构体 { int data[MAXSIZE];//表示结构体的长度 int front;//队列头 int rear;//队列尾 }Queue; 2.初始化队列 Queue *initQueue() { Queue *Q = (Queue *)malloc(sizeof(Queue));//申请空间 Q->front = 0;//新的队列初试没有元素,因此队头和队尾都是0 Q->rear = 0; return Q; } 3.判满 int isFULL(Queue *Q) { if((Q->rear+1)%MAXSIZE==Q->front)//为了和空队进行区别 return 1; else return 0; } 4.入队 int enQueue(Queue *Q,int data) { if(isFULL(Q))//先判断队列是否为满 return 0; else { Q->data[Q->rear] = data;//将元素写入data数组中,具体写在那里看队尾 Q->rear = (Q->rear+1)%MAXSIZE;//除以MAXSIZE是防止溢出 return 1; } } 5.判空 int isEmpty(Queue *Q) { if(Q->front==Q->rear)//与上面的判满做区别 return 0; else return 1; } 6.
文章目录 一句话目标标识符:Java内存小知识:类与对象编程语言的几个发展阶段类成员变量的赋值问题 对象的创建与构造方法对象的内存模型 类与程序的基本结构参数传值对象的组合实例成员与类成员static关键字static关键字的用途static方法 方法重载this关键字包import语句访问权限对象数组小结 子类与继承子类与父类人类属性:行为: 子类的继承性子类与对象成员变量的隐藏和方法重写继承中成员变量的访问特点:方法重写总结: 重写与重载方法的重写规则重载规则: super关键字(super≈父类)final关键字(不变)Java 转型问题对象的上转型对象对象的下转型对象 构造器继承与多态abstract类和abstract方法 接口与实现接口接口的特点接口定义与实现接口定义接口实现 abstract类与接口的比较 字符串与ArrayList统计字符串数组拼接字符串IDEA快捷键 内部类内部类小结 阶段项目常用API常见算法集合进阶异常类文件IO流多线程与并发Java最难的一部分:多线程与高并发 一句话目标 对于一些比较复杂或者第一眼看上去不太好理解的概念,我信奉的观念就是,用一句话把它解释清楚,而且是用很通俗的语言,当然了,如果你已经能够很好的理解了,还是建议用不是那么正式但又不是很通俗的语言解释。
所以我接下来我会用一句话这个标签来解释这些难懂的概念。
C/C++与Java编译运行过程对比:
数据溢出:
byte是一个字节的数据类型,所以它的表示范围是-128~127
当我们此范围之外的数赋值给byte类型变量时,会发生数据溢出,溢出的方式就是
比127大的数要接着顺时针转,假如是128,比127大1,那就是移动一个数变成-128假如是129,比127大2,那就是移动两个数变成-127,这样以此类推;
比-128小的数要接着逆时针,假如是-129,比-128小1,那就移动一个数变成127,假如是-130,比-128小2,那就是移动两个数变成126,其他数据类型也以此类推。
标识符: 大驼峰适用于类名,HelloWorld
小驼峰适用于方法名,变量名,helloWorld
四类八种数据类型:
数值拆分练习:
获取任意一个数上每一位数。
个位:数字 % 10
十位:数字 / 10 % 10
百位:数字 / 100 % 10
千位:数字 / 1000 % 10
。。。以此类推。。。
取值范围从小到大的关系:byte short int long float double(小的跟大的运算,小的会提成为大的的类型)
Java内存小知识: 类与对象 编程语言的几个发展阶段 面向机器语言 ——》面向过程语言——》面向对象语言
(二进制、汇编)——》C语言 ——》Java语言
在面向过程编程中我们是以“方法”为主体的
而面向对象编程汇总我们是以“对象”为主体的
在面向对象语言的学习过程中,一个简单的理念就是,需要完成某种任务的时候,我们首先想到是谁去完成(对象);提到某个数据的时候,想到是谁的数据,这样也更符合我们日常生活中的描述。
类 类?类是干什么的呢?