LwIP的ARP协议实现系列文章 LwIP中的ARP实现(1)之 ARP缓存表的数据结构
LwIP中的ARP实现(2)之 ARP缓存表的超时处理
LwIP中的ARP实现(3)之 发送ARP请求包
LwIP中的ARP实现(4)之 ARP数据包接收
LwIP中的ARP实现(5)之 ARP数据包发送 ARP数据包发送 我们知道一个数据包从底层传递进来的流程是怎么样的,如果是ARP数据包就会给ARP去处理,如果是IP数据报就使用ip4_input()函数传递到上层,这些处理在后面的章节讲解。那么如果上层协议想要发送数据,也肯定需要经过ARP协议将IP地址映射为MAC地址才能完成发送操作,IP数据报通过ip4_output()函数将上层数据包传递到ARP协议处理,关于IP协议是怎么样传递的我们暂且不说,那么ARP通过etharp_output()函数接收到IP数据报后,就会进行发送,ARP会先从数据包中进行分析,看看这个IP数据报是单播数据包还是多播或者是广播数据包,然后进行不同的处理:
对于多播或者是广播数据包,这种处理就很简单,直接将数据包丢给网卡就行了(调用ethernet_output()函数)。对于单播包的处理稍微麻烦一点,ARP协议需要根据IP地址找到对应的MAC地址,然后才能正确发送,如果找不到MAC地址的话,还要延迟发送数据包,ARP协议首先会创建一个ARP表项,然后将数据包挂到ARP表项对应的缓存队列上,与此同时会发出一个ARP请求包,等待目标主机的回应后再发送IP数据报。 此处需要注意的是,对于PBUFF_ERF、PBUF_POOL、PBUF_RAM类型的数据包是不允许直接挂到ARP表项对应的缓存队列上的,因为此时内核需要等待目标主机的ARP应答,而这段时间里,这些数据有可能会被上层改动,这是不允许的,所以LwIP需要将这些pbuf数据包拷贝到新的空间,等待发送。
etharp_output()函数被IP层的ip4_output()函数调用,IP层传递一个数据包到ARP中,etharp_output()会根据数据包的目标IP地址选择不同的处理。
err_t etharp_output(struct netif *netif, struct pbuf *q, const ip4_addr_t *ipaddr) { const struct eth_addr *dest; struct eth_addr mcastaddr; const ip4_addr_t *dst_addr = ipaddr; if (ip4_addr_isbroadcast(ipaddr, netif)) { /* 如果是广播数据包,目标MAC地址设置为FF-FF-FF-FF-FF-FF-FF */ dest = (const struct eth_addr *)ðbroadcast; } else if (ip4_addr_ismulticast(ipaddr)) { /* 如果是多播数据包,目标MAC地址设置为多播地址:01-00-5E-XX-XX-XX */ mcastaddr.addr[0] = LL_IP4_MULTICAST_ADDR_0; mcastaddr.addr[1] = LL_IP4_MULTICAST_ADDR_1; mcastaddr.addr[2] = LL_IP4_MULTICAST_ADDR_2; mcastaddr.
题意 给定两个字符串 text1 和 text2,返回这两个字符串的最长公共子序列的长度。
一个字符串的 子序列 是指这样一个新的字符串:它是由原字符串在不改变字符的相对顺序的情况下删除某些字符(也可以不删除任何字符)后组成的新字符串。
例如,"ace" 是 "abcde" 的子序列,但 "aec" 不是 "abcde" 的子序列。两个字符串的「公共子序列」是这两个字符串所共同拥有的子序列。
若这两个字符串没有公共子序列,则返回 0。
题解 class Solution { public: int longestCommonSubsequence(string text1, string text2) { int len1 = text1.size(); int len2 = text2.size(); int dp[len1+1][len2+1]; for(int i = 0; i <= len1; i++) for(int j = 0; j <= len2; j++) dp[i][j] = 0; for(int i = 1; i <= len1; i++){ for(int j = 1; j <= len2; j++){ if(text1[i-1] == text2[j-1]) dp[i][j] = dp[i-1][j-1] + 1; else dp[i][j] = max(dp[i-1][j], dp[i][j-1]); } } return dp[len1][len2]; } };
1.先来说说counter()函数
2.再来说说整句话是什么意思。
一、Counter函数,顾名思义就是计算函数,参数可以是list,也可以是dict比较灵活
*计算字符串中字母个数 >>> c = Counter('abcdeabcdabcaba') # count elements from a string >>> c.most_common(3) # 输出数量最多的前三个 [('a', 5), ('b', 4), ('c', 3)] *对Counter对象c进行元素筛选 >>> sorted(c) # list all unique elements ['a', 'b', 'c', 'd', 'e'] * >>> ''.join(sorted(c.elements())) # list elements with repetitions 'aaaaabbbbcccdde' *求Counter对象的总长度 >>> sum(c.values()) # total of all counts 15 *求Counter对象中某个元素的数量 >>> c['a'] # count of letter 'a' 5 *将新的字符串融入Counter对象 >>> for elem in 'shazam': # update counts from an iterable .
我是一个HTTP数据包,不知谁创建了我,把我丢到这个房间。
突然,来了一个大汉,我吓得缩到角落。
“该启程了,站起来”。
“去哪里啊?”我弱弱的问。
“还能去哪里,你是一个数据包,当然要出远门,完成你的使命啊,别啰嗦,我要先把你复制到内核空间,一会要发出去,让我来看下你的内容”。
我不敢多言,乖乖的站好,一眨眼的功夫,我来到了另一个房间。
“这是哪里,我怎么到这里来了?”我有点好奇。
“这里是内核地址空间,刚才你在用户态地址空间,所有的数据包都得从这里出发,你也不例外”。
看着这片陌生的环境,我也不知道他说的内核空间是什么意思,这里有好多房间,还有好多跟他一样的大汉在工作,好多数据包都在这里,进进出出,好不热闹。
出发前的俄罗斯套娃
“等着,我去拿点东西”,大汉去了一座大厦,我抬头一看,上面写着tcpip.sys。
这时,旁边另外一个数据包走了过来。
“唉,小子,刚出发呢。”
“你是谁?”
“我也是一个HTTP数据包,不过我是一个响应包,刚刚从遥远的Linux帝国过来这里,我马上就完成我的使命了,这一路给我累得”,这个数据包叹口气回答。
“很远吗,这么辛苦啊”,我开始有点担心我的旅程起来。
“这不好说,我不知道你去哪,接我的人来了,再见”。
不一会儿,大汉提着一个箱子走了出来,箱子正面写了三个大大的字母:TCP。
“这是传输层的箱子,快进去”,大汉命令的口吻。
“你要我钻进这个箱子啊?”我不太相信。
“对,没错,麻利的”。
“这箱子上面写的数字60059和80是什么意思?”我注意到箱子背面也写了很多东西。
“60059是创建你的人用的端口号码,80是后面接待你的人的端口号码”。
“那这第二排的0x2C877F30和0xBD62DFB3又是什么意思呢?”
“这是创建你的人使用的序列号和应答对方的确认号,你问题咋这么多”,大汉开始不耐烦了。
“还有这个···”
“行啦,快进去,赶时间”,大汉打断了我的提问,我乖乖的钻进了这个叫TCP的箱子。
“是不是可以出发了?”我问大汉。
“还早着呢,这才刚开始呢”,大汉笑着说。
“再等一下,我还要进去一趟”,说完大汉又进了这个tcpip.sys大厦。没过多久,大汉又出来了,手里又拎了一个更大的箱子,上面也写着两个大大的字母:IP。
这一次,大汉二话不说,直接把我连同TCP箱子一起丢进了这个IP箱子。
“怎么还要套一层啊”,我紧张的问他。
“你不懂,这个网络是分层的,刚才那个箱子是......唉,我跟你说这些干啥”,大汉提着我离开了tcpip.sys大厦。
我们来到了一个码头,这里数据包来来往往特别繁华,大汉把我带到一个大大的仓库,里面有很多数据包,对我说:“乖乖待在这里排队,我就先走了,一会儿会有人来把你发出去的。”,大汉拍拍我的肩膀就离开了。
“咦,你也是去222.196.242.24的,我也是呢,真巧!”前面一个数据包跟我打招呼。
我愣了一下:“你怎知我要去哪里?”
“喏,你这外面的箱子不是写了吗,你看我的,跟你的一样,这里写的就是咱们要去的地方的IP地址”,她特别激动。
第一站:网关
很快,来了一个小胖子叫到我了,我小心翼翼的走了过去。
“你这是要去公网啊,来,先去网关那里吧”,说完,小胖又给我套了一个箱子,前面写着:Ethernet II,背面写了一串:FE-D8-65-C8-2B-D7;86-D5-32-01-0E-3B。
“小哥,这个箱子又是干嘛的,这上面写的又是啥呢?”
“这个箱子是把你送到网关那里去的,FE-D8-65-C8-2B-D7就是网关的网卡地址,后面那个是咱这里的地址”,小胖说话倒是很客气。
“坐好了,要出发了哦”,小胖一顿操作,我好像坐上火箭,离开了这片土地。
途中见到了好多好多的数据包,有出发的,也有过来的,像穿越时空隧道一般梦幻。
还没来得及欣赏,就被一个黑脸的抓了下来,看来我是到小胖说的网关了。
黑脸人一脸冷漠,问他话也不答,只顾做自己的事情。几下功夫,便将我最外面的箱子拆掉,然后拿着一个手册查了下我的目的IP地址。
“好了,跟我来”,黑脸的把我带到另外一个码头,一顿操作把我给发了出去。
穿越比特宇宙
这里的数据包比刚才那里更多,更热闹。
一路上,不时有人把我抓下去,然后又从一个新的码头发出去,把我弄的晕头转向的。
过了好一会儿,到了一个地方,又一个胖子接待了我。
“大哥,我这是到终点了吗?”
“快了,你已经到目的网络网关了,下一站就到了”。
“来的路上那些人把我抓下去又发出来是干啥呢?”
“这叫网络路由,他们那些人接力才把你送到这里的”。
说完,这胖子又给我套上了一个Ethernet II的箱子发了出去,我知道,这是要送我去最终目的地了。
不知道是什么web服务器会来接待我呢,即将完成使命的我开始兴奋起来。
出师未捷身先死
很快,我又被人抓了下去。
“是不是我到站了啊,终于要完成任务了”,我伸伸懒腰。
那人却不说话,只是将我的Ethernet箱子拆去,放到了一个仓库。
没多久,来了一个大叔,把我带了出去。
“咱们是不是要去tcpip.sys大厦,把我的箱子都拆掉啊”,我小声的问。
“这里是Linux帝国,没有你说的大厦。不过你倒是猜对了,就是去拆箱子”。
很快,这位大叔就拆掉了我的IP箱子和TCP箱子,把我放到一个房间。
“在这等着,一会儿有人会来找你”。
“是不是web服务器的人来找我?”我激动的问。
网络安全
事件描述由于经常购买服务器 ,某天,发现一台服务器上面虽然安装了 360 ,但是 ,仍然提示入侵提示 。让我特别纳闷,于是开始调查下原因
首先设想了三种可能性:1.存在系统漏洞2.由于前期运维在服务器上装了一些工具软件,会不会工具软件引入的病毒3.应用层漏洞。
首先,用更新库的漏扫对系统层面的漏洞检测,未发现任何异常;
由于会有开发连接进这台服务器,去开发那里收集工具软件进行查毒处理,也没发现异常,排除通过软件带入病毒的可能;那难道是通过应用层漏洞进来的?
因为系统上线前都会经过web渗透测试,文件上传,SQL注入等常规漏洞已经修复,虽然这样,还是重新验证了一遍漏洞,没有问题,又用D盾webshell检测工具进行了扫面,未发现任何webshell。
那病毒是怎么产生的?
溯源准备
由于病毒无法清干净,也不清楚黑客是已经在机器上做了哪些手脚,于是重新搭建一个干净的环境,给系统打好最新的补丁。
由于前期没有在主机端做日志收集类工具,缺乏主机端的攻击溯源手段,于是临时搭建了splunk日志分析系统,并在新搭建的服务器上安装了sysmon日志收集工具,对主机层面进行了日志收集。
过了一星期左右,小Z发现系统进程里面居然多了个叫wcmoye的进程,凭感觉这不是个正常程序,那就先从这个程序开始入手调查吧。
常规排查
常规排查还是采用了微软经典系统工具systeminternals套件,分别对启动项,系统进程,网络连接等简单做了排查。
启动项除了services这一项发现了一个奇怪的StuvwxAbcdefg Jkl,其他没有特别值得注意的地方。
进程排查就是那个叫wcmoye.exe的进程
进程依赖于StuvwxAbcdefgh Jkl这个服务
网络通信:用tcpview观察wcmoye.exe会不定时连接一公网ip的9999端口
同时会有一些注册表及文件系统上的行为,确定wcmoye躲在C:windowssyswow64目录下
初步排查得出的结论是:wcmoye进程依赖于名叫Stuvwx Abcdefg Jkl系统服务,去syn链接公网ip的9999端口,是个木马程序。
在对wcmoye有了一定认识之后,小Z想它是从哪里来的,这时,之前搭建的日志分析系统派上了用场。
0×4 日志排查
这个问题得从wcmoye.exe在系统中产生的第一时间着手调查。于是打开splunk开始搜索:通过 wcmoye关键字的搜索,发现6月6日20:24发生如下可疑事件:
20:24:11 Tomcat目录下有一个叫NewRat的可执行文件生成wcmoye.exe,原来wcmoye是有一个叫NewRat的可执行文件生成的,但是回到Tomcat目录下查看,并没有发现NewRat.exe这个文件.
不急,进一步搜索NewRat,发现了更大的信息量:在wcmoye被创建的前一秒 20:24:10,tomcat7.exe去调用cmd.exe执行了一段比较长的脚本,
随着时序跟踪事件的发展,发现在20:24:12 调用cmd.exe删除了NewRat.exe
同时还观察到services.exe的执行,系统服务创建
关注sysmon的EventCode 3 ,wcmoye的进程会与下载NewRat的那个公网ip的9999端口有通信日志,
其实到这里,wcmoye是从哪里进来的已经基本搞清楚了,接下来的问题就是为什么会进来?Tomcat为什么去执行这些恶意命令?现在唯一的线索就是日志中的那个ftp登陆的ip以及账号密码了,继续吧。
0×5 顺藤摸瓜
带着好奇心,继续探索过程,直接进入了这个ftp服务器!
使用FileZilla进入ftp服务器的目录,以一目十行地速度快速扫了一遍,首先蹦入小Z眼帘的就是NewRat.exe,不错,和前面的调查结果相吻合,NewRat就安静地躺在这里。
还有个独特专版st2-045 winlinux小组版文件夹,潜意识告诉这个文件夹里面很可能有谜底的答案,先直接百度一下
好家伙,双系统传马还Kill国内外主流杀毒软件,关键是st2-045这个就是远程代码执行(RCE)漏洞(S2-045,CVE-2017-5638),不禁一颤,之前居然没想到测试这个高危的提权漏洞。
start.bat开始看吧
有一个叫wincmd.txt的文件,是winows平台下的执行脚本,红框的内容和前面splunk日志中的那段日志一模一样,也就是帮引导到这里的那段关键日志。
Linux平台的脚本:关闭防火墙,下载一个叫tatada的ELF文件,把netstat等系统命令改名,清空日志等等
Result.txt文件,记录着一些扫描到的ip的端口开放情况
Windows.txt和linux.txt里面貌似都是存在漏洞的网址。。。
而且其中有一个关键的发现,就是所在公司的网站接口居然在一个叫http.txt的list里面
到这里,已经大致猜得出自己的公司网站是怎么被盯上的了。再看下几个可执行文件:
S.exe就是扫描器
IDA载入str045
看得出Str045.exe就是struts2-045的利用脚本程序,他会去读取S.exe扫描出的ip及端口开放情况的文件,组合do,action等开启多线程去exploit,然后根据被攻击的系统版本,去执行相应的脚本,像这台web服务器是windows的,就会去执行wincmd.txt。
0×6 网络架构
目前调查到的种种迹象让坚信黑客是通过struts2-45漏洞进来的!于是去网上下载了一个最新的struts漏洞检查工具,直接对网站的80端口进行检测,但结果出乎意料,居然没有漏洞报警。
黑客服务器上只有针对strusts2-045的攻击脚本,但是检测又没有发现漏洞。这个矛盾的问题不禁思考更多的可能性。
在陷入迷茫的深思同时, 不经意的翻看着tomcat的localhost_access_log日志,突然一批ABAB型日志出现在他眼前,一个公网地址,一个内网地址,时间就在NewRat出现的前几分钟20:20:36:
这串高度相关的日志 究竟隐藏着什么意义?会不会是解开谜团的入口?带着强烈的好奇心,咨询了网络组的同事,什么情况下才会出现这样的情况,网络组给出了网站如下的网络架构,并说明了由于业务的临时需求,新对网络架构做了新的调整。
点击上方“小白学视觉”,选择“星标”公众号
重磅干货,第一时间送达
本文转载自知乎Artrix
https://zhuanlan.zhihu.com/p/35755039
五一劳动节假期,我们一起来玩扫雷吧。用Python+OpenCV实现了自动扫雷,突破世界记录,我们先来看一下效果吧。
中级 - 0.74秒 3BV/S=60.81
相信许多人很早就知道有扫雷这么一款经典的游(显卡测试)戏(软件),更是有不少人曾听说过中国雷圣,也是中国扫雷第一、世界综合排名第二的郭蔚嘉的顶顶大名。扫雷作为一款在Windows9x时代就已经诞生的经典游戏,从过去到现在依然都有着它独特的魅力:快节奏高精准的鼠标操作要求、快速的反应能力、刷新纪录的快感,这些都是扫雷给雷友们带来的、只属于扫雷的独一无二的兴奋点。
▍0x00 准备 准备动手制作一套扫雷自动化软件之前,你需要准备如下一些工具/软件/环境
- 开发环境
Python3 环境 - 推荐3.6或者以上 [更加推荐Anaconda3,以下很多依赖库无需安装]
numpy依赖库 [如有Anaconda则无需安装]
PIL依赖库 [如有Anaconda则无需安装]
opencv-python
win32gui、win32api依赖库
支持Python的IDE [可选,如果你能忍受用文本编辑器写程序也可以]
- 扫雷软件
· Minesweeper Arbiter 下载地址(必须使用MS-Arbiter来进行扫雷!)
好啦,那么我们的准备工作已经全部完成了!让我们开始吧~
▍0x01 实现思路 在去做一件事情之前最重要的是什么?是将要做的这件事情在心中搭建一个步骤框架。只有这样,才能保证在去做这件事的过程中,尽可能的做到深思熟虑,使得最终有个好的结果。我们写程序也要尽可能做到在正式开始开发之前,在心中有个大致的思路。
对于本项目而言,大致的开发过程是这样的:
完成窗体内容截取部分
完成雷块分割部分
完成雷块类型识别部分
完成扫雷算法
好啦,既然我们有了个思路,那就撸起袖子大力干!
- 01 窗体截取
其实对于本项目而言,窗体截取是一个逻辑上简单,实现起来却相当麻烦的部分,而且还是必不可少的部分。我们通过Spy++得到了以下两点信息:
class_name = "TMain"title_name = "Minesweeper Arbiter " ms_arbiter.exe的主窗体类别为"TMain"
ms_arbiter.exe的主窗体名称为"Minesweeper Arbiter "
注意到了么?主窗体的名称后面有个空格。正是这个空格让笔者困扰了一会儿,只有加上这个空格,win32gui才能够正常的获取到窗体的句柄。
本项目采用了win32gui来获取窗体的位置信息,具体代码如下:
hwnd = win32gui.FindWindow(class_name, title_name)if hwnd: left, top, right, bottom = win32gui.
一起学习,一起成长!
前言 时间序列(time series)数据都是一种重要的结构化数据形式。
时间序列数据的意义取决于具体应用场景,主要有一下几种:
时间戳(timestamp),特定的时刻。固定时期(period),如2007年1月或2010年全年。时间间隔(interval),由起始和结束时间戳表示。时期(period)可以被看做间隔(interval)的特例。实验或过程时间,每个时间点都是相对于特定起始时间的一个度量。例如,从放入烤箱起,每秒钟饼干的直径。 Pandas提供了一组标准的时间序列处理工具和数据算法。因此,可以高效处理非常大的时间序列,轻松地进行切片/切块、聚合、对定期/不定期的时间序列进行重采样等。
日期和时间数据类型及工具 Python标准库包含用于日期(date)和时间(time)数据的数据类型,而且还有日历方面的功能。主要用到datetime、time以及calendar模块。datetime.datetime(也可以简写为datetime)是用得最多的数据类型:
In [97]: from datetime import datetime
In [98]: now=datetime.now()
In [99]: now
Out[99]: datetime.datetime(2018, 9, 16, 11, 2, 28, 854799)
In [100]: now.year,now.month,now.day
Out[100]: (2018, 9, 16)
In [6]: now.year
Out[6]: 2019
In [7]: now.month
Out[7]: 7
In [8]: now.day
Out[8]: 14
datetime以毫秒形式存储日期和时间。
datetime.timedelta:时间差 表示两个datetime对象之间的时间差:
In [101]: delta=datetime(2011,1,7)-datetime(2008,6,24,8,15)
In [102]: delta
Out[102]: datetime.timedelta(926, 56700)
In [103]: delta.days
Out[103]: 926
In [104]: delta.
文章目录 一. 前言二. 动态规划Ⅰ. 什么是动态规划?Ⅱ. 动态规划的求解过程 三. LeetCode 198.打家劫舍四. LeetCode 213. 打家劫舍Ⅱ五. LeetCode 64. 最小路径和六. LeetCode 62. 不同路径七. LeetCode 413. 等差数列的数量八. LeetCode 343. 整数划分九. 背包问题0-1背包问题完全背包问题多重背包问题 十. LeetCode 416. 分割等和子集十一. LeetCode 279. 完全平方数十二. LeetCode 91. 解码方法十三. LeetCode 300. 最长上升子序列十四. LeetCode 646. 最长对数链十五. LeetCode 376. 摆动序列十六. LeetCode 1143. 最长公共子序列 (LCS算法)十七. LeetCode 494. 目标和十八. LeetCode 474. 一和零十九. LeetCode 322. 零钱兑换二十. LeetCode 518. 零钱兑换Ⅱ二十一. LeetCode 139. 单词划分二十二. LeetCode 377. 组合总和 IV二十三. LeetCode 72. 编辑距离二十四.
贪心算法 总是选择当前看起来最优的选择(局部最优解),得到的结果是一个整体最优解。
但是总是选择局部最优解并不总是能得到整体最优解,需要在问题具有:贪心选择性和优化子结构时才成立。
贪心选择性:第一次做出贪心选择是正确的;
优化子结构:第一次做完贪心选择后,得到一个与原问题定义相同(但输入不同)的子问题;
贪心算法的基本要素 贪心选择性质 1.贪心选择性质是指所求问题的整体最优解可以通过一系列局部最优的选择,即贪心选择来达到。这是贪心算法可行的第一个基本要素,也是贪心算法与动态规划算法的主要区别。
2.贪心算法通常以自顶向下的方式进行,一迭代的方式作出相继的贪心选择,每作一次贪心选择就将所求问题简化为规模更小的子问题。动态规划通常以自底向上的方式解决子问题。
最优子结构性质 当一个问题的最优解包含其子问题的最优解时,称此问题具有最优子结构性质。问题的最优子结构性质是该问题可用动态规划算法或贪心算法求解的关键特征。
贪心算法的求解过程 问题的定义
优化解的结构分析
算法设计
算法复杂性
算法正确性证明
活动安排问题定义 定义(活动)
设有n个活动的集合E={1,2,…,n},其中每个活动都要求使用同一资源,而在同一时间内只有一个活动能使用这个资源。
定义(相容活动)
若区间[si, fi)与区间[sj, fj)不相交,则称活动i与活动j是相容的。也就是说,当si≥fj或sj≥fi时,活动i与活动j相容。
活动安排问题的贪心算法思路 1.将各个活动按照活动结束时间fi排序,且f1<=f2<=f3…
2.选择活动1,要求该活动的结束时间最早
3.从2开始按顺序比较各个活动,选择第一个与活动1相容的活动i
4.从i+1开始按顺序考察各个活动,选择第一个与活动 i 相容的活动 j
**每次选择与现有活动相容的结束时间最早的活动 **
举例详解算法过程 现在给出下列活动,s表示开始时间,f表示结束时间;
根据算法,我们首选选择结束时间最早的活动作为第一个活动,第一个活动为:1
然后选择开始时间大于第一个活动的结束时间的活动,而且该活动的结束时间要求最早。显然是活动4;
同理,寻找开始时间晚于活动4的结束时间且结束时间最早的活动,一直到到无活动可选为止。
复杂度分析 算法可以得到最优解的证明 证明贪心算法可以得到最优解要求:
证明第一次选择是正确的证明第一次选择后,问题输入变为与第一个活动相容的活动的子问题
(因为第二个选择的活动 i 是 E’ 中结束时间最早的,所以活动 i 是正确的
依次类推所有的选择都是正确的)
目录
一、Anaconda的安装
二、创建虚拟环境
三、设置镜像
四、Pycharm中导入anaconda环境
五、安装第三方库采坑集锦
一、Anaconda的安装 1.下载地址
https://www.anaconda.com/download/
2.历史版本
anaconda所有版本链接:https://repo.continuum.io/archive/
清华大学开源软件镜像站:https://mirrors.tuna.tsinghua.edu.cn/anaconda/archive/
下载的时候选择与python对应的版本下载,然后是傻瓜式安装。
安装过程注意:
红框内容要选中,然后等待安装,过程会较长。
安装完成后进入cmd,测试
二、创建虚拟环境 在anaconda prompt下输入命令:
1.安装新环境
zzy_python37是自定义的环境名,3.7是python的版本
conda create -n env_name python=X.X 比如: conda create -n zzy_python37 python=3.7 2.复制某个虚拟环境
conda create --name new_env_name --clone old_env_name 3.列出所有已有虚拟环境
*表示当前你所在的环境中
conda env list 或 conda info -e 4.删除某个虚拟环境
conda env remove -n env_name 或 conda remove -name env_name -all 5.查看已安装的包
conda list 6.在某个虚拟环境中安装包
(若进入该虚拟环境中,env_name可以省略)
conda install -n env_name package_name conda install -n zzy_python matplotlib=3.
文章目录 一. 贪心算法二. LeetCode 455. 分配饼干三. LeetCode 435. 不重叠的区间个数四. LeetCode 452. 投飞镖刺破气球五. LeetCode 406. 根据身高和序号重组队列六. LeetCode 121. 买卖股票最大的利益七. LeetCode 122.买卖股票的最大利益Ⅱ八. LeetCode 605. 种植花朵九. 总结 一. 贪心算法 贪心算法的实质就是,直接解决问题太难,于是寻找一个看起来比较tricky的解法,核心思想是先寻找 局部最优解,进而整合起来的结果仍然是 整体最优解。因为寻找局部最优解的过程,可以忽略很多情况条件(因为它考虑的是局部,而非整体),因此才显得比较“贪心”。但要注意的是,每一个地方都是局部最优解,最终的全局并不一定是整体的最优解,所以贪心算法只有在特定的题目才可以使用。
那么什么题目才可以使用贪心算法?自然是具体问题具体分析,关键在于前面的最优解是否会对后续的过程产生影响。直接证明比较难,一般是通过反证法来证明。贪心算法有点像直觉上的不成形算法,关键在于这个局部最优解最后是否能演变成整体最优解,一般可以通过反证法来判断是否能得到整体最优解。接下来,我们通过几道LeetCode的题目来进行操作。
二. LeetCode 455. 分配饼干 455.Assign Cookies (Easy)
Leetcode / 力扣
Input: grid[1,3], size[1,2,4] Output: 2 题目描述:每个孩子都有一个满足度 grid,每个饼干都有一个大小 size,只有饼干的大小大于等于一个孩子的满足度,该孩子才会获得满足。求解最多可以获得满足的孩子数量。
分析:一个饼干只能分配给一个小孩子。这道题,直觉上的思路:优先分配给满足度最小的孩子,并且给一个小孩子分配饼干的时候,应该分配尽量小但又能满足那个小孩子的饼干,留着更大的饼干用于给满足度比较大的孩子。这个思想应该很好理解,其实这就已经是 贪心算法,为什么?因为优先给当前满足度最小的孩子分配尽量小的饼干,这就是一个 局部最优解 的情况。那么,虽然直觉上是认为这样就是正确答案,也就是这个局部最优解,最后会演化成 整体最优解,但如果要专业的证明,如何证明?答案就是开头所说的,反证法。
证明:假设在某次选择中,我们的贪心策略选择:给当前满足度最小的孩子分配第m个饼干,并且第m个饼干是可以满足该孩子的最小饼干。假设这并不一定是全局最优解,也就是存在一种更优的策略,可以给该孩子分配第n个饼干,并且m < n。我们可以发现,经过这一轮的分配,贪心策略可以剩下一个饼干n,而假设的更优策略,留下的是饼干m。又由于m < n,所以在后续的分配中,贪心策略一定能满足更多的孩子。所以不存在比贪心策略更优的策略,即贪心策略是最优策略。
总结:贪心算法看起来确实就是一种直觉上的算法,根据具体问题分析,得出局部最优解,进而获得全局最优解。所以后续的题目直接忽略证明(主要是因为懒得证明),讲述贪心算法的思路即可。(这道题还用到了双指针)
class Solution { // 优先给满足度最小的分配尽量小的饼干,局部最优达到全局最优 public int findContentChildren(int[] g, int[] s) { Arrays.
环境说明:
jdk启动的jar所在系统的版本:CentOS Linux release 7.6.1810 (Core)
jdk版本:jdk-8u261-linux-x64
客户端win10,和服务端使用同一个jdk版本
Centos7.6替换自带的jre安装jdk 先确保安装了jdk。由于我本身安装的是jre,所以先卸载,然后安装jdk。
操作方法如下:
查询已安装的java程序
# rpm -qa | grep java-1.8java-1.8.0-openjdk-1.8.0.232.b09-0.el7_7.x86_64java-1.8.0-openjdk-headless-1.8.0.232.b09-0.el7_7.x86_64 卸载jre
# rpm -e --nodeps java-1.8.0-openjdk-1.8.0.232.b09-0.el7_7.x86_64# rpm -e --nodeps java-1.8.0-openjdk-headless-1.8.0.232.b09-0.el7_7.x86_64参考:https://www.cnblogs.com/cannel/p/11104178.html
jvisualvm 添加 jstatd 连接,远程监控Jvm 在java安装目录:/usr/java/default/lib 新建文件all.policy
# cd /usr/java/default/lib# vi all.policy 给all.policy添加如下内容:
grant codebase "file:/usr/java/default/lib/tools.jar" { permission java.security.AllPermission;}; 通过jstatd启动RMI服务,执行:
$ rmiregistry -J-Djava.rmi.server.codebase=file:/usr/java/default/lib/tools.jar 1099 &$ jstatd -J-Djava.security.policy=/usr/java/default/lib/all.policy -p 1099 -J-Djava.rmi.server.hostname=47.xxx.xxx.xx2 & hostname注意需要配置为公网ip
开启JVM的JMX监控 示例:
java -Dcom.sun.management.jmxremote.port=7199 -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false -Djava.rmi.server.hostname=47.xxx.xxx.xx2 -Xms512m -Xmx512m -jar k12platform-admin-8087.
Ctrl + Shift + F或点击左侧导航栏的“放大镜”按钮,在SEARCH文本框点击最右侧图标开启正则匹配模式,在SEARCH文本框中输入^b*[^:b#/]+.*$,如果有需要的话,在文本过滤器中选择需要统计的代码文本(files to include)和需要排除统计的代码文本(files to exclude)。统计结果中的results个数即为代码行数。
原文来源:数据运营与数据分析
1.先建立数据 # 创建表demo02 CREATE TABLE demo02 ( id int PRIMARY KEY AUTO_INCREMENT COMMENT "编号", name CHAR(20) NOT NULL COMMENT "名字", sex CHAR(5) DEFAULT "未知" COMMENT "性别", age int(10) COMMENT "年龄", qualifications CHAR(40) COMMENT "学历", interests CHAR(40) COMMENT "爱好" ) ENGINE= INNODB character set utf8mb4; # 插入数据 INSERT INTO demo02 (id,name,sex,age,qualifications,interests) VALUES (1,"xr","男",26,"本科","篮球"), (2,"hw","男",27,"硕士","游泳"), (3,"wp","男",26,"本科","游戏"), (4,"ljl","男",26,"本科","桌球"), (5,"cl","女",26,"硕士","小说"), (6,"hl","女",25,"专科","小吃"), (7,"cb","女",25,"本科",null), (8,"cy","男",26,"专科","调酒"), (9,"cj","女",24,"博士"," "), (10,"fj","男",27,"硕士","宅男"), (11,"jl","未知",27,"本科","") ; # 查看数据 SELECT * FROM demo02; 从查询结果中我们可以看出空值null和空字符串的显示是不相同的
Base64简介
Base64是基于64个可打印字符(小写字母a-z,大写字母A-Z,数字0-9,符号"+","/" 再加上作为垫字的"=",实际有65个字符,其他的所有字符都要转换成这个字符集中的字符),用来表示任意二进制数据的方法。
Base64使用缘故
在二进制数据的传输中,常常包含很多无法显示和打印的字符,想要他们正常显示出来,就需要一个二进制到字符串的转换方法。Base64就是一种最常见的二进制编码方法。 常见应用就是Base64用于在HTTP协议下传输数据。由于HTTP协议是超文本传输协议,所以需要将在HTTP协议下传输的二进制数据转换成字符数据,而且网络传输只能传输可打印字符,而在实际网络传输中,传输的数据并不都是可打印字符,比如中文、二进制文件,图片等。为了成功传输这些类型的数据,此时Base64就派上大用场了。
什么是可打印字符:在ASCII码中规定,十进制数字0-31属于非打印控制字符,32-127属于可打印字符(32-126在键盘上能找到对应的字符,32代表键盘中的空格,127代表键盘中的DELETE命令)
Base64编码原理
按照从左往右的规则,每三个字节作为一组,一共就是24个二进制位。将这24个二进制位分为四组,每组6个二进制位。之后在每组数据前面添加00,组成每个组8个二进制位,此时变成了32个二进制位,即四个字节然后根据下表,得到扩展后的每个字节的对应符号,base64编码方法完成 具体编码演示 "G"、"o"、"d" 三个字符的ASCII值分别是71、111、100,二进制值是01000111、01101111、01100100 ,然后就组成了24位的二进制值010001110110111101100100将这24个二进制位分成四组,分别得到010001、110110、111101、100100在每组前面添加00,得到00010001、00110110、00111101、00100100根据上表,得到每个值对应Base64编码,分别是:R、2、9、k 所以,得到字符串"God"的Base64编码为R29k
如果一组没有三个字节,则是这样处理的
两个字节的情况:
将这两个字节一共16个二进制位,按照上面的规则分成三组,最后一组除了前面加上00之外,后面也要加上00,这样就得到一个三位的Base64编码,再在末尾补上一个"="号。
比如,"Go" 这个字符串是两个字节,可以转化成三组 00010001,00110110,00111100,对应Base64编码分别为:h、3、8,再加上一个"=" 号,因此"Go"的Base64编码就是h38=
一个字节的情况:
将这一个字节一共8个二进制位,按照上面的规则分成两组,最后一组除了前面加上00之外,后面也要加上0000,这样就得到一个两位的Base64编码,再在末尾补两个"="号。
比如,"G" 这个字符串是一个字节,可以转化成两组 00010001,00110000,对应Base64编码分别为:R、w,再加上两个"=" 号,因此"G"的Base64编码就是Rw==
Java代码实现Base64编码与解码
public static void main(String[] args) throws UnsupportedEncodingException { //编码 String encode = Base64.getEncoder().encodeToString("God".getBytes()); System.out.println(encode); //解码 System.out.println(new String(Base64.getDecoder().decode(encode))); } 复制代码 打印结果:
R29k God 复制代码 附件:
assic码表
Python运算符
分为两类:计算结果是值、计算结果是布尔值(True,False)
一、计算结果是值
1.算数运算符
包括加+、减-、乘*、除/、乘方**、取余数%、取整除//
如:
2**3=8;
10%3=1;
10//3=3;
2.赋值运算符
包括=、+=、-=、*=、/=、**=、%=、//=
如:
a=1的含义为,将1赋值给a;
a+=1的含义为,将a+1的值赋值给a,等同于a=a+1;
其余参考上条。
注:部分算术、赋值运算符同样可以与字符串(str)使用
如:
text1=10
text2='10'
text3='feng'
print(text1*5)
print(text2*5)
print(text3*5)
print(text2+text3)
运行得出:
50
1010101010
fengfengfengfengfeng
10feng
二、计算结果是布尔值
1.比较运算符
包括==、>、<、>=、<=、!=(同<>)
比较运算符只返回布尔值(True、False)
如:
1==1返回True;
1>2返回False;
!=为不等号,1!=1返回False。
2.逻辑运算符
包括and(逻辑与)、or(逻辑或)、not(逻辑非)
一般情况下,逻辑运算符and、or两边都为比较运算符的式子,即两边都为布尔值。
()and()只有当两边都为True时,返回True,否则返回False;
()or()只需两边有一个为True,即返回True,否则返回False;
not()为括号内参数布尔值的反转。
特殊情况,很少遇到,以后可能补充。
3.成员运算符
包括in、not in,可用于字符串(Str)、列表(List)与元祖(Tuple)
()in()如果在后方序列中找到前面的参数,则返回 True,否则返回 False;
()not in()如果在后方序列中不能找到前面的参数,则返回 True,否则返回 False。
暂时写这么多...
文章目录 前言HashMap原理构造方法:put方法:get方法hash方法resize方法其他一些常见问题:TODO 前言 在准备面试的过程中,发现HashMap源码是很常见的考点,于是进行了仔细的学习。都说读源码是快速提高Java水平的好途径,在阅读了HashMap的部分重要源码之后真的是深有体会,于是写下此篇文章做记录。具体内容包括,HashMap的构造方法,put,get方法,以及put&get所需要的hash方法,还有扩容时所需要的resize方法。我们将对这几个方法的源码进行阅读,理解它的逻辑,以及个中的一些巧妙设计。
HashMap原理 首先,什么是HashMap?HashMap是基于Map接口的实现,存储键值对时,它可以接收null的键值,是非同步的,HashMap存储着Entry(hash, key, value, next)对象。
常用的方法有:
构造方法,可以定义initialCapacity初始容量,factor负载因子。threshold = initialCapacity * factor
put,get,二者需要用到hash方法,也就是散列函数。
resize:放数组容量不足时,元素个数大于threshold时,就要扩容。
HashMap使用链表数组来存储数据(数组的每一项都是一个链表),JDK1.8开始,当链表的长度到达一定程度,就会把该链表转换成红黑树。
构造方法: 构造方法一共有4个:
显然,第一个就是没有参数,此时会设置默认的初始容量initialCapacity和默认的负载因子default_factor。
对于第二个,实际上就是传入自定义的初始容量,将float参数设置为默认的负载因子default_factor。
对于第四个,是传入一个Map对象进行初始化。我们重点看第三个构造方法:
public HashMap(int initialCapacity, float loadFactor) { if (initialCapacity < 0) throw new IllegalArgumentException("Illegal initial capacity: " + initialCapacity); if (initialCapacity > MAXIMUM_CAPACITY) initialCapacity = MAXIMUM_CAPACITY; if (loadFactor <= 0 || Float.isNaN(loadFactor)) throw new IllegalArgumentException("Illegal load factor: " + loadFactor); this.loadFactor = loadFactor; this.threshold = tableSizeFor(initialCapacity); } public HashMap(int initialCapacity) { this(initialCapacity, DEFAULT_LOAD_FACTOR); } 前面的都是判断一下边界值,就省略了。
检查的流程及解决方案 1.1 表现症状1.2 使用步骤1.3 解决方案1.4 结束 1.1 表现症状 Vscode php debug F5后没有反应。
运行处也没有Debug调试,无法自动配置launch.json
1.2 使用步骤 先在vscode中安装PHP Debug,在文件-首选项-设置配置"php.validate.executablePath"项,填写对应版本的php.exe。 //填写你本地的php.exe路径 "php.validate.executablePath": "E:/tools/phpstudy_pro/Extensions/php/php7.3.4nts/php.exe", 按照PHP Debug插件的要求需要在php.ini中进行如下配置 [XDebug] xdebug.remote_enable = 1 xdebug.remote_autostart = 1 上述配置即打开XDebug调试组件,我本地为PHPStudy 在下图所示的位置配置好之后一定要再看一下php.ini文件中是否上有上述两行代码
按F5调试,选择PHP,就可以了,可能会配置失败。按 Ctrl+Shift+D 打开调试面板,点击上面的小齿轮打开launch.json,如果出现"Listen for XDebug"和"Launch currently open script",就证明成功了,如果没有出现 看解决方案~ 1.3 解决方案 即按 1.2 操作后没有出现Listen for XDebug 和 Launch currently open script,那么则需要手动添加
{ "name": "Listen for XDebug", "type": "php", "request": "launch", "port": 9000 },{ "name": "Launch currently open script", "
从事WiFi嵌入式软件开发的同学,802.11协议层抓包分析是一个需要熟练掌握的一个技能,需要通过分析WiFi底层802.11协议层的数据包来定位问题。同时从学习802.11协议的角度而言,最有效的学习方法就是通过抓包来学习,从抓到的数据包中,可以验证之前理论学习部分的知识,通过对数据包的理解,不仅可以加深对802.11协议的理解,还能提高自己在实际使用中问题分析、解决能力。
由于要抓到802.11协议层的数据包需要无线网络进入混杂模式,在不同的操作系统下需要不同的软硬件配合才能实现,本文分3部分依次介绍:
Windows下Microsoft Network MonitorWindows下OmnipeekUbuntu下Wireshark
在不同操作系统下实现802.11数据包捕获分析。 Microsoft Network Monitor 嵌入式开发工程师开发环境通用使用Windows,由于Windows下很多专业的抓包工具都需要特定的无线网卡和驱动支持才可实现混杂模式802.11数据捕获。在某些场景下,这些专业的抓包工具不一定会随身携带。所以先介绍一个Windows下Microsoft官方提供的轻量型的抓包工具Microsoft Network Monitor。
Microsoft Network Monitor是Microsoft官方提供的一款免费网络协议数据分析工具,不仅可以抓包,而且可以基于抓包结果做一些简单的数据分析。Microsoft Network Monitor当前最新版本为3.4,同时支持32位和64位系统,可在Microsoft官方免费下载,下载地址为:https://www.microsoft.com/en-us/download/details.aspx?id=4865。我使用的系统是Win10_64位系统,电脑自带的网卡驱动不支持混杂模式,测试发现外接了360USBWiFi可实现混杂模式。
软件安装过程较简单,没有需要特别注意的地方。需要注意的是,安装完成后需要重新启动一下电脑,否则软件可能无法正确显示当前网络列表。
以管理员身份运行Microsoft Network Monitor 3.4,在“Select Networks”中会正确显示当前全部网卡,可以通过Friendly Name和Description找到我们要抓包的网卡。
一、普通模式 Microsoft Network Monitor 默认是“Local Mode”,该模式下软件只能捕获当前选中的网卡收发的数据,是无法捕获到没有经过该网卡的数据的。
选中网卡后,点击“New Capture”,在弹出的页面中选中“Start”即可开始抓包。
可点击“Pause”暂停当前抓包;点击“Stop”停止当前抓包,“Stop”之后再次“Start”将清除当前已捕获到的数据。
在“Frame Summary”框内可发现软件支持捕获到WiFi管理帧、TCP、UDP等数据包,同时软件还可显示“Process Name”、“Source”、“Destination”等网络信息。
分析以上数据,我们可以发现,当前抓到的数据都是当前网卡收发的数据,即使802.11管理帧,也是当前网卡发给路由器的。
二、混杂模式 我们在WiFi开发过程中定位问题通常需要通过无线网卡,捕获路由器与其他WiFi设备之间通讯的数据,普通模式下软件并没有捕获到。Microsoft Network Monitor支持无线网卡进入“Monitor Mode”,该模式即为混杂模式,此时可捕获路由器与其他WiFi设备之间的通讯数据包。
点击“Capture Settings”进入设置页面,可以看到当前选中的无线网卡- 双击需要设置“Monitor Mode”的无线网卡,点击“Scanning Options”
在“WiFi Scanning Options”页面中,选中“Switch to Monitor Mode”,然后继续选择信道扫描模式,软件支持2种信道切换模式,固定信道和信道扫描的模式。在信道扫描模式下,每一个信道逗留一会,按照时间片顺序,一个个信道开始抓包,这里需要根据实际需要设置。
设置完成后,点击“Apply”,并且需要保持该页面为开启状态不能关闭,否则无线网卡自动退出“Monitor Mode”。返回主界面,再次点击“Start”开始“Monitor Mode”下捕获
从“Frame Summary”可以看到捕获到了各种802.11管理帧,同时捕获到了很多“Source”和“Destination”不一样的数据包。选择需要具体分析的数据包,可在“Frame Details”和“Hex Details”中查看详细数据包数据点击“File”下面选择“Save As”,可将当前捕获数据保存为“.cap”,可供“wireshark”、“Omnipeek”等工具分析。 三、总结 从实际使用中发现,Microsoft Network Monitor功能无法与“wireshark”、“Omnipeek”等专业抓包软件相媲美,但是能满足日常基本的802.11数据包捕获、802.11协议学习。上手快,受限也较少,更多功能也需要在使用在不断发掘。
把maven依赖:
<dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>3.0.1</version> </dependency> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>3.0.1</version> </dependency> 换为:
<dependency> <groupId>jakarta.servlet.jsp</groupId> <artifactId>jakarta.servlet.jsp-api</artifactId> <version>3.0.0</version> <scope>provided</scope> </dependency> <dependency> <groupId>jakarta.servlet</groupId> <artifactId>jakarta.servlet-api</artifactId> <version>5.0.0</version> <scope>provided</scope> </dependency> tomcat10把 javax.servlet 都改为了jakarta.servlet