一、准备阶段 1、在pubilic文件夹下的index.html文件中的head标签中引入Luckysheet的cdn文件
<link rel='stylesheet' href='https://cdn.jsdelivr.net/npm/luckysheet@latest/dist/plugins/css/pluginsCss.css' /> <link rel='stylesheet' href='https://cdn.jsdelivr.net/npm/luckysheet@latest/dist/plugins/plugins.css' /> <link rel='stylesheet' href='https://cdn.jsdelivr.net/npm/luckysheet@latest/dist/css/luckysheet.css' /> <link rel='stylesheet' href='https://cdn.jsdelivr.net/npm/luckysheet@latest/dist/assets/iconfont/iconfont.css' /> <script src="https://cdn.jsdelivr.net/npm/luckysheet@latest/dist/plugins/js/plugin.js"></script> <script src="https://cdn.jsdelivr.net/npm/luckysheet@latest/dist/luckysheet.umd.js"></script> 2、安装三个依赖(exceljs和 file-saver用来导出,luckyexcel用来处理导入数据 )
npm i -S exceljs file-saver luckyexcel 3、创建一个js文件export.js
import FileSaver from 'file-saver' const Excel = require('exceljs') var setMerge = function (luckyMerge = {}, worksheet) { const mergearr = Object.values(luckyMerge) mergearr.forEach(function (elem) { // elem格式:{r: 0, c: 0, rs: 1, cs: 2} // 按开始行,开始列,结束行,结束列合并(相当于 K10:M12) worksheet.mergeCells( elem.
以2000-2018年,每三年一个大刻度,中间每一年为小刻度为例:
(1)打开次刻度:set(gca,'xminortick','on');
(2)设置主刻度:set(gca,'XTick',(2000:3:2018));
(3)设置次刻度:ax = gca;
ax.XAxis.MinorTickValues = 2000:1:2018;
self class Animal(object):
def eat(self,food):
print(f"正在{food}“)
def sleep(self):
self.eat(“狗粮”)
print(“正在玩耍”)
dog = Animal()
dog.eat(“粮”)
这里
def 关键字
eat方法名 全部小写
self 实例对象 就是指的dog
args 参数
self 实例对象 就是指的dog
在sleep实现了类函数内部的嵌套
当在类外面调用eat时候使用
dog = Animal()
dog.eat(“粮”)
在类方法内调用也需要
dog.eat(“粮”)
但是这里是
self.eat(“狗粮”)
是因为在没有实例化对象之前不知道是dog,所以用self来替代
Stereo
source ~/vfusion/devel/setup.bash roslaunch vins vins_rviz.launch source ~/vfusion/devel/setup.bash rosrun loop_fusion loop_fusion_node ~/vfusion/src/VINS-Fusion/kitti_odom/kitti_config00-02.yaml source ~/vfusion/devel/setup.bash rosrun vins kitti_odom_test ~/vfusion/src/VINS-Fusion/config/kitti_odom/kitti_config00-02.yaml ~/dataset/kitti/odm/sequences/00 Stereo+GPS
VINS-Fusion的config文件夹中支持kitti stereo+gps数据的只有09-30和10-03的数据集,在下载数据集的时候要注意一下,下矫正后的数据集。
修改.yaml文件中的储存路径
修改kitti-gps-test中结果文件储存格式,去掉每行最后的空格,否则evo评估时会报格式不统一
打开三个terminal
source ~/vfusion/devel/setup.bash roslaunch vins vins_rviz.launch source ~/vfusion/devel/setup.bash rosrun vins kitti_gps_test ~/vfusion/src/VINS-Fusion/config/kitti_raw/kitti_09_30_config.yaml ~/dataset/kitti/sync/2011_09_30_drive_0027_sync/ source ~/vfusion/devel/setup.bash rosrun global_fusion global_fusion_node ./Examples/Stereo-Inertial/stereo_inertial_kitti ./Vocabulary/ORBvoc.txt ./Examples/Stereo-Inertial/KITTI05-10.yaml ../slam_data/kitti/sequences/07 ../slam_data/kitti/IMU/2011_09_30/2011_09_30_drive_0027_extract/oxts/data ../slam_data/kitti/IMU/NewTimestamps07.txt
【问题描述】
定义一个有理数类Rational。该类存放分数形式的有理数。要求如下:
(1)定义私有变量x和y分别存放分子和分母。
(2)定义带默认参数值的构造函数,默认有理数为0(分子为0,分母为1)。
(3)定义成员函数Add,Sub,Mul和Div,分别用来完成两个有理数的加、减、乘、除运算。
(4)以X/Y形式打印有理数的Print函数。
(5)以浮点数形式打印有理数的PrintFloat函数。(说明:分数不需以最简形式存放,四则运算后亦不需约分。主函数中需要你写出输出语句,其中加法和乘法输出X/Y形式结果,减法和除法输出浮点数形式结果,两个分数的分子分母皆由键盘输入)
#include<iostream> using namespace std; class Rational { int x,y; //x-分子; y-分母 public: Rational(int X = 0, int Y = 1) { x = X; y = Y; } void Add(Rational r1,Rational r2) { x = r1.x * r2.y + r1.y * r2.x; y = r1.y * r2.y; Print(); } void Sub(Rational r1,Rational r2) { x = r1.x * r2.y - r1.y * r2.
问题描述: 最近安装了zabbix设置了一些开机启动服务
例如:zabbix-server.service,httpd.service,mariadb.service,或者系统的firework.service
想知道这些服务开机有没有自启服务,查询了一下网上都讲到chkconfig,但是centos7已不用chkconfig了
解决方法: 使用 systemctl list-unit-files 可以查看启动项
左边是服务名称,右边是状态,enabled是开机启动,disabled是开机不启动
过滤查询可以systemctl list-unit-files | grep enable 过滤查看启动项如下
过滤查询可以systemctl list-unit-files | grep zabbix 过滤查看某服务名如下
参考:Centos7查询开机启动项服务 - Tse先生 - 博客园
#include <stdlib.h> #include <stdio.h> #include <string.h> char* filename_suffix(char* filename, char delimit) { int len=strlen(filename); int i; for(i=0; i<len; i++) { if(filename[len-i] == delimit) { return (filename+len-i); } } return NULL; } void main( void ) { char* path = "/home/lhb/share/data/1/10600000.bin"; char *p=strrchr(path, '.');//倒序查找字符出现的位置,返回地址 printf( "p=%p, filename suffix: %s\n", p,p+1 ); p=filename_suffix(path, '.'); if(NULL != p) { printf("def:filename suffix:%s\n",p+1); } else { printf("no suffix\n"); } char* path1 = "/home/lhb/share/data/1/10600000"; char *p1=strrchr(path, '.
el-select添加表单校验步骤
1.在el-form中添加rules
<el-form :inline="true" :rules="formRules" :model="dataForm"> </el-from> 2.在data中声明formRules,定义要校验的字段
这里的mrpVersion一定要跟el-form-item中prop字段对应
data() { return { dataForm: { formRules: { mrpVersion: [ {required: true, message: 'MRP版本不能为空', trigger: ['blur']} ] }, }; }, <el-form-item :label="$t('common.mrpVersionSelect')" :required="true" prop="mrpVersion"> <el-select ref="mrpVersionSelect" v-model="dataForm.mrpVersion" :remote-method="getMrpVersionOptions" clearable/> </el-form-item> 这个时候发现添加的校验不起作用
找了很多,最后发现就是一个很简单的设置问题
把rules里的这条改成:
{required: true, message: 'MRP版本不能为空', trigger: ['blur', 'change']} 加一个change之后,就起作用了。如图 完美解决
真的太难了,记录整理一下。
【10分】A. 实验7-1-1 简化的插入排序
题目描述
本题要求编写程序,将一个给定的整数插到原本有序的整数序列中,使结果序列仍然有序。
输入
输入在第一行先给出非负整数N(<=10);第二行给出N个从小到大排好顺序的整数;第三行给出一个整数X。
输出
在一行内输出将X插入后仍然从小到大有序的整数序列,每个数字后面有一个空格
输入样例
5
1 2 4 5 7
3
输出样例
1 2 3 4 5 7
#include<stdio.h> int main() { int n=0; int t=0; int a[11]={0}; //先将数字存入数组a之中 scanf("%d",&n); for(int i=0;i<n;i++) { scanf("%d",&a[i]); } //再将要插入的数字存到数组a最后一个元素中 scanf("%d",&a[n]); //选择排序:将a[i]与数组a后面的各个元素一一对比,将较小的数调到左边,随着i的增加实现移到下一位,连续将较小的数调到左边 for(int i=0;i<=n;i++) { for(int j=i+1;j<=n+1;j++) { if(a[i]>a[j]) { t=a[i]; a[i]=a[j]; a[j]=t; } } } //遍历数组并输出 for(int k=0;k<=n;k++) { printf("%d ",a[k]); } return 0; } 【20分】B.
【题目描述】
如果一个字符串可以由某个长度为k的字符串重复多次得到,我们说该串以k为周期。 例如,abcabcabcabc以3为周期(注意它也以6和12为周期)。
输入一个长度不超过80的串,输出它的最小周期。
----------------------------------------------------------------------
非常简单好玩的题,我快要爱上string了,比char *;char [];好用太多了
#include <iostream> using namespace std; int main() { string s; int i = 0; getline(cin,s); char c = s[0]; //周期串的第一个字符保存在c中 int n = s.length(); for(i = 1; i < n; i++) //注:从1开始找, { //因为它是周期串,只要确定首尾相等即可 if(s[i]==c && s[i-1]==s[2*i-1]) break; } cout<<i<<endl; return 0; }
创建项目
将我们上篇文章中的分组的三个项目,拷贝一份修改名称及服务名称
没有分区的情况下演示
发送多条消息查看效果
@RunWith(SpringRunner.class)
@SpringBootTest(classes=StreamSenderStart.class)
public class StreamTest {
@Autowired
private ISendeService sendService;
@Test
public void testStream(){
Product p = new Product(999, “stream test …999”);
// 将需要发送的消息封装为Message对象
Message message = MessageBuilder
.withPayload§
.build();
for (int i = 0; i < 10; i++) {
// 发送多条消息到队列中
sendService.send().send(message );
}
}
}
10条消息被随机的分散到了两个消费者中:
我们可以看到A中6条消息,B中4条消息,而且这是随机的,下次执行的结果肯定不一样。
分区
1.发送者中配置 spring.application.name=stream-partition-sender
server.port=9060
#设置服务注册中心地址,指向另一个注册中心
eureka.client.serviceUrl.defaultZone=http://dpb:123456@eureka1:8761/eureka/,http://dpb:123456@eureka2:8761/eureka/
#rebbitmq 链接信息
spring.rabbitmq.host=192.168.88.150
spring.rabbitmq.port=5672
spring.rabbitmq.username=dpb
spring.rabbitmq.password=123
spring.rabbitmq.virtualHost=/
对应 MQ 是 exchange outputProduct自定义的信息 spring.
环境变量 .env文件 访问
现在的前端项目都是用 Node 来作为辅助开发工具,而 process 是 Node.js 中的 一个全局变量,提供来有关当前 Node.js 进程的信息并对其进行控制。而 process 中的 env 则是返回包含用户环境的对象。
通俗点说,就是可以通过 process.env 拿到当前项目运行环境的信息。
console.log("环境变量:",process.env) .env文件
VUE_APP_URL = 127.0.0.1:9088 从控制台可以看到,process.env是一个对象,BASE_URL 和 NODE_ENV属性是默认属性,比较常用的是NODE_ENV 用来判断当前的环境
文件名
项目中env文件挺多的,常见的有.env、.env.local、.env.development 、.env.production
.env 在所有环境中被载入.env.local 在所有的环境中被载入,但会被git忽略.env.[mode] 在指定的环境被载入,比如.env.development 开发环境中被载入.env.[mode].local 在指定的模式中被载入,但是会被git忽略 一般开发时修改一些东西,比如ajax请求地址,在.env.local里进行修改
定义属性,及注释
属性名必须以VUE_APP_开头,比如 VUE_APP_PROJECT_NAME
注释必须是 # 开头,后面跟要注释的内容
文件加载
这么多的文件,那项目启动时会加载哪一个文件?这个与启动命令有关,可以看一下package.json文件
"scripts": { "serve": "vue-cli-service serve", "serve-prod": "vue-cli-service serve --mode prod", "build": "vue-cli-service build", "build-prod": "vue-cli-service build --mode prod", }, 当你运行npm run serve 会加载.
Redis 客户端 概述 Redis 是一种C/S 架构的分布式缓存数据库,它有自带的命令行客户端,也有对应的Java或其它语言客户端,可以在这些客户端中通过一些API对redis进行读写操作。
准备工作 第一步:创建工程。
创建maven父工程,例如03-redis,并在此工程下创建两个子工程,一个为sca-jedis,一个为sca-tempate,例如:
第二步:添加父工程依赖
修改父工程pom.xml文件,添加编译配置
<build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.8.1</version> <configuration> <source>8</source> <target>8</target> </configuration> </plugin> </plugins> </build> Jedis的基本应用 简介 Jedis是Java中操作redis的一个客户端,类似通过jdbc访问mysql数据库。
准备工作 在sca-jedis 工程添加如下依赖
<dependencies> <dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> <version>3.5.2</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> <scope>test</scope> </dependency> <dependency> <groupId>com.google.code.gson</groupId> <artifactId>gson</artifactId> <version>2.8.6</version> </dependency> <dependencies> Jeids基本应用实践 在Jedis工程中的src/test/java目录创建单元测类,例如:
package com.jt; import org.junit.Test; import redis.clients.jedis.Jedis; import java.util.concurrent.TimeUnit; public class JedisTests { @Test public void testGetConnection(){ //假如不能连通,要注释掉redis.conf中 bind 127.0.0.1, //并将protected-mode的值修改为no,然后重启redis再试 Jedis jedis=new Jedis("
d2lzh_pytorch包报错问题解决 报错: ModuleNotFoundError: No module named ‘d2lzh_pytorch’
d2lzh_pytorch包中找不到名为’xxx’的属性/函数,如
module 'd2lzh_pytorch' has no attribute 'use_svg_display()' module 'd2lzh_pytorch' has no attribute 'load_data_fashion_mnist' 解决方法: 方法1、放到Jupyter工作目录 下载d2lzh_pytorch文件夹,将其放置到当前Jupyter的工作目录的根目录中即可 方法2、放到虚拟环境的包安装目录 下载d2lzh_pytorch文件夹,将其放置到anaconda虚拟环境的包路径下,如:Anaconda/envs/pytorch(虚拟环境名)/Lib/site-packages/注释代码中的sys.path.append("…"),使其从site-packages中找到d2lzh_pytorch包并引入
方法一:直接从谷歌应用商店安装(vue官网有给链接,我这边不能翻墙,我没用)
https://v3.cn.vuejs.org/guide/installation.html#vue-devtools
方法二:从github上下载安装
1、从github上下载安装包(链接:https://hub.fastgit.org/vuejs/devtools/tree/main)
不知道为啥我直接下载下来的,运行npn run build 会报错,找了半天没找到解决办法
然后我从这里点进去下载的压缩包,解压之后,运行npm run build 就可以了(好奇怪,不知道是我哪里没有搞好,知道的大佬可以指正一下,谢谢!)
2、把下载下来的压缩包解压,点进去安装依赖执行npm install(我是用yarn安装的,npm太慢),安装完成之后执行npm run build
3、然后把插件添加进浏览器就行了,添加选中的文件路劲:
添加成功以后遇到的问题:
Vue.js is detected on this page.
Devtools inspection is not available because it's in production mode or explicitly disabled by the author.
解决办法:在入口文件(main.ts)添加
这样就成功了,打开控制台,发现工具栏没有vue,(按F5+F12,我是这样的),就出来了
在vue2就可以用了,但是在vue3不能用
vue3 要下载对应版本的bate版才行(但是我还没自己安装打包成功,导入都报错,搞不好,以后搞好了再更新吧,我用的别人封装好的ctx文件),文件链接:Vue.js Devtools_6.0.0.20_chrome扩展插件下载_极简插件
添加成功了
而且vue3对应的devtools也还在测试中
Discovery功能:
对于注册进Eureka里面的微服务,可以通过服务发现来获取服务的信息。此处以payment8001为例。payment8002与payment8001一样。
1. 修改cloud-prodvider-payment8001的controller。 @RestController @Slf4j public class PaymentController { @Autowired private PaymentServices paymentServices; @Value("${server.port}") private String serverPort; @Autowired private DiscoveryClient discoveryClient;//服务发现,就是展示服务的一些信息 @GetMapping("/payment/get/{id}") public CommonResult<Payment> getPaymentById(@PathVariable("id") Long id) { Payment payment=paymentServices.getPaymentById(id); log.info("********查询结果*********:"+payment); if(payment != null) { return new CommonResult<>(200,"查询数据成功,serverport:"+serverPort,payment); }else { return new CommonResult<>(444,"查询到对应的记录,查询ID:"+id); } } @PostMapping("/payment/create") public CommonResult addPayment(@RequestBody Payment payment) { int result = paymentServices.addPayment(payment); log.info("******插入结果:"+result); if(result>0) { return new CommonResult(200,"插入成功"); }else { return new CommonResult(445,"插入失败"); } } @GetMapping("
css selector 的高级用法 CSS(Casading Style Sheets)层叠样式表。一种用来表现HTML或者XML等文件样式的语言。css选择器是浏览器用来选择元素,selenium 也要选择元素,可以使用css选择器来选择Web元素。
定位元素的注意事项:
1、找到等待定位的元素的唯一属性
2、如果该元素没有唯一属性,则先找到能够给被唯一定位的父元素/子元素/相邻元素,再使用 “>” 、“ ”、“+” 等辅助定位要定位的元素。
3、不要使用随机唯一属性,属性是会发生改变。 ID = “Dev45” Dev46
1、ID选择器 通过 # 来定义,通过元素的ID属性来定位。 格式: #id属性值 id选择器来定位元素 格式: #id属性值 通过 # 来定义的
print (driver.find_element_by_css_selector("#pLabel").get_attribute("id")) 2、class选择器(类选择器) 使用 . (点)来定义 格式: .class属性的值 class 选择器 使用 . (点)来定义 格式: .class属性的值
driver.find_element_by_css_selector(".RedChamber").send_keys("class 选择器 使用 . (点)来定义 格式: .class属性的值") 如果class属性值有空格,则可以使用如下方法来定位:
方法一:
可以使用 点 替换掉所有的空格(例如: .Dream.of.the.Red.Chamber)
方法二:
可以截取一部分值,然后在截取的部分值之前加上 . (点) (例如: .Dream) class属性值有空格
driver.find_element_by_css_selector(".Dream.of.the.Red.Chamber").send_keys("class属性值有空格") driver.find_element_by_css_selector(".Dream").send_keys("截取部分值在前面加上点来定位") 3、标签选择器 通过 标签的标签名 来定位元素 标签选择器 通过标签的标签名来定位元素
先放解决方法:
.select2-container--open .select2-dropdown{left:0;z-index:999999999;}
只需添加一个样式即可
原理大概就是跟图层有关,具体请自行去select2和弹出层插件官网了解。
我用的是layer.open和dialog弹窗插件,均能解决。
并不保证能解决你们的问题,仅供参考,不喜勿喷。
转自:Layer弹窗和 jquery select2 兼容问题【附源码】_wx5bffdbaf11b62_51CTO博客
大家好,我是一碗周,一个不想被喝(内卷)的前端。如果写的文章有幸可以得到你的青睐,万分有幸~
这是【从头学前端】系列文章的第三十六篇-《表单》
写在前面 本篇文章将来介绍HTML中原生表单的内容。
HTML表单主要作用你是用于收集不同类型的用户输入并与服务端进行交互。
HTML表单是由一个或多个小部件组成的。这些小部件可以是文本字段(单行或多行)、选择框、按钮、复选框或单选按钮。大多数情况下,这些小部件与描述其目的的标签配对——正确实现的标签能够清楚地指示视力正常的用户和盲人用户输入表单输入的内容。
HTML表单和常规HTML文档的主要区别在于,大多数情况下,表单收集的数据被发送到web服务器。在这种情况下,您需要设置一个web服务器来接收和处理数据。
目前表单可以完成的工作包括但不限于下面这些,例如:
网站的用户注册和登录功能
搜索功能中的搜索框
写博客的发布功能
我们可以将表单划分为两个部分:
<form>元素:表单容器的元素
表单组件元素:真正用来接收用户输入的信息的元素,包括但不限于<input>、<textarea>等。
form元素 <form>元素定义HTML表单,HTML表单用于收集用户输入,与服务端进行通信。
该元素有几个常用的属性,具体如下:
action属性:设置提交表单的服务端地址
method属性:设置提交表单时的请求类型
- GET:默认方式,发送敏感信息时不可使用!GET最适合短,非敏感,一定量数据,因为它有大小限制太。
- POST:如果表单数据包含敏感或个人信息,则总是使用POST。POST方法在页地址字段中不显示提交的表单数据;POST没有大小限制,可以用来发送大量的数据。
enctype属性:设置表单提交时的MIME类型.(主要可实现文件上传) input元素 <input>是一个空元素,最重要的表单元素。该元素的功能比较强大,常用属性有如下几个:
属性描述type设置输入框的类型value设置输入框的默认值disabled设置输入框是否可用(仅设置属性名)readonly设置输入框是否为只读autofocus设置输入框自动获取焦点placeholder设置输入框的默认提示信息(代替value向用户展示信息) type属性可用的值如下,每一个值都代表了一个类型的组件:
值描述值描述text单行文本框password密码框radio单选框checkbox复选框button按钮reset重置按钮submit提交按钮file文件域hidden隐藏域email电子邮件urlURL地址search搜索框date日期控件week星期控件month月控件color颜色控件number数字控件 如下代码展示了部分组件的使用,代码如下:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>input元素</title> </head> <body> <form method="GET" action="#"> <!-- disabled 属性,设置为禁用的 readonly 属性,设置为只读 autofocus 属性,自动获取焦点 placeholder 属性,设置提示信息 --> <input type="text" value="我是一段文本"> <br> <!-- 单选框或者复选框要为其定义相同的name属性,表示为一组 checked 属性:表示当前选框默认被选中 --> <input type="
直接上工具类方法了,方法是参考网上其它同学所写,在此只是作为一个记录
import java.text.SimpleDateFormat; import java.util.Calendar; import java.util.GregorianCalendar; import java.util.Hashtable; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * 身份证校验类型 */ public class IdCardNoUtils { // 新身份证长度18 private static final int IDCARDNO_LENGTH_NEW = 18; // 老身份证长度15 private static final int IDCARDNO_LENGTH_OLD = 15; private static final int IDCARDNO_SUB_LENGTH = 17; private static final int MAX_YEAR = 100; private static final int MAX_MONTH = 12; private static final int MAX_DAY = 31; private static final String CHAR = "
Wireshark实验
一.数据链路层 准备 请自行查找或使用参考资料,了解 Wireshark 的基本使用:
1.选择对哪块网卡进行数据包捕获
2.开始/停止捕获
3.了解 Wireshark 主要窗口区域
4.设置数据包的过滤
5.跟踪数据流
实作一 熟悉 Ethernet 帧结构 选择WLAN
打开cmd进行抓包
根据ip进行搜索
与www.baidu.com的数据交换:
使用 Wireshark 任意进行抓包,熟悉 Ethernet 帧的结构,如:目的 MAC、源 MAC、类型、字段等:
问题
你会发现 Wireshark 展现给我们的帧中没有校验字段,请了解一下原因。
Wireshark 抓包前,物理层网卡已经进行过校验,正确时才会进行下一步操作,当我们使用Wireshark进行抓包,抓到的为校验后的包,所以我么看到的帧中是没有校验字段。
实作二 了解子网内/外通信时的 MAC 地址 1.ping 你旁边的计算机(同一子网),同时用 Wireshark 抓这些包(可使用 icmp 关键字进行过滤以利于分析),记录一下发出帧的目的 MAC 地址以及返回帧的源 MAC 地址是多少?这个 MAC 地址是谁的?
略。
2.然后 ping qige.io (或者本子网外的主机都可以),同时用 Wireshark 抓这些包(可 icmp 过滤),记录一下发出帧的目的 MAC 地址以及返回帧的源 MAC 地址是多少?这个 MAC 地址是谁的?
发出帧的目的 MAC 地址以及返回帧的源 MAC 地址是c2:68:e6:04:6d:53,这个MAC地址是本机的网关MAC地址
租用高防服务器,可以通过防火墙,数据监测、牵引系统等对流量性攻击进行有效削弱,从而起到防御作用。
高防服务器主要是针对DDos、CC流量攻击而出现的。由于当前互联网中存在很多黑客攻击,最为普遍就是DDos攻击,对目标网络或者服务器进行资源占取,导致服务器出现拒绝式服务。租用高防服务器,可以通过防火墙,数据监测、牵引系统等对流量性攻击进行有效削弱,从而起到防御作用。那么,高防服务器如此有用,又应当如何挑选呢? 下面壹基比小喻就来给你们一一讲解。
首先,要了解高防服务器是如何进行防护的。
目前,常见的网站攻击类型主要为流量攻击,就是我们常说的DDOS攻击,最基本的DDoS攻击就是利用合理的服务请求来占用过多的服务资源,从而使合法用户无法得到服务的响应。CC攻击,也是流量攻击的一种,CC就是模拟多个用户不停地进行访问那些需要大量数据操作的页面,造成服务器资源的浪费,CPU长时间处于100%,永远有处理不完的连接直至网络拥塞、正常访问被迫中止。
流量攻击是对资源上的占取,如果资源充足时,那么这类攻击就不会对网络有着很大的影响,如果服务器的带宽不充足,那么受到攻击后的影响会很明显,这就要求高防服务器要有足够充足的带宽。另外,在租用高防服务器后,服务商会对流量攻击的防御有一个硬防范围,在这个范围下,高防服务器的牵引系统会对进入服务器的流量有一个识别功能,即使受到网络攻击时,在保护范围内的用户的网络都不会有什么影响,如果攻击流量超过保护的范围时,就会对IP进行暂时性的牵引。有需要求联系TG:TW_001
因此,我们就可以清楚选购高防服务器主要的点:带宽和”范围”大小。
文章目录 预备知识与前期工作ARS_40X源码架构雷达数据格式及流向数据流向图雷达数据格式socket_can通信格式(以雷达配置帧通信为例)关键数据介绍及格式说明cluster数据帧can_messages 关键参数:topic与服务SubscriptionServices 关键类与继承关系二次开发流程展示:TODO下一步工作 预备知识与前期工作 前期工作链接雷达手册链接中文注释版本源码(未添加完成滤波器,暂时不能跑,仅提供注释参考) 最近阅读了前段时间刚部署到电脑上的源码,作者的源码是基于c++的,最近穿插着复习c++并完成了雷达的源码阅读,对源码关键部分进行了注释,增加了可读性。另外此架构上进行修改,完成了雷达滤波的初步开发,下一步部署到电脑进行效果验证。
首先,雷达检测有两种方式,一种是cluster模式,即点云模式,将所有的扫描到的点云经过内部滤波后,不加处理的发布出来,具体效果可见我前一篇博客。这种状态下生成的地图可以看作点云图,优点是点云信息密集,利于二次开发。缺点也很明显:噪声大,一个物体上存在多个雷达点,分辨起来较为困难。
另一种模式就是object模式,其中将点云进一步处理,以RCS等参数判断物体的类别并输出。这种模式的优点是:可视化程度高,易于分辨,同时噪声点少。缺点是:难以进行二次开发,对的点云特征的利用度较低。
ARS_40X源码架构 源码架构 如图所示,源码主要分为三个功能部分:
与雷达接口socket_can的通信与数据处理、发布等任务;在第一个的基础上,实现与rviz“通信”的可视化任务;同样在第一个人物的基础上,实现object mode下的目标姿态发布任务; 图中源码文件结构如图所示,基本上由hpp文件定义通信的数据基本格式和所有类的初始化,然后再src文件中对所有类进行初始化,其中./src/ros文件下的ros文件初始化各类上层接口类,其功能包含利用service服务将更改的参数传递给socket_can接口,可视化点云等上层服务。
雷达数据格式及流向 数据流向图 源码数据流向图 如上图所示,soket_can负责与毫米波雷达的通信,包括对数据的封装以及读取等功能,供ars_40x_can.cpp使用,包括建立的通信类来实现与socket_can的通信、并负责更新所有数据,,实现的功能有:receive_data(), write_data(),端口设置,获取雷达状态等,这个是底层接口中最重要的文件。
实现了数据的更新后,上层的ros接口要继承底层接口,建立ROS功能包间的通信,以进一步实现可视化与用户的实时更改。综上,实现了数据从底层获取到上层的接口开发,具体细节请读者自行看源码。
雷达数据格式 数据的通信包含:源码与socket_can包的通信、源码内部之间的数据通信。通信之间的数据格式对于理清楚源码的架构及运行机制有着重要作用。在大陆雷达开发手册中有着清楚的说明,建议读者首先读一下开发手册。
socket_can通信格式(以雷达配置帧通信为例) 源码与socket_can通信是符合大陆雷达的标准格式的。
数据发送包括:数据头(fram_id) + 数据(n byte)
其中:数据头说明我要发送什么数据:例如配置数据中对于最大距离的配置、滤波配置等。
数据请求:数据头(fram_id) + 数据(n byte),与数据发送同理,不过功能是对于雷达状态的请求、雷达点云数据的请求等。
这是雷达配置信息的设置帧参数:可以看到,其中对于雷达的配置帧,包含了8 * 8位的数据,每一位都有特殊的用途,例如要设置雷达最大距离,就要设置其中的max_distance块,并包装进一个8byte的数据中,发送出去,当然,这些底层的移位操作作者通过union体帮我们解决了,我们只需要按照作者给定的格式进行编码,如下图所示。
如上图,这是雷达的配置数据格式,这些变量按照名字都是雷达配置帧中的关键变量。雷达的配置帧数据格式是按照:第一位,第二位…每一位分别配置的,通过union体与":"符号(每个变量后面都是一个整数,这是设置的union位域)是每个变量对应其在8byte中特定的位置,免去了需要单独移位的操作。
raw_data就是8byte的数据,raw_data用来保存所有的数据,同时还能够按照位域整体接收来自socket_can的雷达8byte数据,这样在查看某个参数时也不需要移位操作就能取到对应的雷达配置参数,非常方便。
通过以上,可以了解到源码内部对于雷达配置等参数的封装格式以及和底层雷达通信的数据格式,下面介绍其他比较关键的数据格式。
关键数据介绍及格式说明 同样重要的还有:雷达状态帧、目标模式下的目标帧、cluster模式下的数据帧、can_messages
重点介绍:cluster下的数据帧,can_messages
cluster数据帧 cluster数据帧主要是包括三个部分:
cluster_0_status:包含一帧内的点云数量等基本点云信息;cluster_1_general:点云的速度和方向等信息;cluster_2_quality:点云的质量信息:例如点的真实存在概率等;
cluster的数据格式定义 can_messages 这个信息头的作用在于发送的时候作为fram_id,说明自己要发送或者要取什么数据,例如要ars_40x_can.cpp中的receive_data()中,要发送数据前必须要加上frame_id = RadarCfg。
ars_40x_can.cpp中关于发送配置请求的部分 前面的can_messsages作为frame_id就是这个作用,通过socket_can提供的can_.write来确定写入的信息类别,类的get方法返回的raw_data传入雷达中修改配置信息等雷达运行参数。
关键参数:topic与服务 通过查看topic,可以实时获取雷达的当前状态与目标点的信息等,ROS的topic机制实现了ROS系统内的信息共享,免去了复杂的进程间通信,利用其在系统内发布的信息进行订阅,继而可以方便的进行二次开发。
MessageTypeDescriptionMessage Box/radar_statusars_40X/RadarStatusDescribe the radar configuration0x201/ars_40X/clustersars_40X/ClusterListRaw clusters data from radar0x600, 0x701/ars_40X/objectsars_40X/ObjectListRaw objects data from radar0x60A, 0x60B, 0x60C, 0x60D/visualize_clustersvisualization_msgs/MarkerArrayClusters markers for RViz visualization-/visualize_objectsvisualization_msgs/MarkerArrayObject markers for RViz visualization- 这是作者源码中定义的基本topic。
【题目描述】
给定一个多项式,以及变量的值,求多项式的值。
【输入格式】
输入为三行数据,第一行是多项式的项数n,第二行有n个数,是多项式从高到低的系数,第三行是变量x的值。
【输出格式】
一行一个数表示多项式的值
【输入样例】
3
1 2 1
2
【输出样例】
9
#include <iostream> #include <vector> using namespace std; int main() { int n, *p;//项数 cin>>n; p = new int[n]; for(int i = 0; i < n; i++) { cin >>p[i];//系数 } int x;//变量 cin>>x; int value = p[0];//多项式的值 for(int i = 0; i < n - 1; i++) { value = value * x + p[i+1]; } cout<<value; return 0; } 解题思路:因式分解再求值!
Eureka的微服务信息参数,注意新版的已经显示默认是(IP:服务名:端口) cloud-provider-payment8001 eureka: ... instance: instance-id: payment8001 # 微服务名称 cloud-provider-payment8002 eureka: ... instance: instance-id: payment8002 # 微服务名称 效果 服务发现Discovery 修改消费模块的控制层代码如下 package com.gcl.springcloud.controller; import com.gcl.springcloud.entities.CommonResult; import com.gcl.springcloud.entities.Payment; import lombok.extern.slf4j.Slf4j; import org.springframework.cloud.client.ServiceInstance; import org.springframework.cloud.client.discovery.DiscoveryClient; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.client.RestTemplate; import javax.annotation.Resource; import java.util.List; @RestController @Slf4j public class OrderController { private static final String PAYMENT_URL = "http://cloud-payment-service"; @Resource private RestTemplate restTemplate; @Resource private DiscoveryClient discoveryClient; @GetMapping(value = "/consumer/payment/create") public CommonResult<Payment> create(Payment payment){ return restTemplate.
【题目描述】
有M(1 000以内)个猴子围成一圈,每个猴子有一个编号,编号从1到M,打算从中选出一个大王。经过协商。 决定选大王的规则如下:从第1个猴子开始,每隔N(任意正整数)个,数到的猴子出圈,最后剩下来的就是大王。要求从键盘输入M、N(均为正整数),试编程计算编号为多少的猴子将成为大王。
要求:
1、 变量m表示猴子的个数;变量n表示出圈基数。
2、数数规则:每次从1数到n,当前为n的猴子出圈,若有3个猴子,n为2,猴子编号:1,2,3,出圈猴子的序号:2,1,猴子大王的序号为3。
【输入格式】
一行两个数 m 和 n
【输出格式】
一行输出猴子大王的编号
【输入输出样例】
屏幕输出 input m:
用户输入 3
屏幕输出 input n:
用户输入 2
屏幕输出 king:
屏幕输出 3
#include<iostream> using namespace std; int main() { //从第1个猴子开始,每隔N(任意正整数)个,数到的猴子出圈,最后剩下来的就是大王 bool a[101] = { 0 };//猴子状态,0-在,1-出圈 int n, m, f = 0, t = 0, s = 0; cout << "input m: ";//猴子个数 cin >> m; cout << "input n: ";//出圈基数 cin >> n; do { ++t;//逐个枚举圈中的所有位置 if (t > m) t = 1;//数组模拟环状,最后一个与第一个相连 if (!
vue-color-picker-sheldon使用说明(一款基于Vue的取色器/颜色选择器) Introduction 简介Functions 功能点Install 安装Usage 用法CSDN 地址 Introduction 简介 This is a color picker component based on vue. It is powerful and supports drag and drop, color picking and other functions.
这是一个基于 vue 的取色器组件,功能强大,支持拖拽、取色等功能。
Functions 功能点 Show a gradient palette with all colors and adjustable transparency 展示一个有所有颜色并且可以调节透明度的渐变调色板
Click on the palette to get the selected color, and display the corresponding hex value or rgba value in real time 点击调色板获取选中的颜色,并且实时显示对应 hex 值或者 rgba 值
说明 我们上一篇已经演示了在Eureka集群下单个服务提供微服务的案例,我们这篇就演示多个服务提供的案例。我们复制cloud-provider-payment8001,并命名新的项目为cloud-provider-payment8002。
在父Pom中添加对应的module <modules> <module>cloud-provider-payment8001</module> <module>cloud-provider-payment8002</module> <module>cloud-consumer-order8080</module> <module>cloud-api-commons</module> <module>cloud-eureka-server7001</module> <module>cloud-eureka-server7002</module> <module>cloud-eureka-server7003</module> </modules> 更改复制来的pom的artifactId <artifactId>cloud-provider-payment8002</artifactId> 更改复制来的yml server: port: 8002 eureka: client: #表示是否将自己注册进Eurekaserver默认为true。 register-with-eureka: true #是否从EurekaServer抓取已有的注册信息,默认为true。单节点无所谓,集群必须设置为true才能配合ribbon使用负载均衡 fetchRegistry: true service-url: defaultZone: http://127.0.0.1:7001/eureka,http://localhost:7002/eureka,http://192.168.0.135:7003/eureka spring: application: name: cloud-payment-service datasource: type: com.alibaba.druid.pool.DruidDataSource driver-class-name: org.gjt.mm.mysql.Driver url: jdbc:mysql://localhost:3306/my?useUnicode=true&characterEncoding=utf-8&useSSL=false username: root password: 123456 mybatis: mapper-locations: classpath:mapper/*.xml type-aliases-package: com.gcl.springcloud.entities 更改复制来的启动类 package com.gcl.springcloud; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.netflix.eureka.EnableEurekaClient; @EnableEurekaClient @SpringBootApplication public class PaymentMain8002 { public static void main(String[] args) { SpringApplication.
【题目描述】
财务处要给公司的n位员工发工资了,请你帮助计算最少要多少张人民币才能给每位员工发工资而不必找零呢?已知人民币的面额为100元,50元,10元,5元,2元和1元这6种。
【输入格式】
第一个值为正整数n,后面接着n个正整数表示n位员工的工资
【输出格式】
一行一个整数输出一共要准备的人民币张数。
【输入样例】
3 1 2 3
【输出样例】
4
先叨叨两句:作为一个小菜鸡,也是第一次开始创作,后续会一直记录平时的练习和作业。实际上自己写代码的时候,没有那么多注释,准备发上来,所以添了很多注释。因为如果它能有幸帮助到初学者,希望你们都能看懂,看懂了再复制粘贴(我大一就查找到资源直接交作业,然后美美挂科~~)。当然如果能被大佬发现这些文章,希望得到批评指正(希望学会更好的算法)!谢谢看完这段废话的兄弟姐妹萌~
代码如下:
#include<iostream> using namespace std; int main() { int n, *p, count=0;// count记录所需人民币张数 int a[6] = {100,50,10,5,2,1};//面额 cin>>n; //员工数 p = new int[n]; //利用指针建立动态数组 for(int i=0;i<n;i++) { cin>>p[i]; } int t = n;//t:保存待计算员工人数 while(t != 0) { //每计算完成一位员工所需数量,t--,直到全体员工计算完成后跳出循环 for(int i = 0; i < n; i++) //从第0位员工算到第n-1个 { for(int j = 0; j < 6; j++)//需要最小数量,即需从最大面额开始计算 { if(p[i] >= a[j]) //员工工资大于当前面额 { //k:记录当前面值的钱需要多少张 int k = p[i] / a[j]; count += k; p[i] -= k * a[j]; //为下一面额作准备 } } } t--; } cout<<count<<endl; return 0; } 解题思路:
这道题其实是要求利用数组解决。不过考虑到用set(集合)解题更加简便,因此,下面做法是使用了容器set。
【题目描述】
给定两个集合A和B,判断他们是否相等。(集合元素可能需要去重) 。
如果两个集合相等,则输出"yes",否则输出"no"
【输入格式】
两行分别表示集合 A 和 B ,每一行先输入一个数 n 表示集合的大小,接下来输入 n 个数表示集合的元素
【输出格式】
按要求输出 "yes" 或 "no" (不带引号)
【输入样例】
4 1 2 3 4
4 4 3 2 1
【输出样例】
yes
【样例解释】
第一个集合里不重复的元素为{1,2,3,4},第二个集合也为{1,2,3,4},故两个集合相等
#include<iostream> #include<set> //使用set需要引入头文件 using namespace std; int main() { set<int> a; //int类型的集合a,b set<int> b; int n1,n2;//集合大小 //输入要比较的集合 cin>>n1; for(int i = 0; i < n1; i++) { int k; cin>>k; a.insert(k);//向集合a中插入整数k } cin>>n2; for(int i = 0; i < n2; i++) { int k; cin>>k; b.
【题目描述】
若干个数据首尾相连,构成一个圆环,找到连续的4个数之和最大的一段。
【输入格式】
一行若干个数,当输入−1时结束输入
【输出格式】
一行输出其中连续的4个数之和最大的一段的起始数据的位置及这4个数的和,若输入的数据少于4个数,输出显示“invalid input”。
【输入样例1】
1 23 -1
【输出样例1】
invalid input
【输入样例2】
7 6 2 3 9 1 8 -1
【输出样例2】
5 25
(注:从第5个数据开始的连续四个数据和最大,为25)
代码如下:
#include<iostream> using namespace std; int main() { int n,a[20];//n记录输入数的个数,max记录最大累加值 for(n = 0;;n++) { cin>>a[n]; if(a[n]==-1) break; } if(n < 4) cout<<"invaild input"<<endl;//判断个数 else { int max = 0; int t1,t2;//记录累加值 int j;//记录数的序号 for(int i = 0;i<n;i++) { //KEY:实现一个圆环 t1 = a[i] + a[(i+1)%n] + a[(i+2)%n] + a[(i+3)%n]; if(t1 > max) //把最大值放入max中 { t2 = t1; t1 = max; max = t2; j = i;//记录最大值时,数的下标 } } cout<<j+1<<"
背景 写了一段sql,一个表自关联了三次,测试后发现如果用left join不到一秒可以查询出数据,如果用inner join却查询几分钟都不会出数据。
表格结构 如下所示,这是一张用户的报表浏览记录表,其中CLICK_TIME创建了索引字段,表格总数据量20w条左右
查询sql 下面sql想要查询每个用户一周之内每天相比较点击报表的最早时间和最晚时间,以及所点击的报表的明细。
SELECT c.wx_user_id AS wxUserId, c.menu_name AS earliestMenu, c.click_time AS earliestTime,d.menu_name lastMenu,d.click_time lastTime,b.num num,b.max maxTime,b.min minTime,'0' menu_type FROM ( -- 得到报表浏览最早的时间和浏览最晚的时间 SELECT a.wx_user_id, count(1) num , max(to_CHAR(a.click_time, 'HH24:MI:SS')) AS max , min(to_CHAR(a.click_time, 'HH24:MI:SS')) AS min FROM MOBILE_DOOR_MENU_CLICK_LOG a WHERE a.click_time >= trunc(SYSDATE-4) and a.click_time <= TO_DATE(TO_CHAR(SYSDATE, 'yyyy-MM-dd HH24'),'yyyy-MM-dd HH24:MI:SS') GROUP BY a.wx_user_id ) b -- 根据用户名和浏览时间进行自关联得到最早查询明细 left JOIN (select * from MOBILE_DOOR_MENU_CLICK_LOG WHERE click_time >= trunc(SYSDATE-4) and click_time <= TO_DATE(TO_CHAR(SYSDATE, 'yyyy-MM-dd HH24'),'yyyy-MM-dd HH24:MI:SS')) c ON c.
一、云数据库概述 ** 特点:**
1.按需服务
2.随时服务
3.通用性
4.高可靠性
5.易用性
6.免维护
7.安全性
iaas saas paas
云数据库与其他数据库的关系
无专有模型,多个并用,以服务形式提供
二、云数据库产品 亚马逊、微软等
三、UMP系统概述 Alibaba开发通用 Unified Mysql Platform
1.单一对外访问接口
2.消除单点故障
3.动态可伸缩性
4.实现资源间相互隔离
系统架构 Mnesia 分布式数据库
异步消息队列
zoomkeep
LVS linux virtual server
controller 服务器
Proxy服务器
agent server
日志服务器
信息统计服务器
愚公系统
ump系统功能 资源隔离
四、Amazon 产品 亚马逊开创云计算服务模式作为IT资源提供给客户
AWS
AWS架构 NEtwork
compute
EC2 ELB
storage
database
App service
Deployment
使用模式
云数据库 SimpleDB
DynamoDB
RDS
微软云SQL Azure
关注它,不迷路。
之前写过一篇文章,使用pywasm来导出wasm里的函数,具体内容见下面的文章:
JS逆向:Wasm文件导出函数的调用|猿人学爬虫对抗赛第十五题
依然以猿人学的题目作为练手地址,看看今天介绍的2种wasm文件的解法。
一,使用WebAssembly.instantiate方法。
我们看到网站上有这样的一段代码:
我们可以依葫芦画瓢,写出类似的代码:
exports.yrx15_wasm = (buf)=>{ let wasm_buffer = new Uint8Array(buf).buffer res = WebAssembly.instantiate(wasm_buffer) return res.then( res => { instance = res.instance q = instance.exports.encode m = function () { t1 = parseInt(Date.parse(new Date()) / 1000 / 2); t2 = parseInt(Date.parse(new Date()) / 1000 / 2 - Math.floor(Math.random() * (50) + 1)); return q(t1, t2).toString() + '|' + t1 + '|' + t2; } return m() } ) } 再使用 node_vm2 这个库来调用这段js,参数buf传递的是Python里的list类型数据:
Q:python使用Axes3D画三维图加入legend图例时报错AttributeError: 'Poly3DCollection' object has no attribute '_edgecolors2d' 报错源代码
fig = plt.figure() ax = Axes3D(fig) X, Y = np.meshgrid(3, 3) ax.plot_surface(X, Y, np.zeros([3,3], label="surf") ax.legend() plt.show() A:在ax.legend()加入如下代码 surf._facecolors2d=surf._facecolors3d surf._edgecolors2d=surf._edgecolors3d 修改后代码
fig = plt.figure() ax = Axes3D(fig) X, Y = np.meshgrid(3, 3) surf = ax.plot_surface(X, Y, np.zeros([3,3], label="surf") surf._facecolors2d=surf._facecolors3d surf._edgecolors2d=surf._edgecolors3d ax.legend() plt.show()
其实这个是因为我们的canvas在绘制的时候发现了不同源头的资源,这个时候浏览器会认为污染了当前canvas同时也会带来一系列安全的问题。
。
在"被污染"的canvas中调用以下方法将会抛出安全错误: 在 <canvas> 的上下文上调用getImageData() 在 <canvas> 上调用 toBlob() 在 <canvas> 上调用 toDataURL() 这种机制可以避免未经许可拉取远程网站信息而导致的用户隐私泄露 在html2canvas中画图的时候,如果图片地址和当前页面不同源头的情况下,会出现跨域问题,比如 图片域名和当前域名不同,当前是的http,而图片的域名是 https的协议。
这个时候 我们应该先让后端童鞋去配置下 允许跨域访问静态资源,
然后在html2canvas绘制的时候 配置上加上
useCORS: true,
html2canvas(demo, { useCORS: true, scrollY: 0, scrollX: 0, allowTaint: true }).then(canvas => { let base64 = canvas.toDataURL("image/png") this.base64Img = base64 }) 但是上线之后发现还是会出现跨域的问题
其实这个时候我们可以通过 img标签的一个属性
crossorigin= anonymous
之前看网上说的这个方式。还是不行。照样还是空白的情况会发生
还有一种方式就是 直接我们吧图片的源头写在这里 就可以处理
比如你的图片的域名是 “https://demo.example.com”
我们在下载的图片上直接写
<img src="https://demo.example.com/cdn/demo.png" crossorigin="https://demo.example.com" /> 解决跨域问题
关注我 持续更新前端知识。
近期导师让我将一个matlab代码改写成C,其中涉及到大量的矩阵运算和稀疏矩阵运算,经过一段时间的摸索和实践,记录一下这段时间的学习经验
首先由于要基于C语言实现一些矩阵的运算,加减乘除,求逆,以及解线性方程组AX=B之类。
这种矩阵运算自己写的话就有点浪费时间,而且效率肯定不会很高了...故选择eigen库,开源库不用考虑引用问题,且数据结构、函数等相对简单,安装方便,很好上手。
Windows下Eigen + vs配置_wilsonass的博客-CSDN博客
基础的一些矩阵定义及基础矩阵操作可参考以下:
Eigen: Main Page(官方说明文档)
C++矩阵库 Eigen 简介 - rainbow70626 - 博客园 (cnblogs.com)
基本求解器(只需要eigen就可以)
下面具体说一下解系数为稀疏矩阵的线性方程组的问题:
在eigen中,有自带的一部分求解器,参考这篇博客的讲解:
Eigen教程5 - 求解稀疏线性方程组_张学志の博客-CSDN博客
如SimplicialLLT、SimplicialLDLT等求解器是可以直接使用的:
SimplicialLDLT<SparseMatrix<float>> solver; solver.compute(A); VectorXf x = solver.solve(B); 这是直接法求解,此外还有迭代法求解线性方程组:
87 Eigen应用:解线性方程组的古典迭代法 - 十步一杀2017 - 博客园 (cnblogs.com)
共轭梯度求解
//共轭梯度求解器ConjugateGradient ConjugateGradient<SparseMatrix<float>, Lower | Upper> cg; cg.compute(A); VectorXf x = cg.solve(B); std::cout << "#iterations: " << cg.iterations() << std::endl; std::cout << "estimated error: " << cg.error() << std::endl; // update b, and solve again x = cg.
主要原因:vim-plug 上的插件大部分都托管在github上。
解决方案:用国内镜像,间接访问vim-plug的实际网站。即修改plug.vim访问的网站为镜像网站。
打开 plug.vim ,搜索 github, 修改两条语句,即可成功下载GitHub资源。 let fmt = get(g:, 'plug_url_format', 'https://git::@github.com/%s.git') 改为
let fmt = get(g:, 'plug_url_format', 'https://git::@hub.fastgit.org/%s.git') \ '^https://git::@github\.com', 'https://github.com', '') 改为
\ '^https://git::@hub.fastgit\.org', 'https://hub.fastgit.org', '') 上述两个步骤的目是让镜像网站代替实际网站, 这样能有效提高下载成功率。
SPU和SKU是电商产品具有的两个重要概念。在电商网站设计中,它对应了一件商品在数据库表中的具体存储方式。抛开数据库来描述某一件商品,自然不需要额外加入这两个比较复杂的概念,但是考虑到电商系统中对商品信息的调用非常频繁,而数据的展现方式又各式各样,因此如果不对商品信息在数据库中存储方式进行合理设计,可能会导致数据库查询复杂化、页面请求变慢等问题。对前端而言,spu、sku可以对商品描述进行合理分级,便于在不同页面根据需求展示不同详细程度的产品信息。对后端而言,spu、sku可以统一和规范化产品的数据库设计,减少管理商品(增/删/改/查)时数据库的开销。
SPU:标准化产品单元 SPU = Standard Product Unit (标准化产品单元),SPU是商品信息聚合的最小单位,是一组可复用、易检索的标准化信息的集合,该集合描述了一个产品的特性。
通俗点讲,属性值、特性相同的商品就可以称为一个SPU。
SKU:库存量最小单位(最小库存单元) SKU=stock keeping unit(库存量单位) ,SKU即库存进出计量的单位(买家购买、商家进货、供应商备货、工厂生产都是依据SKU进行的)。SKU在服装、鞋类商品中使用最多最普遍。 例如纺织品中一个SKU通常表示:规格、颜色、款式。
SKU是物理上不可分割的最小存货单元。也就是说一款商品,可以根据SKU来确定具体的货物存量。
如一件M码(四个尺码:S码、M码、L码、X码)的粉色(三种颜色:粉色、黄色、黑色)Zara女士风衣,其中M码、粉色就是一组SKU的组合。
SKU在生成时, 会根据属性生成相应的笛卡尔积。根据一组SKU可以确定商品的库存情况,那么上面的Zara女士风衣一共有4 * 3 = 12个SKU组合。
M码+粉色这两个属性组合被称为一组SKU、因为SKU是物理上不可分割的最小存货单元,单凭尺寸或者颜色是没有办法确认这款商品的库存情况。
同理商家进货补货也是通过SKU来完成的,试问淘宝店家跟供货商说我要100件红色女士风衣?供应商知道该怎么给他备货吗?
显然是不知道的。因为还欠缺了另外的一个销售属性【尺码】。
举例:
iPhone X 全网通黑色256G 就是一个SKU,表示了具体的规格、颜色等信息。iPhone X 就是一个SPU,与商家、颜色、款式、规格、套餐等都无关。 SPU和SKU是在不同层次上对商品进行描述,我们可以这么理解:
标准化产品单元(SPU),标准化,即被广泛认知的的东西。在市场中我们买卖东西,我们会说某某品牌的xx产品如何如何,而不会去说某某品牌的xx产品xx颜色xx尺寸的产品如何如何。就好比手机发布会中,说我们今年推出了一款新的手机,型号为iphone11,而不会说我们今年推出了iphone 11 128g 玫瑰金、iphone 11 128g 深空灰等等。标准化,也可以理解为对描述产品的详细程度上的一种标准,但需要注意的是这个标准是对产品的最小粒度描述。也就是说不同的spu就是不同的产品了,如iphone11和iphone8,虽然都是iphone,但是普遍认为iphone产品的最小粒度在型号上,因此他们就是两个产品了。库存量单元(SKU),顾名思义就是库存商品单元,是要具体往外出售 的物品。SPU告诉了我们一个物品是什么东西,SKU则告诉了我们这个物品是一件具有什么特征什么规格尺寸当前是否还有库存的东西。 SKU和商品之间的关系 SPU,即标准化产品单元。简单理解就是某一种产品。SKU,即库存量单元。即具体的一件商品。商品SPU和商品SKU是包含关系,一个商品SPU包含若干个商品SKU子实体,商品SKU从属于商品SPU。
3)SKU不是编码,每个SKU包含一个唯一编码,即SKU Code,用于管理。
4)商品本身也有一个编码,即Product Code,但不作为直接库存管理使用。
问题描述 在使用谷歌浏览器时,频繁出现内存不足,无法打开此网页。
问题分析 Chrome需要虚拟内存的支持!总是优先占用虚拟内存,虚拟内存满了才使用物理内存。也就是说,虚拟内存设置的越大,Chrome可以打开的标签页就越多。
注:本人就是在清除C盘一些“无用”文件,想把虚拟内存移到D盘,但C盘虚拟内存删了,但D盘没设置,所有导致了错误。
问题解决 1.在系统属性中选择“高级”,点击“设置(S)...”。
2.在性能选项中选择“高级”,点击“更改(C)...”。
3.选择一个盘符,点击系统管理的大小,点击设置,点击确定,最后点击性能选项中的“应用(A)”,不过一般需要重启电脑才能生效。
注:也可以自定义虚拟内存的大小。如果勾选自动管理所有驱动器的分页文件大小(A),默认托管在系统盘。本人由于避免占用C盘内存,所以设置了D盘。
参考链接
谷歌浏览器Chrome黑屏\提示内存不足\应用程序错误的解决办法 && 虚拟内存应该设多大_learner_zyj的专栏-CSDN博客_谷歌浏览器内存不足,无法打开此网页https://blog.csdn.net/wmm131333/article/details/98734443
一、代码模板是什么 它的原理就是配置一些常用代码字母缩写,在输入简写时可以出现你预定义的固定模式的代码,使得开发效率大大提高,同时也可以增加个性化。最简单的例子就是在Java中输入sout会出现System.out.println();
(一)所处位置:
(1)Live Templates
(2)Postfix Completion
(二)区别:
【1】
Live Templates中可以做用户的个性化定制。
Postfix Completion中只能用,不能修改。
【2】使用方式不同
二、常用的代码模板 【1】模板1: main方法:
main 或者 psvm
【2】模板2:输出语句:
sout 或者 .sout
一些变型:
soutp:打印方法的形参
soutm:打印方法的名字
soutv:打印变量
【3】模板3: 循环
普通for循环: fori(正向) 或者 .fori (正向) . forr(逆向)
增强for循环: iter 或者 .for
(可以用于数组的遍历,集合的遍历)
【4】模板4: 条件判断
ifn 或者 .null :判断是否为null (if null)
inn 或者 .nn :判断不等于null (if not null)
【5】模板5: 属性修饰符:
prsf : private static final
psf :public static final
三、修改现有模板 【1】案例1:改main方法: psvm
第一串代码为精简版
#include<iostream> using namespace std; #define N 10//用宏定义来控制数组的大小 void input(float *p,int n) //给数组num赋值 { for(int i=0;i<n;i++) { cout<<"请输入第"<<i+1<<"个元素:"; cin>>*p; p++; } } void output(float *p,int n) //输出数组num的值 { for(int i=0;i<n;i++) { cout<<p[i]<<'\t'; if((i+1)%5==0)cout<<'\n'; //每输出5个换行 } } float fun(float a[],int n,float &max,float *min) { int i,j;float s=0,aver; for(i=0;i<n;i++) //查找数组中的重大和最小值,并累加 { if(max<a[i])max=a[i]; if(*min>a[i])*min=a[i]; s+=a[i]; } aver=(s-max-*min)/(n-2); //计算去除最大值和最小值后数组的平均值 return aver; } int main() { cout<<"输入一个10个数的数组\n,每输入一个数按回车确定"; float num[10],*p=num; //指针p指向数组num的首地址 input(num,N); cout<<"输入的数组为:\n"; output(num,N); float max=p[0],min=p[0]; float aver=fun(p,5,max,&min); cout<<"
先说环境版本号,我使用的是: 1.虚拟机:VMware® Workstation 16 Pro
版本16.2.1
2.Linux环境:Ubuntu64位
版本ubuntu-20.04.3-desktop-amd64
3.Mysql:使用apt安装
版本mysql-server-8.0,mysql-server-core-8.0
不管是用Navicat连,还是用终端连,都会出现ERROR 1698
先说原因 因为在Ubuntu下用apt装mysql是没有设置密码的环节的
所以一开始就是没有密码,而且密码也不是为空值,直接敲回车也进不去mysql
所以我们这个时候可以直接mysql -u root
直接进入mysql
——————————————手动分隔符————————————————————————————
进入mysql后 我们先use mysql;,使用mysql数据库
再输入SELECT User, Host, plugin FROM mysql.user;,查看我们root用户的身份认证器插件
这里可以看到我们root用户的认证器插件(plugin)是auth _socket
auth_socket这个插件的验证方式不要求输入密码,即使输入了密码也不验证
而它也只能用 UNIX 的 socket 方式登陆,这就是说只能本地登陆
以及操作系统的用户和 MySQL 数据库的用户名必须一致,例如你要登陆 MySQL 的 root 用户,必须用操作系统的 root 用户登陆
再就是Mysql8.0版本后,默认认证方式都改成sha2了,就是图上的 caching_sha2_password
但很多开发环境都没有sha2的插件,就导致这个sha2问题贼多,所以我们要换成最稳的那个认证插件:mysql_native_password
所以我们再输入 UPDATE user SET plugin='mysql_native_password' WHERE User='root';
将root用户的认证插件改为mysql_native_password
修改成功后再输入一次SELECT User, Host, plugin FROM mysql.user;查看用户插件
可以看到是修改成功了,然后再输入FLUSH PRIVILEGES;,使其生效
然后我们重启一下mysql服务,输入sudo service mysql restart;
文章目录 前言一、原理图设计1.1镜像翻转1.2 放置元器件1.3 设置图纸大小 二、检查封装三、交叉选择功能四、布局传递功能五、布局六、铺铜功能6.1、填充功能6.2、矩形填充功能 七、自动布线 前言 学习目录
(一)立创EDA的安装
(二)立创EDA之新建工程,原理图,PCB
(三)立创EDA原理图库的创建
(四)立创EDA之封装库的创建
(五)立创EDA之3D模型
我们前面学习了如何新建工程,如何新建原理图库,如何新建封装库,如何导入3D模型库,并且是以LM358为例。那么我们今天就以LM358为例进行原理图设计示例。
首先先把我们之前做好的子库添加进来。放在原理图里面。
一、原理图设计 1.1镜像翻转 拖动状态下按X,实现X轴镜像翻转,按Y,Y轴镜像翻转。
1.2 放置元器件 然后我们把VCC,GND也加上,然后再加上电阻,然后立创商城搜索22uF的引线型电解电容。放置到画布。同时我们添加滑动变阻器,二极管,三极管,插针,这些在基础库都有
1.3 设置图纸大小 这个图我们就大致画好了,图纸看起来有点大,那么我们就调整一下图纸大小。
然后改一下具体参数,那么我们的原理图设计就基本完成了。
二、检查封装 在原理图转PCB之前我们需要检查我们的封装是否正确,那么就需要用到我们的封装管理器了。
有两种方法进入,一个是工具里面的封装管理器。
一种是点击某一个器件,点击右侧的封装
然后我们检查我们的封装,发现都没啥要改的。那就可以转成PCB了,
当然三极管改成9013呀,8050也是可以的,改一下封装就可以了。
三、交叉选择功能 前面的文章我们也说过了,立创EDA这个交叉选择的功能。
我们在原理图选择选中一个器件,然后在工具选择交叉选择,那么在PCB中我们就可以看到器件高亮选中了。同理在pcb中选中,然后交叉选择也是一样的道理。
四、布局传递功能 前面的文章我们也说过了,立创EDA这个布局传递的功能。可以快速进行布局。
就是根据你的原理图来进行预布局,我们选中全部器件,然后工具,布局传递。
那么在PCB图里面就可以单击,放置就可以了,当然也可以选中一部分进行布局传递。
五、布局 然后我们按照原理图进行一个大概的布局,差不多就这样,反正线不那么乱就行。
到时候再微调嘛。
六、铺铜功能 铺铜我们一般框一个矩形,然后里面铺铜,铺铜的意思就是在我们的PCB板上铺一层铜皮。
,一般双层板的话,就是底层铺地铜。
我们选择底层,然后选择铺铜。然后依此点击边框的四个角,然后围成一个闭合形状,右键即可。
或者按L改变布线方式,按1次的话就是直角了。
我们可以看到默认就是十字花连接。我们单击我们的铺铜框,就是这个铺铜线,然后我们可以看到这个铺铜的属性,我们可以选择发散,也可以选择直连,不建议直连,因为直连散热太快了,不利于焊接。
那么顶层的铺铜方式也是类似的。如果我们在内部覆铜也是一样的情况。但是为什么没有显示出来,那是因为在这个覆铜的框内没有一个与覆铜相同的网络,这个默认是GND,也就是说,你这里没有GND。
比如说这个R1的焊盘是U1_3的网络,然后把这个网络名字赋予给刚才我们这层铜皮,
把它改为U1_3,那就很明显,我们的铜皮就有了网络属性,就连接上了。
6.1、填充功能 还有一个功能就是填充,填充在覆铜的右边,然后点击就可以添加网络
6.2、矩形填充功能 就是一个填充的铜皮了,不过是矩形的,同样也是可以添加网络。
七、自动布线 自动布线这个功能你说他好,确实方便,不好吧,也确实不够美观。
虽然立创EDA的自动布线功能非常好用,但并不建议大家在设计PCB的时候经常使用自动布线功能,在PCB设计中,布线其实也可以说是一门艺术,好的布线可以使系统性能更加优化,电路设计是一个持续积累的过程,需要多多练习。
这里就不过多讲解自动布线的细节了。感兴趣的可以点击下方链接,观看B站视频。
https://www.bilibili.com/video/BV1EE41147yV?p=16
前言 统称来说,容器是一种工具,指的是可以装下其它物品的工具,以方便人类归纳放置物品、存储和异地运输,具体来说比如人类使用的衣柜、行李箱、背包等可以成为容器,但今天我们所说的容器是一种IT技术。
容器技术是虚拟化、云计算、大数据之后的一门新兴的并且是炙手可热的新技术,容器技术提高了硬件资源利用率(相对于物理机或者虚拟机)、方便了企业的业务快速横向扩容、实现了业务宕机自愈功能,因此未来数年会是一个容器愈发流行的时代,这是一个对于IT行业来说非常有影响和价值的技术,而对于IT行业的从业者来说,熟练掌握容器技术无疑是一个很有前景的行业工作机会。容器技术最早出现在freebsd叫做jail。
虚拟机是直接运行在物理机上的,可以实现嵌套虚拟化,但是性能较差,不经常使用。公司一般使用KVM+openstack对容器进行批量管理,创建以及批量维护,实现虚拟机的快速创建。KVM通过命令快速创建虚拟机,提供了虚拟机的运行环境,使用docker+k8s对容器进行管理,实现容器的快速创建,通过k8s快速创建容器,docker提供了容器的运行环境。
docker和虚拟机的差别主要在于: 横向扩容和故障自治愈。
故障自治愈指的是:业务跑在多个物理机或者虚拟机上vm1 vm2,当虚拟机或者物理机挂掉以后,可以自动转移到其他物理机或虚拟机并启动。
一、docker 简介 1.1 Docker 是什么 首先,Docker是一个在2013年开源的应用程序并且是一个基于go语言编写是一个开源的PAAS服务(Platform as a Service,平台即服务的缩写), go语言是由google开发,docker公司最早叫dotCloud后由于Docker开源后大受欢迎就将公司改名为Docker Inc,总部位于美国加州的旧金山, Docker是基于linux内核实现,Docker最早采用LXC技术(LinuX Container的简写, LXC是Linux原生支持的容器技术,可以提供轻量级的虚拟化,可以说docker就是基于LXC发展起来的,提供LXC的高级封装,发展标准的配置方法), 而虚拟化技术KVM(Kernelbased Virtual Machine)基于模块实现,Docker后改为自己研发并开源的runc技术运行容器。 Docker相比虚拟机的交付速度更快,资源消耗更低, Docker采用客户端/服务端架构,使用远程API来管理和创建Docker容器,其可以轻松的创建一个轻量级的、可移植的、自给自足的容器。 docker的三大理念是 build(构建)、ship(运输)、run(运行),Docker遵从apache 2.0协议,并通过(namespace及cgroup等)来提供容器的资源隔离与安全保障等, 所以Docke容器在运行时不需要类似虚拟机(空运行的虚拟机占用物理机6-8%性能)的额外资源开销,因此可以大幅提高资源利用率。 总而言之,Docker是一种用了新颖方式实现的轻量级虚拟机。 类似于VM但是在原理和应用上和VM的差别还是很大的,并且docker的专业叫法是应用容器(Application Container)。 > 可移植指的是:可以运行在多种不同的操作平台上 > 自给自足指的是:每个docker夺回包括自己服务的运行环境, 例如java (JDL + TOMCAT + APP代码),将服务封装在服务中就可以运行。 docker的三大理念是build(构建)、ship(运输)、run(运行)指的是: 一次构建到处运输到处运行将构建的结果运输到任何有docker环境的服务器上,就可以运行构建的结果就是一个image(镜像) 在KVM(虚拟机)中称为模板, 创建虚拟机的时候就是基于模板创建虚拟机,创建容器的时候就是基于镜像创建容器。 namespace及cgroup也调用宿主机的内核,实现了docker运行的两个核心机制,这就会导致支持的功能可能会不一样。 dockerd 是整个docker的守护进程,基于守护进程再创建容器,这些容器都属于docker的子进程, 优点在于结构简单,当dockerd进程被kill掉或者出现异常以后,所有容器都会无法使用。 远程API指的是创建容器的时候,使用Cli客户端命令,或者使用web界面(由k8s封装的界面,称为dashborad) dashborad也是调用了dockerd来创建容器。 1.2 Docker 的组成 Docker官方文档: https://docs.docker.com/engine/docker-overview/ Docker官方仓库: https://hub.docker.com/ 包括第三方提供的镜像 Docker主机(Host/node节点/宿主机):一个物理机或虚拟机,用于运行Docker服务进程和容器。 Docker服务端(Server): Docker守护进程,用于启动创建和运行docker容器。 Docker客户端(Client): 客户端使用docker命令或其他工具调用docker API。 Docker仓库(Registry): 统一保存镜像的仓库,类似于git或svn这样的版本控制系统,公司内部一般使用harbor搭建 Docker镜像(Images): 镜像可以理解为创建实例使用的模板。 Docker容器(Container): 容器是从镜像生成对外提供服务的一个或一组服务。 1.
搭建WebService开发环境,改进“图片上传”实验 结果展示Server端ImageDAOImpl.java Client端Add.jspList.jspDetail.jspUploadServlet.java images.sql 写在前面:
本次实验是在Jsp+servlet+SqlServer实现图片上传、图片详情查看、表格分页实验的基础上进行改进……
(°ー°〃)菜鸟一枚!如有问题欢迎指出!!
结果展示 1、新建两个工程,一个是Webservice的Server端,一个是Webservice的Client端。
2、把原来业务层里的类(方法)通过Webservice发布出来,提供Client调用。
↓ 画了个简陋的图。。凑合看看
Server端 ——此处有修改——
(之前写的太着急,百度了一个如何发布webservice的文章就开始写了,后续也没有验证!如果之前看过这篇博客的朋友们,对不起!这里误导你们了!因为如果你使用的是Endpoint类的发布方法,是不必导入我之前说的那些jar包的。。)
这里使用的是jdk自带的websevice发布方法
1.6版本的jdk中JAX-WS规范定义了如何发布一个WebService服务。。
但貌似jdk10以上就没有javax.xml.ws这个包了。。所以,如果你的jdk版本高于10那就别往下看了!
再次感谢这位朋友!
核心代码
ImageDAOImpl.java package com.bnuz.dao.impl; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.util.ArrayList; import java.util.List; import javax.jws.WebService; import com.bnuz.dao.ImageDAO; import com.bnuz.javabean.Image; import com.bnuz.util.DBUtil; /* * 这个注解一定要加上!!在ImageDAO中也要加上这个注解 */ @WebService public class ImageDAOImpl implements ImageDAO{ private Connection conn; private PreparedStatement ps; @Override public void add(Image image) { try { conn = DBUtil.
Solidworks学习笔记-链接Solidworks
在此基础上
允许访问执行绘图操作的函数。
属性 NameDescription备注ActiveDrawingViewGets the currently active drawing view. 获取当前活动的图纸视图。AutomaticViewUpdateGets or sets whether the drawing views in this drawing are automatically updated if the underlying model in that drawing view changes. 获取或设置该工程图中的工程视图是否在该工程视图中的基础模型发生更改时自动更新。BackgroundProcessingOptionGets or sets the background processing option for this drawing. 获取或设置此绘图的后台处理选项。HiddenViewsVisibleShows or hides all of the hidden drawing views. 显示或隐藏所有隐藏的图纸视图。IActiveDrawingViewGets the currently active drawing view. 获取当前活动的图纸视图。SheetGets the specified sheet. 获取指定的工作表。 System.int BackgroundProcessingOption {get; set;}
This example shows how to fire notifications when background processing events occur.
如图一个xml文档由元素节点,属性节点,文本节点成
其中bookstore被称为文档元素或根元素,也是一个元素节点
整个文档是一个文档节点,即document节点。在Java中document接口是继承于node接口,表示整个xml文档
每个xml标签是一个元素节点,即element节点。在Java中element接口继承于node接口,表示xml文档中的一个元素
text节点为文本节点,在Java中text接口继承于characterData接口,而characterData继承于node接口,表示element或者attr的文本内容
所以其实xml文档中每一个对象都是一个节点,元素一定是节点,而节点不一定是元素。
node(节点)和element(元素)的区别
node是相对tree这种数据结构而言的。tree就是由node组成
element元素是一个小范围的定义,必须是含有完整信息的结点才是一个元素
。但是一个结点不一定是一个元素,而一个元素一定是一个结点。
Element是可以有属性和子节点的node
Element是从Node继承的
DOM将文档中的所有都看作节点 node>element
node有几个子类型:
Element,
Text,
Attribute,
RootElement,
Comment,
Namespace等
简介:
通过SPEL 和 fastjson 配合使用,指定JSON中字段解析的表达式,实现指定字段按照指定表达式填充的效果。
以使用三目运算符指定解析为例,代码如下。
首先,定义字段的反序列化解析类 SpelExpressDeserializer
import java.lang.reflect.Type; import org.springframework.expression.Expression; import org.springframework.expression.ExpressionParser; import org.springframework.expression.spel.standard.SpelExpressionParser; import org.springframework.expression.spel.support.StandardEvaluationContext; import org.springframework.util.StringUtils; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.parser.DefaultJSONParser; import com.alibaba.fastjson.parser.deserializer.ContextObjectDeserializer; import com.alibaba.fastjson.parser.deserializer.ObjectDeserializer; import lombok.extern.slf4j.Slf4j; /** * SPEL运算反序列 * * @author jun.chen * */ @Slf4j public class SpelExpressDeserializer extends ContextObjectDeserializer implements ObjectDeserializer { private ExpressionParser spel = new SpelExpressionParser(); @Override public int getFastMatchToken() { return 0; } @SuppressWarnings("unchecked") @Override public <T> T deserialze(DefaultJSONParser parser, Type type, Object fieldName, String format, int features) { Object parse = parser.
程序员在程序设计时常常需要对存储在数组中的大量数据进行处理,如排序、查找等。使用数据库时,用户可能需要频繁通过输入键字值来查找相应的记录。在数组中搜索一个特定元素的处理过程,称为查找。上次我们已经介绍了一种最基础的查找算法——线性查找(顺序查找)这次我们来介绍一种查找算法:折半查找。
我们知道,线性查找法不要求被查找的数组元素事先是有序排列的,所以既可能在第一个元素位置,也可能在最后一个元素位置,找到与查找键相等的元素值。
然而在日常生活的很多场合下需要在有序条件下进行检索操作,例如电话簿中的姓名、字典、图书馆或书架上的书、邮递员包裹里的信件、班级名册、地址簿、计算机中的文件列表等,在有序表中查找信息比较容易,如果信息列表(例如价格表)是有序的,一些特殊数字(如最大和最小值)就会很显眼,因为它们会位于列表的开头或末尾,重复的数字也容易发现,因为它们都是相邻的。
当待查找信息有序排列时,折半查找法比顺序查找法的平均查找速度要快得多。折半查找也成为对分搜索。二分法求方程的根使用的也是对分搜索的思想,正如二分法要求在求根区间中函数是单调的一样,使用折半查找法的前提是被查找的数据集合是已排好序的。顺序查找法不受这一前提条件的约束,所以对于无序的数据而言,顺序查找是唯一可行的办法。
折半查找法的基本思想为:首选选取位于数组中间的元素,将其与查找键进行比较。如果它们的值相等,则查找键被找到,返回数组中间元素的下标,否则,将查找的区间缩小为原来区间的一半,即在一半的数组元素中查找。假设数组元素已按升序排列,如果查找键小于数组的中间元素值,则在前一半数组元素中继续查找,否则在后一半数组元素中继续查找。如果在该子数组(原数组的一个片段)中仍未找到查找键,则算法将在原数组的四分之一大小的子数组中继续查找。每次比较之后,都将目标数组中一半的元素排除在比较范围之外。不断重复这样的查找过程,直到查找键等于某个子数组中间元素的值(找到查找键),或者子数组只包含一个不等于查找键的元素(即没有找到查找键为止)。
听起来很抽象,难以理解?我们先来玩一个猜数游戏:我在1到100之间随便想一个整数,你来猜数,当你猜错的时候,我会提醒你猜的数是过大还是过小。
首先,我相信你的第一选择是50,这样能够把1~100分成1~49和51~100两个区间,如果我提示过大,你就能在1~49之间再猜一个数,这样猜中的几率就从1%变成了2%,提高了一倍(当然如果一次猜中那就是欧皇了)……接下来反复重复这个步骤,直到猜中为止,最多只需要猜6~7次就能把数猜出来。其实我们在不知不觉中已经运用了折半查找的思想。
理论上说,折半查找最多所需的比较次数是第一个大于数组元素个数的2的幂次数。以查找一个拥有1024个元素的数组为例,采用折半查找,在最坏的情况下只需10次比较。因为不断地用2来除1024得到的商分别是512、256、128、64、32、16、8、4、2、1,即1024()用2除10次就可以得到1。用2除一次就相当于折半查找算法中的一次比较。而线性查找发在最坏情况下,即查找键位于所有数据的尾部且数据量较大时,或者已知数据中不存在该值时,查找次数等于总的数据量大小。从平均情况来看,需要一半的数组元素(这里为512)与查找键进行比较。可见,两种查找算法在效率上可谓是天壤之别。
因此我们在查找数据时,如果能先使用排序算法对数据进行排序,再对数据进行折半查找,就能够大大提高我们的效率。鉴于排序算法在我之前的文章中已有提及,这里不再赘述:C语言丨交换法排序、C语言丨选择法排序、C语言丨冒泡法排序
假如我们有22 24 26 28 30五个数据,查找值x=28,我们可以先把第一个元素22的下标设置为low,最后一个元素30的下标设置为high,则中间的元素的下标mid=low+(high-low)/2(若使用mid=(high+low)/2计算mid的值,如果使用数组长度很大很大,使得low和high之和超出了limits.h中定义的有符号整数的极限值,那么执行到取数据区间中点的语句“mid=(high+low)/2;”时就会发生数值溢出,导致mid成为负数),那么我们查找的过程可展现如下:
数组下标01234第一次循环2224262830查找值x=28lowmidhighx>array[mid],low=mid+1第二次循环2224262830low(mid)highx=array[mid],找到 若查找值x=27,则查找过程如下:
第一次循环2224262830查找值x=27lowmidhighx>array[mid],low=mid+1第二次循环2224262830low(mid)highx<array[mid],high=mid-1第三次循环2224262830不满足low<=highhighlow循环结束,未找到 按此算法编写折半查找函数BinSearch()如下:
//按折半查找法查找值为x的数组元素,若找到则返回x在数组中的下标位置,否则返回-1 int BinSearch(int array[], int x, int n) { int low = 0, high = n-1, mid;//区间左端点low置为0,右端点high置为n-1 while (low <= high)//若左端点小于等于右端点,则继续查找 { mid = low + (high - low) / 2;//取数据区间的重点 if (x > array[mid]) low = mid + 1;//若x>array[mid],则修改区间的左端点 else if (x < array[mid]) high = mid - 1;//若x<array[mid],则修改区间的右端点 else return mid;//若找到,则返回下标值mid } return -1;//循环结束仍未找到,则返回值-1 } 以上程序是按待查找数据已按升序排列而写的;若待查找顺序已按降序排列,只需将if与else if语句中的大于号和小于号互换即可。