[{"categories":["技术"],"collections":["博客搭建"],"content":"以工作/学习/技术/生活四大类为基础，设计一级+二级分类体系，统一分类维度，明确归类原则。","date":"2026-10-16","objectID":"/posts/blog-taxonomy-design/","tags":["博客","Hugo","信息架构","分类设计"],"title":"博客分类体系设计：工作/学习/技术/生活四大类方案","uri":"/posts/blog-taxonomy-design/"},{"categories":["技术"],"collections":["博客搭建"],"content":"一、为什么选择四大类方案 本站内容跨度较大，涵盖工作、学习、技术、生活四个方面。在设计分类体系时，面临两个选择： 扁平方案：一级分类直接对应内容领域（如网络测试、算法、编程、系统……） 层次方案：一级分类对应用途场景，二级分类细分内容领域 扁平方案的优点是少点击、定位快，缺点是分类维度不统一——有的按工作领域分，有的按学科分，有的按技术栈分，容易混乱。 层次方案的优点是维度统一、结构清晰，缺点是需要二级分类，多一层导航。 本站最终选择层次方案，以「工作/学习/技术/生活」四大类为一级分类，每类下设 2-3 个二级分类。 核心理由：四大类的维度是统一的——都是**\u0026ldquo;用途/场景\u0026quot;维度**，这比混合维度更清晰、更易维护。 ","date":"2026-10-16","heading":"一、为什么选择四大类方案","objectID":"/posts/blog-taxonomy-design/:1:0","tags":["博客","Hugo","信息架构","分类设计"],"title":"博客分类体系设计：工作/学习/技术/生活四大类方案","uri":"/posts/blog-taxonomy-design/#一为什么选择四大类方案"},{"categories":["技术"],"collections":["博客搭建"],"content":"二、分类维度总览 四大类的一级分类，维度完全统一： 一级分类 维度 本质 工作 用途/场景 职业产出 学习 用途/场景 知识输入 技术 用途/场景 技能实践 生活 用途/场景 非技术日常 统一维度的好处： 归类时只需问一个问题：\u0026ldquo;我为什么写这篇文章？\u0026rdquo; 不会出现\u0026quot;这个算工作还是算学科\u0026quot;的维度冲突 分类体系稳定，不易随内容变化而重构 统一维度的代价： 某些一级分类会过大（如\u0026quot;技术\u0026rdquo;），需要二级分类细分 归类时\u0026quot;用途\u0026quot;有时难以判断（如\u0026quot;为工作学的算法\u0026quot;） ","date":"2026-10-16","heading":"二、分类维度总览","objectID":"/posts/blog-taxonomy-design/:2:0","tags":["博客","Hugo","信息架构","分类设计"],"title":"博客分类体系设计：工作/学习/技术/生活四大类方案","uri":"/posts/blog-taxonomy-design/#二分类维度总览"},{"categories":["技术"],"collections":["博客搭建"],"content":"三、一级 + 二级分类结构 工作\r├── 网络测试\r└── 项目记录\r学习\r├── 算法\r├── 数学\r└── 物理\r技术\r├── 编程\r├── 系统\r└── 工具\r生活\r├── 随笔\r└── 育儿 一级分类：4 个（工作/学习/技术/生活） 二级分类：10 个 ","date":"2026-10-16","heading":"三、一级 + 二级分类结构","objectID":"/posts/blog-taxonomy-design/:3:0","tags":["博客","Hugo","信息架构","分类设计"],"title":"博客分类体系设计：工作/学习/技术/生活四大类方案","uri":"/posts/blog-taxonomy-design/#三一级--二级分类结构"},{"categories":["技术"],"collections":["博客搭建"],"content":"四、四大类详解 ","date":"2026-10-16","heading":"四、四大类详解","objectID":"/posts/blog-taxonomy-design/:4:0","tags":["博客","Hugo","信息架构","分类设计"],"title":"博客分类体系设计：工作/学习/技术/生活四大类方案","uri":"/posts/blog-taxonomy-design/#四四大类详解"},{"categories":["技术"],"collections":["博客搭建"],"content":"1. 工作 维度：职业产出 包含：网络测试、项目记录 二级分类： 网络测试——测试理论、实践、工具、调优 项目记录——具体项目的过程、复盘、踩坑 典型 tags：TCP、DPDK、iperf、压测、调优、复盘 边界处理：工作中的学习（如为项目学的新技术）算工作还是学习？ 为完成工作而学 → 工作 独立于工作的知识积累 → 学习 ","date":"2026-10-16","heading":"1. 工作","objectID":"/posts/blog-taxonomy-design/:4:1","tags":["博客","Hugo","信息架构","分类设计"],"title":"博客分类体系设计：工作/学习/技术/生活四大类方案","uri":"/posts/blog-taxonomy-design/#1-工作"},{"categories":["技术"],"collections":["博客搭建"],"content":"2. 学习 维度：知识输入 包含：算法、数学、物理 二级分类： 算法——算法导论、数据结构、复杂度 数学——线性代数、微积分、概率论 物理——量子力学、电磁学、力学 典型 tags：动态规划、图论、线性代数、量子力学 边界处理：学习中的编程练习算学习还是技术？ 主题是算法/数学知识 → 学习 主题是编程技巧 → 技术 ","date":"2026-10-16","heading":"2. 学习","objectID":"/posts/blog-taxonomy-design/:4:2","tags":["博客","Hugo","信息架构","分类设计"],"title":"博客分类体系设计：工作/学习/技术/生活四大类方案","uri":"/posts/blog-taxonomy-design/#2-学习"},{"categories":["技术"],"collections":["博客搭建"],"content":"3. 技术 维度：技能实践 包含：编程、系统、工具 二级分类： 编程——语言、框架、设计模式 系统——Linux、macOS、树莓派、Shell 工具——Git、Vim、Docker、编辑器 典型 tags：Python、Go、Linux、Git、Docker 边界处理：技术里的算法实现算技术还是学习？ 主题是算法原理 → 学习 主题是工程实现 → 技术 ","date":"2026-10-16","heading":"3. 技术","objectID":"/posts/blog-taxonomy-design/:4:3","tags":["博客","Hugo","信息架构","分类设计"],"title":"博客分类体系设计：工作/学习/技术/生活四大类方案","uri":"/posts/blog-taxonomy-design/#3-技术"},{"categories":["技术"],"collections":["博客搭建"],"content":"4. 生活 维度：非技术日常 包含：随笔、育儿 二级分类： 随笔——感想、记录、杂谈 育儿——孩子日常、教育 典型 tags：育儿、随笔、感想 边界处理：生活中的技术折腾（如家庭 NAS）算生活还是技术？ 主题是生活应用 → 生活 主题是技术本身 → 技术 ","date":"2026-10-16","heading":"4. 生活","objectID":"/posts/blog-taxonomy-design/:4:4","tags":["博客","Hugo","信息架构","分类设计"],"title":"博客分类体系设计：工作/学习/技术/生活四大类方案","uri":"/posts/blog-taxonomy-design/#4-生活"},{"categories":["技术"],"collections":["博客搭建"],"content":"五、归类判断原则 ","date":"2026-10-16","heading":"五、归类判断原则","objectID":"/posts/blog-taxonomy-design/:5:0","tags":["博客","Hugo","信息架构","分类设计"],"title":"博客分类体系设计：工作/学习/技术/生活四大类方案","uri":"/posts/blog-taxonomy-design/#五归类判断原则"},{"categories":["技术"],"collections":["博客搭建"],"content":"核心口诀 先问\u0026quot;为什么写\u0026quot;，再问\u0026quot;写什么\u0026quot;。 为工作产出 → 工作 为知识输入 → 学习 为技能实践 → 技术 为生活记录 → 生活 ","date":"2026-10-16","heading":"核心口诀","objectID":"/posts/blog-taxonomy-design/:5:1","tags":["博客","Hugo","信息架构","分类设计"],"title":"博客分类体系设计：工作/学习/技术/生活四大类方案","uri":"/posts/blog-taxonomy-design/#核心口诀"},{"categories":["技术"],"collections":["博客搭建"],"content":"判断示例 文章 一级 二级 理由 TCP 拥塞控制原理 工作 网络测试 服务于工作 网卡压测实战 工作 网络测试 工作产出 项目 X 复盘 工作 项目记录 工作复盘 快速排序详解 学习 算法 知识输入 线性代数笔记 学习 数学 知识输入 量子力学笔记 学习 物理 知识输入 Python 装饰器 技术 编程 技能实践 Linux 进程管理 技术 系统 技能实践 Git 分支策略 技术 工具 技能实践 用 Python 写监控脚本 技术 编程 技能实践 周末带娃记录 生活 育儿 生活记录 年度总结 生活 随笔 生活记录 ","date":"2026-10-16","heading":"判断示例","objectID":"/posts/blog-taxonomy-design/:5:2","tags":["博客","Hugo","信息架构","分类设计"],"title":"博客分类体系设计：工作/学习/技术/生活四大类方案","uri":"/posts/blog-taxonomy-design/#判断示例"},{"categories":["技术"],"collections":["博客搭建"],"content":"易混淆边界 Q：为工作学的算法，算工作还是学习？ 为完成具体工作 → 工作 通用知识积累 → 学习 Q：技术文章里的数学推导，算技术还是学习？ 数学是主题 → 学习 数学是工具 → 技术 Q：家庭 NAS 搭建，算生活还是技术？ 主题是生活应用 → 生活 主题是技术方案 → 技术 Q：博客搭建文章，算技术还是生活？ 主题是技术实践（Hugo 配置、taxonomy 设计） → 技术 主题是生活记录（博客感想） → 生活 ","date":"2026-10-16","heading":"易混淆边界","objectID":"/posts/blog-taxonomy-design/:5:3","tags":["博客","Hugo","信息架构","分类设计"],"title":"博客分类体系设计：工作/学习/技术/生活四大类方案","uri":"/posts/blog-taxonomy-design/#易混淆边界"},{"categories":["技术"],"collections":["博客搭建"],"content":"六、Hugo 实现方式 Hugo 原生不支持多级 categories，有三种实现方式： ","date":"2026-10-16","heading":"六、Hugo 实现方式","objectID":"/posts/blog-taxonomy-design/:6:0","tags":["博客","Hugo","信息架构","分类设计"],"title":"博客分类体系设计：工作/学习/技术/生活四大类方案","uri":"/posts/blog-taxonomy-design/#六hugo-实现方式"},{"categories":["技术"],"collections":["博客搭建"],"content":"方式 A：单层 categories，用命名约定 categories: [\"工作/网络测试\"] 优点：Hugo 原生支持 缺点：斜杠在 URL 里需要处理，部分主题显示不友好 ","date":"2026-10-16","heading":"方式 A：单层 categories，用命名约定","objectID":"/posts/blog-taxonomy-design/:6:1","tags":["博客","Hugo","信息架构","分类设计"],"title":"博客分类体系设计：工作/学习/技术/生活四大类方案","uri":"/posts/blog-taxonomy-design/#方式-a单层-categories用命名约定"},{"categories":["技术"],"collections":["博客搭建"],"content":"方式 B：categories + subcategories 两个 taxonomy（推荐） [taxonomies]\rcategory = \"categories\"\rsubcategory = \"subcategories\"\rtag = \"tags\"\rcollection = \"collections\" categories: [\"工作\"]\rsubcategories: [\"网络测试\"] 优点：结构清晰，两个独立 taxonomy 缺点：需要主题支持，二级分类页面需单独配置 ","date":"2026-10-16","heading":"方式 B：categories + subcategories 两个 taxonomy（推荐）","objectID":"/posts/blog-taxonomy-design/:6:2","tags":["博客","Hugo","信息架构","分类设计"],"title":"博客分类体系设计：工作/学习/技术/生活四大类方案","uri":"/posts/blog-taxonomy-design/#方式-bcategories--subcategories-两个-taxonomy推荐"},{"categories":["技术"],"collections":["博客搭建"],"content":"方式 C：categories 只放一级，二级用 tags categories: [\"工作\"]\rtags: [\"网络测试\", \"TCP\", \"压测\"] 优点：最简单，Hugo 原生 缺点：二级分类和普通 tag 混在一起，不够\u0026quot;正式\u0026quot; 本站采用方式 B，最符合\u0026quot;一级 + 二级\u0026quot;的语义。 ","date":"2026-10-16","heading":"方式 C：categories 只放一级，二级用 tags","objectID":"/posts/blog-taxonomy-design/:6:3","tags":["博客","Hugo","信息架构","分类设计"],"title":"博客分类体系设计：工作/学习/技术/生活四大类方案","uri":"/posts/blog-taxonomy-design/#方式-ccategories-只放一级二级用-tags"},{"categories":["技术"],"collections":["博客搭建"],"content":"hugo.toml 配置 [taxonomies]\rcategory = \"categories\"\rsubcategory = \"subcategories\"\rtag = \"tags\"\rcollection = \"collections\" ","date":"2026-10-16","heading":"hugo.toml 配置","objectID":"/posts/blog-taxonomy-design/:6:4","tags":["博客","Hugo","信息架构","分类设计"],"title":"博客分类体系设计：工作/学习/技术/生活四大类方案","uri":"/posts/blog-taxonomy-design/#hugotoml-配置"},{"categories":["技术"],"collections":["博客搭建"],"content":"文章 front matter 模板 ---\rtitle: \"文章标题\"\rdate: 2026-10-16\rdraft: false\rcategories: [\"工作\"] # 一级，4 选 1\rsubcategories: [\"网络测试\"] # 二级，按一级对应\rtags: [\"TCP\", \"原理\"] # 自由\rcollections: [\"网卡测试实战\"] # 可选，系列\rdescription: \"文章摘要\"\r--- ","date":"2026-10-16","heading":"文章 front matter 模板","objectID":"/posts/blog-taxonomy-design/:6:5","tags":["博客","Hugo","信息架构","分类设计"],"title":"博客分类体系设计：工作/学习/技术/生活四大类方案","uri":"/posts/blog-taxonomy-design/#文章-front-matter-模板"},{"categories":["技术"],"collections":["博客搭建"],"content":"一级与二级的对应关系 一级 category 可选二级 subcategories 工作 网络测试、项目记录 学习 算法、数学、物理 技术 编程、系统、工具 生活 随笔、育儿 ","date":"2026-10-16","heading":"一级与二级的对应关系","objectID":"/posts/blog-taxonomy-design/:6:6","tags":["博客","Hugo","信息架构","分类设计"],"title":"博客分类体系设计：工作/学习/技术/生活四大类方案","uri":"/posts/blog-taxonomy-design/#一级与二级的对应关系"},{"categories":["技术"],"collections":["博客搭建"],"content":"七、Tags 与 Collections 的配合 ","date":"2026-10-16","heading":"七、Tags 与 Collections 的配合","objectID":"/posts/blog-taxonomy-design/:7:0","tags":["博客","Hugo","信息架构","分类设计"],"title":"博客分类体系设计：工作/学习/技术/生活四大类方案","uri":"/posts/blog-taxonomy-design/#七tags-与-collections-的配合"},{"categories":["技术"],"collections":["博客搭建"],"content":"Tags：细分维度 Tags 用于同一二级分类内的细分： # 网络测试的理论文章\rcategories: [\"工作\"]\rsubcategories: [\"网络测试\"]\rtags: [\"TCP\", \"原理\", \"拥塞控制\"]\r# 网络测试的实践文章\rcategories: [\"工作\"]\rsubcategories: [\"网络测试\"]\rtags: [\"iperf\", \"压测\", \"调优\"] 好处：二级分类下理论和实践都在，读者一览无余；想只看理论，点 tag 原理。 ","date":"2026-10-16","heading":"Tags：细分维度","objectID":"/posts/blog-taxonomy-design/:7:1","tags":["博客","Hugo","信息架构","分类设计"],"title":"博客分类体系设计：工作/学习/技术/生活四大类方案","uri":"/posts/blog-taxonomy-design/#tags细分维度"},{"categories":["技术"],"collections":["博客搭建"],"content":"Collections：系列维度 Collections 用于跨分类的系列文章： collections: [\"算法导论笔记\", \"网卡测试实战\", \"Python 进阶\", \"树莓派折腾记\"] 好处：把同一系列的零散文章聚合起来，不受分类限制。 ","date":"2026-10-16","heading":"Collections：系列维度","objectID":"/posts/blog-taxonomy-design/:7:2","tags":["博客","Hugo","信息架构","分类设计"],"title":"博客分类体系设计：工作/学习/技术/生活四大类方案","uri":"/posts/blog-taxonomy-design/#collections系列维度"},{"categories":["技术"],"collections":["博客搭建"],"content":"八、设计原则总结 一级分类维度统一——四大类都是\u0026quot;用途/场景\u0026quot;维度，避免混合维度。 二级分类按需添加——不必全部预设，按实际内容增长添加。 每个二级分类至少 5 篇——否则不值得独立，可先并入一级。 用 tags 细分，不用分类——语言、工具、具体技术，都放 tags。 先问\u0026quot;为什么写\u0026quot;——归类时以写作目的为准，而非内容主题。 可以动态调整——先少后多，比先多后少好。 ","date":"2026-10-16","heading":"八、设计原则总结","objectID":"/posts/blog-taxonomy-design/:8:0","tags":["博客","Hugo","信息架构","分类设计"],"title":"博客分类体系设计：工作/学习/技术/生活四大类方案","uri":"/posts/blog-taxonomy-design/#八设计原则总结"},{"categories":["技术"],"collections":["博客搭建"],"content":"九、未来调整预案 如果某领域内容增长，可考虑调整： 触发条件 调整方案 \u0026ldquo;网络测试\u0026quot;内容超过 20 篇 从\u0026quot;工作\u0026quot;拆出，升为一级分类 \u0026ldquo;算法\u0026quot;内容超过 20 篇 从\u0026quot;学习\u0026quot;拆出，升为一级分类 \u0026ldquo;编程\u0026quot;内容超过 20 篇 从\u0026quot;技术\u0026quot;拆出，升为一级分类 某二级分类长期不足 3 篇 并入一级，或合并到相近二级 出现新领域（如\u0026quot;阅读\u0026rdquo;） 评估是否新增一级或二级 原则：先少后多，按实际增长调整，不预设。 ","date":"2026-10-16","heading":"九、未来调整预案","objectID":"/posts/blog-taxonomy-design/:9:0","tags":["博客","Hugo","信息架构","分类设计"],"title":"博客分类体系设计：工作/学习/技术/生活四大类方案","uri":"/posts/blog-taxonomy-design/#九未来调整预案"},{"categories":["技术"],"collections":["博客搭建"],"content":"十、与扁平方案的对比 对比项 扁平方案（6 个 categories） 四大类方案（4+10） 维度 混合（工作/学科/技术栈/生活） 统一（用途/场景） 一级数量 6 4 二级数量 无 10 优点 一级即定位，扁平 维度统一，层次清晰 缺点 维度不统一 需要二级 taxonomy 导航 6 个入口 4 个入口 + 二级 适合 内容分布不均 内容分布较均匀 核心差异： 扁平方案：一级分类直接对应内容领域，扁平，但维度混合 四大类方案：一级分类对应用途场景，统一，但需要二级细分 选择建议： 重视维度统一、结构清晰 → 四大类方案 重视扁平、少点击 → 扁平方案 ","date":"2026-10-16","heading":"十、与扁平方案的对比","objectID":"/posts/blog-taxonomy-design/:10:0","tags":["博客","Hugo","信息架构","分类设计"],"title":"博客分类体系设计：工作/学习/技术/生活四大类方案","uri":"/posts/blog-taxonomy-design/#十与扁平方案的对比"},{"categories":["技术"],"collections":["博客搭建"],"content":"十一、一句话总结 本站采用「工作/学习/技术/生活」四大类为一级分类，维度统一（用途/场景）；每类下设 2-3 个二级分类，共 10 个。 归类时先问\u0026quot;为什么写\u0026rdquo;：为工作产出→工作，为知识输入→学习，为技能实践→技术，为生活记录→生活。 Hugo 用 categories + subcategories 两个 taxonomy 实现。 ","date":"2026-10-16","heading":"十一、一句话总结","objectID":"/posts/blog-taxonomy-design/:11:0","tags":["博客","Hugo","信息架构","分类设计"],"title":"博客分类体系设计：工作/学习/技术/生活四大类方案","uri":"/posts/blog-taxonomy-design/#十一一句话总结"},{"categories":["工作"],"collections":null,"content":"华为OD第一次技术面手撕题解析","date":"2024-03-11","objectID":"/posts/interview/huawei_od/","tags":["算法题"],"title":"记华为 OD 技术面一手撕题","uri":"/posts/interview/huawei_od/"},{"categories":["工作"],"collections":null,"content":"华为OD技术面一，手撕题正好遇到不太熟悉的前缀树。总体思路是想到了， 但是一些细节还是没有考虑到，比如： 想到用字典模拟树结构，但是没想到用嵌套字典，导致构造前缀树的 复杂度没有降下来。 想到要建立前缀树，但是还想在建树的同时进行连接词的判断，导致 建树的复杂度上升，有更多的细节问题需要考虑。应该将建树和判断连接词 分别考虑，遍历两次，这样既不需要按单词长度进行排序这步预操作，在代码 实现上也容易得多。 想到要对单词进行连接词判断，但是没有想到用递归的方式，和前两个欠 考虑之处有点关系。 一旦采用递归来判断连接词，那么各种优化操作也更容易实现，比如： 维护已知为连接词的单词集合，加速未处理单词的判断过程。 ","date":"2024-03-11","heading":"","objectID":"/posts/interview/huawei_od/:0:0","tags":["算法题"],"title":"记华为 OD 技术面一手撕题","uri":"/posts/interview/huawei_od/#"},{"categories":["工作"],"collections":null,"content":"题目 描述：给你一个不含重复单词的字符串数组 words，请你找出并返回 words 中的所有连接词。 连接词：一个完全由给定数组中的至少两个较短单词组成的字符串。 提示： 1 \u0026lt;= words.length \u0026lt;= 104 0 \u0026lt;= words[i].length \u0026lt;= 1000 words[i] 仅由小写字母组成 0 \u0026lt;= sum(words[i].length) \u0026lt;= 105 样例： 输入：words = [\u0026quot;cat\u0026quot;, \u0026quot;cats\u0026quot;, \u0026quot;catsdogcats\u0026quot;, \u0026quot;dog\u0026quot;, \u0026quot;dogcatsdog\u0026quot;, \u0026quot;hippopotamuses\u0026quot;, \u0026quot;rat\u0026quot;, \u0026quot;ratcatdogcat\u0026quot;] 输出：[\u0026quot;catsdogcats\u0026quot;, \u0026quot;dogcatsdog\u0026quot;, \u0026quot;ratcatdogcat\u0026quot;] 解释： \u0026quot;catsdogcats\u0026quot; 由 \u0026quot;cats\u0026quot;，\u0026quot;dog\u0026quot; 和 \u0026quot;cats\u0026quot; 组成； \u0026quot;dogcatsdog\u0026quot; 由 \u0026quot;dog\u0026quot;，\u0026quot;cats\u0026quot; 和 \u0026quot;dog\u0026quot; 组成； \u0026quot;ratcatdogcat\u0026quot; 由 \u0026quot;rat\u0026quot;，\u0026quot;cat\u0026quot;，\u0026quot;dog\u0026quot; 和 \u0026quot;cat\u0026quot; 组成； ","date":"2024-03-11","heading":"题目","objectID":"/posts/interview/huawei_od/:1:0","tags":["算法题"],"title":"记华为 OD 技术面一手撕题","uri":"/posts/interview/huawei_od/#题目"},{"categories":["工作"],"collections":null,"content":"思路 遍历两次给定数组，第一遍构造前缀树，第二遍寻找连接词。 构造前缀树：用嵌套字典模拟树结构，用列表保存节点属性：嵌套字典表示的子树和 布尔值标识当前节点是否为单词结尾。 寻找连接词：递归回溯查找。 ","date":"2024-03-11","heading":"思路","objectID":"/posts/interview/huawei_od/:2:0","tags":["算法题"],"title":"记华为 OD 技术面一手撕题","uri":"/posts/interview/huawei_od/#思路"},{"categories":["工作"],"collections":null,"content":"解答 #!/usr/bin/env python def insert(strees, s): \"\"\" 构建字符串的前缀树，用嵌套字典表示树结构，列表存储节点属性。 strees: {u: [{}, True/False]} \"\"\" d = strees for c in s[:-1]: if c not in d: d[c] = [{}, False] d = d[c][0] c = s[-1] if c not in d: d[c] = [{}, False] d[c][1] = True def search(strees, s, count, visited): \"\"\" 回溯算法 s: 待判断是否为连接词的子串 count: 当前分割的单词集合 visited: 当前判定为连接词的单词集合 \"\"\" # 如果先前判定过 s 为连接词，则直接返回 if s in visited: return 2 d = strees for i, c in enumerate(s[:-1]): if c not in d: # 一定在递归调用过程中，否则 c 必在 d 中。 # 上一步分割无解，返回 0 return 0 if d[c][1]: # 尝试在下标 i 处分割 count.add(s[: i + 1]) remain = search(strees, s[i + 1 :], count, visited) # 剪枝 if remain \u003c 2: # 下标 i 处作分割无解，寻找下一个分割点 count.remove(s[: i + 1]) d = d[c][0] else: # 分割成功，不需要找出所有方案，直接返回 return remain else: d = d[c][0] c = s[-1] if c in d and d[c][1]: count.add(s) return len(count) else: # 意味着 s 无法匹配 return 0 def getConnectedString(arr): \"\"\" arr: [word1, ...] \"\"\" strees = {} for word in arr: insert(strees, word) ans = set() for word in arr: count = search(strees, word, set(), ans) if count \u003e= 2: ans.add(word) return list(ans)","date":"2024-03-11","heading":"解答","objectID":"/posts/interview/huawei_od/:3:0","tags":["算法题"],"title":"记华为 OD 技术面一手撕题","uri":"/posts/interview/huawei_od/#解答"},{"categories":["生活"],"collections":null,"content":"Python绘制冰墩墩","date":"2022-02-17","objectID":"/posts/bingdundun/","tags":["冰墩墩","冬奥会"],"title":"冰墩墩——2022北京冬奥会吉祥物","uri":"/posts/bingdundun/"},{"categories":["生活"],"collections":null,"content":" 送给老婆的冰墩墩 倾情奉献 参考代码1 import turtle turtle.speed(100) # 速度 # 左手 turtle.penup() turtle.goto(177, 112) turtle.pencolor(\"lightgray\") turtle.pensize(3) turtle.fillcolor(\"white\") turtle.begin_fill() turtle.pendown() turtle.setheading(80) turtle.circle(-45, 200) turtle.circle(-300, 23) turtle.end_fill() # 左手内 turtle.penup() turtle.goto(182, 95) turtle.pencolor(\"black\") turtle.pensize(1) turtle.fillcolor(\"black\") turtle.begin_fill() turtle.setheading(95) turtle.pendown() turtle.circle(-37, 160) turtle.circle(-20, 50) turtle.circle(-200, 30) turtle.end_fill() # 轮廓 # 头顶 turtle.penup() turtle.goto(-73, 230) turtle.pencolor(\"lightgray\") turtle.pensize(3) turtle.fillcolor(\"white\") turtle.begin_fill() turtle.pendown() turtle.setheading(20) turtle.circle(-250, 35) # 左耳 turtle.setheading(50) turtle.circle(-42, 180) # 左侧 turtle.setheading(-50) turtle.circle(-190, 30) turtle.circle(-320, 45) # 左腿 turtle.circle(120, 30) turtle.circle(200, 12) turtle.circle(-18, 85) turtle.circle(-180, 23) turtle.circle(-20, 110) turtle.circle(15, 115) turtle.circle(100, 12) # 右腿 turtle.circle(15, 120) turtle.circle(-15, 110) turtle.circle(-150, 30) turtle.circle(-15, 70) turtle.circle(-150, 10) turtle.circle(200, 35) turtle.circle(-150, 20) # 右手 turtle.setheading(-120) turtle.circle(50, 30) turtle.circle(-35, 200) turtle.circle(-300, 23) # 右侧 turtle.setheading(86) turtle.circle(-300, 26) # 右耳 turtle.setheading(122) turtle.circle(-53, 160) turtle.end_fill() # 右耳内 turtle.penup() turtle.goto(-130, 180) turtle.pencolor(\"black\") turtle.pensize(1) turtle.fillcolor(\"black\") turtle.begin_fill() turtle.pendown() turtle.setheading(120) turtle.circle(-28, 160) turtle.setheading(210) turtle.circle(150, 20) turtle.end_fill() # 左耳内 turtle.penup() turtle.goto(90, 230) turtle.setheading(40) turtle.begin_fill() turtle.pendown() turtle.circle(-30, 170) turtle.setheading(125) turtle.circle(150, 23) turtle.end_fill() # 右手内 turtle.penup() turtle.goto(-180, -55) turtle.fillcolor(\"black\") turtle.begin_fill() turtle.setheading(-120) turtle.pendown() turtle.circle(50, 30) turtle.circle(-27, 200) turtle.circle(-300, 20) turtle.setheading(-90) turtle.circle(300, 14) turtle.end_fill() # 左腿内 turtle.penup() turtle.goto(108, -168) turtle.fillcolor(\"black\") turtle.begin_fill() turtle.pendown() turtle.setheading(-115) turtle.circle(110, 15) turtle.circle(200, 10) turtle.circle(-18, 80) turtle.circle(-180, 13) turtle.circle(-20, 90) turtle.circle(15, 60) turtle.setheading(42) turtle.circle(-200, 29) turtle.end_fill() # 右腿内 turtle.penup() turtle.goto(-38, -210) turtle.fillcolor(\"black\") turtle.begin_fill() turtle.pendown() turtle.setheading(-155) turtle.circle(15, 100) turtle.circle(-10, 110) turtle.circle(-100, 30) turtle.circle(-15, 65) turtle.circle(-100, 10) turtle.circle(200, 15) turtle.setheading(-14) turtle.circle(-200, 27) turtle.end_fill() # 右眼 # 眼圈 turtle.penup() turtle.goto(-64, 120) turtle.begin_fill() turtle.pendown() turtle.setheading(40) turtle.circle(-35, 152) turtle.circle(-100, 50) turtle.circle(-35, 130) turtle.circle(-100, 50) turtle.end_fill() # 眼珠 turtle.penup() turtle.goto(-47, 55) turtle.fillcolor(\"white\") turtle.begin_fill() turtle.pendown() turtle.setheading(0) turtle.circle(25, 360) turtle.end_fill() turtle.penup() turtle.goto(-45, 62) turtle.pencolor(\"darkslategray\") turtle.fillcolor(\"darkslategray\") turtle.begin_fill() turtle.pendown() turtle.setheading(0) turtle.circle(19, 360) turtle.end_fill() turtle.penup() turtle.goto(-45, 68) turtle.fillcolor(\"black\") turtle.begin_fill() turtle.pendown() turtle.setheading(0) turtle.circle(10, 360) turtle.end_fill() turtle.penup() turtle.goto(-47, 86) turtle.pencolor(\"white\") turtle.fillcolor(\"white\") turtle.begin_fill() turtle.pendown() turtle.setheading(0) turtle.circle(5, 360) turtle.end_fill() # 左眼 # 眼圈 turtle.penup() turtle.goto(51, 82) turtle.fillcolor(\"black\") turtle.begin_fill() turtle.pendown() turtle.setheading(120) turtle.circle(-32, 152) turtle.circle(-100, 55) turtle.circle(-25, 120) turtle.circle(-120, 45) turtle.en","date":"2022-02-17","heading":"","objectID":"/posts/bingdundun/:0:0","tags":["冰墩墩","冬奥会"],"title":"冰墩墩——2022北京冬奥会吉祥物","uri":"/posts/bingdundun/#"},{"categories":["技术"],"collections":null,"content":"在树莓派4B上搭建Hugo静态网站","date":"2021-09-27","objectID":"/posts/hugo-on-raspberrypi/","tags":["Hugo","raspberrypi"],"title":"Hugo on 树莓派","uri":"/posts/hugo-on-raspberrypi/"},{"categories":["技术"],"collections":null,"content":"安装最新版golang 目前，树莓派官方系统中，默认golang版本为1.11，无法支持DoIt主题所需的一些特性，因此卸载golang1.11，手动安装最新版。 截止本文发布，golang最新版为1.17.1，用如下脚本下载并安装 LATEST_VERSION=$(curl https://golang.org/VERSION?m=text) ARCH=armv6l wget -e \"https_proxy=http://127.0.0.1:1082\" https://golang.org/dl/$(LATEST_VERSION).linux-$ARCH.tar.gz # 解压至 tar -zxvf -C ~/Program/Go/latest_version $(LATEST_VERSION).linux-$ARCH.tar.gz # 将相应目录添加至PATH变量，并验证安装无误 go version ","date":"2021-09-27","heading":"安装最新版golang","objectID":"/posts/hugo-on-raspberrypi/:1:0","tags":["Hugo","raspberrypi"],"title":"Hugo on 树莓派","uri":"/posts/hugo-on-raspberrypi/#安装最新版golang"},{"categories":["技术"],"collections":null,"content":"安装最新版Hugo 从GitHub下载Hugo最新源码包，并用如下命令进行编译安装 wget hugo_latest_download_url unzip file_name cd inside_the_extracted_folder go build --tags extended # 将hugo二进制文件复制到 cp hugo ~/Program/Hugo/latest_version/bin/ 由于golang.org被墙，源码安装Hugo前需要设置代理。国内最可靠的Go模块代理设置方法如下 # Go 1.13及以上（推荐） go env -w GO111MODULE=on go env -w GOPROXY=https://goproxy.cn,direct # 或者在终端执行 export GO111MODULE=on export GOPROXY=https://goproxy.cn ","date":"2021-09-27","heading":"安装最新版Hugo","objectID":"/posts/hugo-on-raspberrypi/:2:0","tags":["Hugo","raspberrypi"],"title":"Hugo on 树莓派","uri":"/posts/hugo-on-raspberrypi/#安装最新版hugo"},{"categories":["技术"],"collections":null,"content":"ECharts {\"grid\":{\"bottom\":\"5%\",\"containLabel\":true,\"left\":\"5%\",\"right\":\"5%\",\"top\":\"20%\"},\"legend\":{\"data\":[\"邮件营销\",\"联盟广告\",\"视频广告\",\"直接访问\",\"搜索引擎\"],\"top\":\"10%\"},\"series\":[{\"data\":[120,132,101,134,90,230,210],\"name\":\"邮件营销\",\"stack\":\"总量\",\"type\":\"line\"},{\"data\":[220,182,191,234,290,330,310],\"name\":\"联盟广告\",\"stack\":\"总量\",\"type\":\"line\"},{\"data\":[150,232,201,154,190,330,410],\"name\":\"视频广告\",\"stack\":\"总量\",\"type\":\"line\"},{\"data\":[320,332,301,334,390,330,320],\"name\":\"直接访问\",\"stack\":\"总量\",\"type\":\"line\"},{\"data\":[820,932,901,934,1290,1330,1320],\"name\":\"搜索引擎\",\"stack\":\"总量\",\"type\":\"line\"}],\"title\":{\"left\":\"center\",\"text\":\"折线统计图\",\"top\":\"2%\"},\"toolbox\":{\"feature\":{\"saveAsImage\":{\"title\":\"保存为图片\"}}},\"tooltip\":{\"trigger\":\"axis\"},\"xAxis\":{\"boundaryGap\":false,\"data\":[\"周一\",\"周二\",\"周三\",\"周四\",\"周五\",\"周六\",\"周日\"],\"type\":\"category\"},\"yAxis\":{\"type\":\"value\"}}","date":"2021-09-27","heading":"ECharts","objectID":"/posts/hugo-on-raspberrypi/:3:0","tags":["Hugo","raspberrypi"],"title":"Hugo on 树莓派","uri":"/posts/hugo-on-raspberrypi/#echarts"},{"categories":["技术"],"collections":null,"content":"了解Hugo建站的基本流程，配置文件维护等","date":"2021-09-22","objectID":"/posts/theme-documentation-basics/","tags":["Hugo"],"title":"Hugo建站-基础教程","uri":"/posts/theme-documentation-basics/"},{"categories":["技术"],"collections":null,"content":"1 准备 Hugo提供全平台（Windows,Linux,macOS），安装最新版本\u0026nbsp;Hugo (\u0026gt;0.62.0) ","date":"2021-09-22","heading":"1 准备","objectID":"/posts/theme-documentation-basics/:1:0","tags":["Hugo"],"title":"Hugo建站-基础教程","uri":"/posts/theme-documentation-basics/#1-准备"},{"categories":["技术"],"collections":null,"content":"2 安装 按照以下步骤初始化新网站 ","date":"2021-09-22","heading":"2 安装","objectID":"/posts/theme-documentation-basics/:2:0","tags":["Hugo"],"title":"Hugo建站-基础教程","uri":"/posts/theme-documentation-basics/#2-安装"},{"categories":["技术"],"collections":null,"content":"2.1 创建项目 使用new命令创建新网站： hugo new site my_site ","date":"2021-09-22","heading":"2.1 创建项目","objectID":"/posts/theme-documentation-basics/:2:1","tags":["Hugo"],"title":"Hugo建站-基础教程","uri":"/posts/theme-documentation-basics/#21-创建项目"},{"categories":["技术"],"collections":null,"content":"2.2 安装主题 克隆DoIt主题仓库到themes目录： git clone https://github.com/HEIGE-PCloud/DoIt 或者，初始化项目为git仓库，并且把主题仓库作为子模块导入： git init git submodule add https://github.com/HEIGE-PCloud/DoIt.git themes/DoIt ","date":"2021-09-22","heading":"2.2 安装主题","objectID":"/posts/theme-documentation-basics/:2:2","tags":["Hugo"],"title":"Hugo建站-基础教程","uri":"/posts/theme-documentation-basics/#22-安装主题"},{"categories":["技术"],"collections":null,"content":"2.3 基础配置 ","date":"2021-09-22","heading":"2.3 基础配置","objectID":"/posts/theme-documentation-basics/:2:3","tags":["Hugo"],"title":"Hugo建站-基础教程","uri":"/posts/theme-documentation-basics/#23-基础配置"},{"categories":["技术"],"collections":null,"content":"2.4 创建文章 通过以下命令创建一篇新文章： hugo new posts/title.md 在配置文件中添加newContentEditor = 'vim'，创建新文章后能自动在vim中打开该文档进行编辑 ","date":"2021-09-22","heading":"2.4 创建文章","objectID":"/posts/theme-documentation-basics/:2:4","tags":["Hugo"],"title":"Hugo建站-基础教程","uri":"/posts/theme-documentation-basics/#24-创建文章"},{"categories":["技术"],"collections":null,"content":"2.5 本地启动网站 使用以下命令启动网站： hugo serve[r] 在浏览器中打开地址http://localhost:1313/可以实时预览。 基本配置下的预览 技巧 当你运行 hugo serve 时, 当文件内容更改时, 页面会随着更改自动刷新. 注意 由于本主题使用了 Hugo 中的 .Scratch 来实现一些特性, 非常建议你为 hugo server 命令添加 --disableFastRender 参数来实时预览你正在编辑的文章页面. hugo serve --disableFastRender ","date":"2021-09-22","heading":"2.5 本地启动网站","objectID":"/posts/theme-documentation-basics/:2:5","tags":["Hugo"],"title":"Hugo建站-基础教程","uri":"/posts/theme-documentation-basics/#25-本地启动网站"},{"categories":["技术"],"collections":null,"content":"2.6 构建网站 一切就绪时，运行以下命令： hugo 会生成一个public目录，其中包含网站所需的所有静态文件，可将其部署在任何Web服务器上。 ","date":"2021-09-22","heading":"2.6 构建网站","objectID":"/posts/theme-documentation-basics/:2:6","tags":["Hugo"],"title":"Hugo建站-基础教程","uri":"/posts/theme-documentation-basics/#26-构建网站"},{"categories":["技术"],"collections":null,"content":"3 配置目录 除了使用单个配置文件外，还可以使用configDir目录维护组织和环境特定的变量。 ├── config │ ├── _default │ │ ├── config.toml │ │ ├── languages.toml │ │ ├── menu.en.toml │ │ ├── menu.zh-cn.toml │ │ ├── params.toml │ │ ├── params.en.toml │ │ └── params.zh-cn.toml │ ├── production │ │ ├── config.toml │ │ └── params.toml │ └── staging │ ├── config.toml │ └── params.toml 每个文件代表一个配置根对象，如Params，Menu，Languages等 每个目录包含一组文件，这些文件包含特定的环境设置。 可以对文件进行本地化以使其成为特定语言的配置文件。 在运行hugo -environment staging命令时，Hugo将使用config/_default和staging合并后的配置。 ","date":"2021-09-22","heading":"3 配置目录","objectID":"/posts/theme-documentation-basics/:3:0","tags":["Hugo"],"title":"Hugo建站-基础教程","uri":"/posts/theme-documentation-basics/#3-配置目录"},{"categories":["技术"],"collections":null,"content":"4 内容组织 以下是方便管理和生成文章的目录结构建议: 保持博客文章存放在 content/posts 目录, 例如: content/posts/我的第一篇文章.md 保持简单的静态页面存放在 content 目录, 例如: content/about.md 本地资源组织 本地资源引用 有三种方法来引用图片和音乐等本地资源: 使用页面包中的页面资源. 你可以使用适用于 Resources.GetMatch 的值或者直接使用相对于当前页面目录的文件路径来引用页面资源. 将本地资源放在 assets 目录中, 默认路径是 /assets. 引用资源的文件路径是相对于 assets 目录的. 将本地资源放在 static 目录中, 默认路径是 /static. 引用资源的文件路径是相对于 static 目录的. 引用的优先级符合以上的顺序. 在这个主题中的很多地方可以使用上面的本地资源引用, 例如 链接, 图片, image shortcode, music shortcode 和前置参数中的部分参数. 页面资源或者 assets 目录中的图片处理会在未来的版本中得到支持. 非常酷的功能! ","date":"2021-09-22","heading":"4 内容组织","objectID":"/posts/theme-documentation-basics/:4:0","tags":["Hugo"],"title":"Hugo建站-基础教程","uri":"/posts/theme-documentation-basics/#contents-organization"},{"categories":["技术"],"collections":null,"content":"5 数学公式 Hugo目前使用goldmark作为默认的Markdown引擎。配合mathjax可以输出漂亮的数学公式。 由于Markdown和LaTeX对下划线_及转义符号\\的处理差异，在调用mathjax渲染 公式时会出错。有几种方法可以解决。 手动添加转义符号\\，使得goldmark解析过后，符合TeX语法。 添加shortcode，避免公式部分被goldmark解析，直接调用mathjax处理。 使用goldmark-matjax插件，避免手动修改文件内容，缺点是需要自行编译。 这里采用最后一种方法，克隆Hugo仓库 到本地并添加插件 # Go 1.13及以上（推荐） # 考虑网速，国内走代理 go env -w GO111MODULE=on go env -w GOPROXY=https://goproxy.cn,direct # Sass/SCSS 支持 go env -w CGO_ENABLED=1 # 或者在终端执行 export GO111MODULE=on export GOPROXY=https://goproxy.cn export CGO_ENABLED=1 # 添加插件 go get github.com/litao91/goldmark-mathjax 修改文件markup/goldmark/convert.go import ( \"bytes\" \"fmt\" // ... // Add mathjax-support mathjax \"github.com/litao91/goldmark-mathjax\" ) // ... // register goldmark-mathjax extensions = append(extensions, mathjax.MathJax) md := goldmark.New( goldmark.WithExtensions( extensions..., ), // ... ) 最后编译 # 编译|安装 go build|install --tags extended 接下来有两种方案调用JS脚本进行公式渲染。 方案一（当前采用） 在配置文件params.toml中添加mathjax的全局控制开关 [page] # ... [page.mathjax] enable =false # 默认关闭 若要开启数学公式支持，则在前置参数中添加mathjax: true。 最后修改模板文件assets.html导入JS脚本 {{- $mathjax := or (eq $params.mathjax true) .Site.Params.page.mathjax.enable | dict \"enable\" -}} {{- if $mathjax.enable -}} {{- $source := $cdn.mathjaxJS | default \"/js/load-mathjax.js\" -}} {{- dict \"Source\" $source \"Fingerprint\" $fingerprint \"Defer\" true | dict \"Scratch\" .Scratch \"Data\" | partial \"scratch/pjaxScript.html\" -}} {{- end -}} 采用的load-mathjax.js内容为 window.MathJax = { loader: {load: ['[tex]/physics']}, tex: { packages: {'[+]': ['physics']}, inlineMath: [['$', '$'], ['\\\\(','\\\\)']], displayMath: [['$$','$$'], ['\\\\[','\\\\]']], processEscapes: true, processEnvironments: true, processRefs: true, tags: 'ams', macros: { bm: [\"\\\\boldsymbol{#1}\", 1], bigo: \"\\\\mathcal{O}\", pprime: \"\\\\prime\\\\prime\", lrp: [\"\\\\left(#1\\\\right)\", 1], lrb: [\"\\\\left[#1\\\\right]\", 1], Abs: [\"\\\\lvert #1 \\\\rvert\", 1] }, physics: { italicdiff: false, arrowdel: false } } }; (function () { var script = document.createElement('script'); script.src = 'https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-chtml-full.js'; script.async = true; document.head.appendChild(script); })(); 方案二（推荐） 优点：在于无需修改模板文件assets.html，load-mathjax.js可根据内容是否包含 数学公式自动加载JS脚本进行渲染，也无需在前置参数中添加mathjax: true进行声明。 缺点：确实自动加载了JS脚本，但仍需额外刷新一次网页才会渲染公式 （若解决此Bug，则会切换到此方案）。 在配置文件中找到page.library.js，添加如下内容 [page] # ... [page.library] # ... [page.library.js] mathjaxJS = \"/js/load-mathjax.js\" 这里用的load-mathjax.js与方案一差别在于套了一层if进行数学环境的判断。 (function () { if (document.body.querySelector('math') || document.body.textContent.match(/(?:\\$|\\\\\\(|\\\\\\[|\\\\begin\\{.*?})/)) { if (!window.MathJax) { window.MathJax = { tex: { inlineMath: {'[+]': [['$', '$']]}, displayMath: [['$$','$$'], ['\\\\[','\\\\]']], processEscapes: true, processEnvironments: true, processRefs: true, tags: 'ams', macros: { bm: [\"\\\\boldsymbol{#1}\", 1], bigo: \"\\\\mathcal{O}\", pprime: \"\\\\prime\\\\prime\", lrp: [\"\\\\left(#1\\\\right)\", 1], lrb: [\"\\\\left[#1\\\\right]\", 1], Abs: [\"\\\\lvert #1 \\\\rvert\", 1] } } }; } var script = document.createElement('script'); script.src = 'https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-chtml-full.js'; script.async = true; document.head.appendChild(script); } })(); ","date":"2021-09-22","heading":"5 数学公式","objectID":"/posts/theme-documentation-basics/:5:0","tags":["Hugo"],"title":"Hugo建站-基础教程","uri":"/posts/theme-documentation-basics/#5-数学公式"},{"categories":["技术"],"collections":null,"content":"5.1 行内公式 默认的行内公式分割符为$/$和\\\\(/\\\\) $ c = \\pm\\sqrt{a^2 + b^2} $ 和 \\\\( f(x)=\\int_{-\\infty}^{\\infty} \\hat{f}(\\xi) e^{2 \\pi i \\xi x} d \\xi \\\\) 效果如下： $ c = \\pm\\sqrt{a^2 \u0026#43; b^2} $ 和 \\( f(x)=\\int_{-\\infty}{\\infty} \\hat{f}(\\xi) e{2 \\pi i \\xi x} d \\xi \\) ","date":"2021-09-22","heading":"5.1 行内公式","objectID":"/posts/theme-documentation-basics/:5:1","tags":["Hugo"],"title":"Hugo建站-基础教程","uri":"/posts/theme-documentation-basics/#51-行内公式"},{"categories":["技术"],"collections":null,"content":"5.2 公式块 默认的公式块分割符为$$/$$和\\\\[/\\\\] $$ c = \\pm\\sqrt{a^2 + b^2} $$ \\\\[ f(x)=\\int_{-\\infty}^{\\infty} \\hat{f}(\\xi) e^{2 \\pi i \\xi x} d \\xi \\\\] 效果如下： $$ c = \\pm\\sqrt{a^2 \u0026#43; b^2} $$\\[ f(x)=\\int_{-\\infty}{\\infty} \\hat{f}(\\xi) e{2 \\pi i \\xi x} d \\xi \\] ","date":"2021-09-22","heading":"5.2 公式块","objectID":"/posts/theme-documentation-basics/:5:2","tags":["Hugo"],"title":"Hugo建站-基础教程","uri":"/posts/theme-documentation-basics/#52-公式块"},{"categories":["技术"],"collections":null,"content":"6 添加文章打赏 DoIt主题自带打赏功能，为使用微信和支付宝二维码打赏，需对主题进行一些改动。修改主要涉及三个文件：sponsor.html，_custom.scss和params.zh-cn.toml。 考虑到英文页面的兼容性，需额外修改国际化文件i18n/zh-CN.toml。 ","date":"2021-09-22","heading":"6 添加文章打赏","objectID":"/posts/theme-documentation-basics/:6:0","tags":["Hugo"],"title":"Hugo建站-基础教程","uri":"/posts/theme-documentation-basics/#6-添加文章打赏"},{"categories":["技术"],"collections":null,"content":"6.1 配置文件添加相关变量 在/config/_default/params.zh-cn.toml中找到sponsor，并作如下改动 [sponsor] enable = true bio = \"\" link = \"\" [sponsor.wechat] enable = true link = \"/images/wechat-sponsor.webp\" [sponsor.alipay] enable = true link = \"/images/alipay-sponsor.webp\" 英文页面修改对应的params.en.toml。 ","date":"2021-09-22","heading":"6.1 配置文件添加相关变量","objectID":"/posts/theme-documentation-basics/:6:1","tags":["Hugo"],"title":"Hugo建站-基础教程","uri":"/posts/theme-documentation-basics/#61-配置文件添加相关变量"},{"categories":["技术"],"collections":null,"content":"6.2 修改模板文件 将/theme/DoIt/layouts/partials/single/sponsor.html拷贝到站点对应目录下，并作相应修改 {{- $wechatenable := .Params.sponsor.wechat.enable | default .Site.Params.sponsor.wechat.enable -}} {{- $wechatlink := .Params.sponsor.wechat.link | default .Site.Params.sponsor.wechat.link -}} {{- $alipayenable := .Params.sponsor.alipay.enable | default .Site.Params.sponsor.alipay.enable -}} {{- $alipaylink := .Params.sponsor.alipay.link | default .Site.Params.sponsor.alipay.link -}} {{- if $enable -}} {{- else -}} {{- T \"sponsor\" -}} {{- if $wechatenable -}} {{- dict \"Src\" $wechatlink | partial \"plugin/image.html\" -}} {{- T \"wechatreward\" -}} {{- end -}} {{- if $alipayenable -}} {{- dict \"Src\" $alipaylink | partial \"plugin/image.html\" -}} {{- T \"alipayreward\" -}} {{- end -}} {{- end -}} {{- end -}} ","date":"2021-09-22","heading":"6.2 修改模板文件","objectID":"/posts/theme-documentation-basics/:6:2","tags":["Hugo"],"title":"Hugo建站-基础教程","uri":"/posts/theme-documentation-basics/#62-修改模板文件"},{"categories":["技术"],"collections":null,"content":"6.3 调整CSS样式 CSS样式参考自雨临Lewis， 采用悬浮样式，需修改/assets/css/_custom.scss。 // 打赏样式 article .sponsor .post-reward { margin: 30px auto; display: block; .sponsor-button { margin: 0; border-radius: 5px; .qr-code { display: none; position: absolute; bottom: 125%; left: -100%; background-color: rgba(255, 255, 255, 1); backdrop-filter: blur(10px); width: 300%; .qr-code-image { display: inline-block; position: relative; width: 100%; margin: 5% 5% 3%; img { display: inline-block; width: 100%; } span { display: inline-block; width: 100%; margin: 0; } } } \u0026:hover .qr-code { display: flex; } } } ","date":"2021-09-22","heading":"6.3 调整CSS样式","objectID":"/posts/theme-documentation-basics/:6:3","tags":["Hugo"],"title":"Hugo建站-基础教程","uri":"/posts/theme-documentation-basics/#63-调整css样式"},{"categories":["技术"],"collections":null,"content":"6.4 国际化文件添加翻译内容 将/theme/DoIt/i18n/zh-CN.toml拷贝至/i18n/zh-CN.toml，打开并添加如下内容 [sponsor] other = \"赞赏\" # added for WeChat and AliPay [wechatreward] other = \"微信打赏\" [alipayreward] other = \"支付宝打赏\" ","date":"2021-09-22","heading":"6.4 国际化文件添加翻译内容","objectID":"/posts/theme-documentation-basics/:6:4","tags":["Hugo"],"title":"Hugo建站-基础教程","uri":"/posts/theme-documentation-basics/#64-国际化文件添加翻译内容"},{"categories":["技术"],"collections":null,"content":"7 添加鼠标点击特效 DoIt主题支持自定义JavaScript脚本，在配置文件中设置脚本路径 [page] # ... # 第三方库配置 [page.library] [page.library.css] # ... [page.library.js] someJavaScript = \"/js/custom.js\" # ... 自定义脚本custom.js存放在站点目录/assets/js下，在其中添加如下内容 /* 返回随机颜色 */ function randomColor() { return \"rgb(\"+~~(255*Math.random())+\",\"+~~(255*Math.random())+\",\"+~~(255*Math.random())+\")\"; } /* 鼠标点击文字特效 */ var a_idx = 0; var a_click = 1; var a = new Array(\"富强\", \"民主\", \"文明\", \"和谐\", \"自由\", \"平等\", \"公正\" ,\"法治\", \"爱国\", \"敬业\", \"诚信\", \"友善\", \"老哥稳\", \"带我飞\", \"厉害了word哥\", \"扎心了老铁\", \"蓝瘦香菇\", \"还有这种操作?\", \"就是有这种操作\", \"皮皮虾我们走\", \"笑到猪叫\", \"石乐志\", \"不存在的\", \"黑车!\", \"我要下车!\", \"他还只是个孩子\", \"请不要放过他\", \"惊不惊喜?\", \"意不意外?\", \"我有一个大胆的想法\", \"你的良心不会痛吗\", \"你心里就没点b数吗\", \"没有,我很膨胀\", \"秀\", \"天秀\", \"陈独秀\", \"蒂花之秀\", \"造化钟神秀\", \"我去买几个橘子\", \"你就站在此地\", \"不要走动\", \"我可能读了假书\", \"贫穷限制了我的想象力\", \"打call\", \"当然是选择原谅她啊\", \"你有freestyle吗\", \"北大还行撒贝宁\", \"不知妻美刘强东\", \"悔创阿里杰克马\", \"一无所有王健林\", \"普通家庭马化腾\", \"别点了\", \"求求你别点了\", \"你还点\", \"你再点!\", \"有本事继续点!\", \"你厉害\", \"我投翔\", \"w(·Д·)w\", \"(#`O′)\", \"（/TДT)/\", \"┭┮﹏┭┮\", \"_(:3」∠)_\"); jQuery(document).ready(function($) { $(\"body\").click(function(e) { /* 点击频率，点击几次就换文字 */ var frequency = 1; if (a_click % frequency === 0) { var $i = $(\"\").text(a[a_idx]); a_idx = (a_idx + 1) % a.length; var x = e.pageX, y = e.pageY; $i.css({ \"z-index\": 9999, \"top\": y - 20, \"left\": x, \"position\": \"absolute\", \"font-weight\": \"bold\", \"color\": randomColor(), \"-webkit-user-select\": \"none\", \"-moz-user-select\": \"none\", \"-ms-user-select\": \"none\", \"user-select\": \"none\" }); $(\"body\").append($i); $i.animate({ \"top\": y - 180, \"opacity\": 0 }, 1500, function() { $i.remove(); }); } a_click ++; }); }); ","date":"2021-09-22","heading":"7 添加鼠标点击特效","objectID":"/posts/theme-documentation-basics/:7:0","tags":["Hugo"],"title":"Hugo建站-基础教程","uri":"/posts/theme-documentation-basics/#7-添加鼠标点击特效"},{"categories":["技术"],"collections":null,"content":"8 切换页面时网页标题个性化 为实现离开页面时，标题显示喔唷，崩溃啦！，回来时恢复原来标题的个性化效果，在custom.js中添加如下内容 /* 离开当前页面时修改网页标题，回到当前页面时恢复原来标题 */ window.onload = function() { var OriginTitile = document.title; var titleTime; document.addEventListener('visibilitychange', function() { if(document.hidden) { $('[rel=\"icon\"]').attr('href', \"/failure.ico\"); $('[rel=\"shortcut icon\"]').attr('href', \"/failure.ico\"); document.title = '喔唷，崩溃啦！'; clearTimeout(titleTime); } else { $('[rel=\"icon\"]').attr('href', \"/favicon-32x32.png\"); $('[rel=\"shortcut icon\"]').attr('href', \"/favicon-32x32.png\"); document.title = '咦，页面又好了！'; titleTime = setTimeout(function() { document.title = OriginTitile; }, 2000); } }); }","date":"2021-09-22","heading":"8 切换页面时网页标题个性化","objectID":"/posts/theme-documentation-basics/:8:0","tags":["Hugo"],"title":"Hugo建站-基础教程","uri":"/posts/theme-documentation-basics/#8-切换页面时网页标题个性化"},{"categories":null,"collections":null,"content":"这里是「我」的页面。 ","date":"2021-09-22","heading":"","objectID":"/about/:0:0","tags":null,"title":"关于「我」","uri":"/about/#"},{"categories":["工作"],"collections":null,"content":"如何在Ubuntu中配置Citrix Workspace App进行远程办公","date":"2020-10-24","objectID":"/posts/citrix-workspace-first-use/","tags":["远程办公"],"title":"在Ubuntu中使用Citrix Workspace App办公","uri":"/posts/citrix-workspace-first-use/"},{"categories":["工作"],"collections":null,"content":"远程办公 在家里办公需要下载Citrix Reciver远程连接公司服务器。Google之后发现在Linux平台上， Citrix Workspace APP已经取代前者并提供兼容性支持，因此将选择它进行安装。 ","date":"2020-10-24","heading":"远程办公","objectID":"/posts/citrix-workspace-first-use/:1:0","tags":["远程办公"],"title":"在Ubuntu中使用Citrix Workspace App办公","uri":"/posts/citrix-workspace-first-use/#远程办公"},{"categories":["工作"],"collections":null,"content":"安装使用 ","date":"2020-10-24","heading":"安装使用","objectID":"/posts/citrix-workspace-first-use/:2:0","tags":["远程办公"],"title":"在Ubuntu中使用Citrix Workspace App办公","uri":"/posts/citrix-workspace-first-use/#安装使用"},{"categories":["工作"],"collections":null,"content":"下载安装包 在最新版本所在 页面 根据Linux发行版找到相应的安装包并下载。 ","date":"2020-10-24","heading":"下载安装包","objectID":"/posts/citrix-workspace-first-use/:2:1","tags":["远程办公"],"title":"在Ubuntu中使用Citrix Workspace App办公","uri":"/posts/citrix-workspace-first-use/#下载安装包"},{"categories":["工作"],"collections":null,"content":"遇到的问题 安装完成后，启动程序并填入服务器地址进行连接，发现缺少SSL证书导致连接失败。 网上搜索之后找到解决办法，如下 # 将证书软链接至相应目录 sudo ln -s /usr/share/ca-certificates/mozilla/GlobalSign_Root_CA.crt /opt/Citrix/ICAClient/keystore/cacerts/ # 更新证书记录 sudo c_rehash /opt/Citrix/ICAClient/keystore/cacerts 由于本人所用笔记本屏幕分辨率较高2560x1600，打开远程桌面后，字体很小，眼睛累。公司用的服务器系统是window server 2008， 调整字体大小为150%之后，基本解决问题。不过这只是治标，治本得等Linux客户端程序提供功能改进了。 使用体验暂时OK，仍然不用切换到Windows办公，就这样！ ","date":"2020-10-24","heading":"遇到的问题","objectID":"/posts/citrix-workspace-first-use/:2:2","tags":["远程办公"],"title":"在Ubuntu中使用Citrix Workspace App办公","uri":"/posts/citrix-workspace-first-use/#遇到的问题"},{"categories":["学习"],"collections":null,"content":"规范扰动理论中的MS方程","date":"2020-06-18","objectID":"/posts/physics/cosmology/mukhanov-sasaki-equation/","tags":["Cosmology"],"title":"Mukhanov-Sasaki 方程","uri":"/posts/physics/cosmology/mukhanov-sasaki-equation/"},{"categories":["学习"],"collections":null,"content":"标量扰动 记录一下宇宙学中规范扰动理论中的Mukhanov-Sasaki(MS)方程。 $$ \\begin{equation} \\label{eq:ms-equation} u^{\\pprime}-c_s^2\\Delta u - \\frac{\\theta^{\\pprime}}{\\theta}u = 0. \\end{equation} $$以标量扰动以及完美流体为例，验证MS方程$\\eqref{eq:ms-equation}$。 为了叙述方便，以下将规范扰动简称为扰动，在物理量上添加横线代表相对应的规范不变量，若不至于产生误解，则省略横线。 完美流体的能动张量为 $$ \\begin{equation} \\label{eq:perfect-fluid-energy-mommentum-tensor} T^{\\alpha}_{\\ \\beta} = (\\varepsilon \u0026#43; p) u^{\\alpha}u_{\\beta}-p\\delta^{\\alpha}_{\\ \\beta}. \\end{equation} $$ 相应的一阶扰动为 $$ \\begin{equation} \\label{eq:guage-invariant-perturbation-for-perfect-fluid} \\overline{\\delta T}^{0}_0 = \\overline{\\delta\\varepsilon},\\quad \\overline{\\delta T}^{0}_i = \\frac{1}{a}(\\varepsilon_0\u0026#43;p_0)(\\overline{\\delta u}_{\\parallel i}\u0026#43;\\overline{\\delta u}_{\\bot i}), \\quad \\overline{\\delta T}^i_j = -\\overline{\\delta p}\\delta^i_j. \\end{equation} $$ 其中$\\overline{\\delta\\varepsilon},\\overline{\\delta u}_{\\parallel i},\\overline{\\delta p}$对应标量扰动，而$\\overline{\\delta u}_{\\bot i}$有旋无源，贡献为矢量扰动。 因为$\\delta T^i_{\\ j}=0$，当$i\\ne j$。因而$(ij)$分量对应的标量扰动方程约化为 $$ \\begin{equation} (\\Phi-\\Psi)_{,ij} = 0 \\qquad (i\\ne j). \\end{equation} $$ 因而有$\\Phi = \\Psi$。故而标量扰动满足的规范方程组的形式具体如下 $$ \\begin{align} \\Delta \\Phi - 3\\mathcal{H}(\\Phi^{\\prime}\u0026#43;\\mathcal{H}\\Phi) =4\\pi Ga^2\\overline{\\delta\\varepsilon}, \\label{eq:scalar-guage-invariant-perturbation-equations-00} \\\\ (a\\Phi)_{,}^{\\prime}=4\\pi Ga^2(\\varepsilon_0\u0026#43;p_0)\\overline{\\delta u}_{\\parallel i}, \\label{eq:scalar-guage-invariant-perturbation-equations-0i} \\\\ \\Phi^{\\pprime}\u0026#43;3\\mathcal{H}\\Phi^{\\prime}\u0026#43;(2\\mathcal{H^\\prime\u0026#43;H^2}\\Phi) = 4\\pi Ga^2\\overline{\\delta p}. \\label{eq:scalar-guage-invariant-perturbation-equations-ij} \\end{align} $$ 根据热力学可知压强为内能和熵的函数，$p=p(\\varepsilon, S)$。压强的涨落$\\overline{\\delta p}$可以表示为 $$ \\begin{equation} \\label{eq:sound-speed} \\overline{\\delta p}=c_s^2\\overline{\\delta\\varepsilon}\u0026#43;\\tau\\delta S. \\end{equation} $$ 这里只考虑绝热扰动（$\\delta S=0$），故$\\overline{\\delta p}=c_s^2\\overline{\\delta\\varepsilon}$。 联合方程$\\eqref{eq:scalar-guage-invariant-perturbation-equations-00}$和$\\eqref{eq:scalar-guage-invariant-perturbation-equations-ij}$ 得到引力势$\\Phi$满足的动力学方程 $$ \\begin{equation} \\label{eq:bardeen-equation} \\Phi^{\\pprime}\u0026#43;3(1\u0026#43;c_s^2)\\mathcal{H}\\Phi^{\\prime}-c_s^2\\Delta\\Phi\u0026#43;\\lrp{2\\mathcal{H^\\prime}\u0026#43;(1\u0026#43;3 c_s^2)\\mathcal{H^2}}\\Phi = 0 \\end{equation} $$ 方程$\\eqref{eq:bardeen-equation}$称为bardeen方程，这个方程并不总是存在解析解。 不过有可能得到长波极限和短波极限下的渐进解。这可以通过引入新变量，改写方程形式消灭其中的摩擦项做到。 $$ \\begin{equation} \\begin{aligned} u\u0026amp;\\equiv \\exp\\lrp{\\frac{3}{2}\\int (1\u0026#43;c_s^2)\\mathcal{H}d\\eta}\\Phi \\\\ \u0026amp;=\\exp\\lrp{-\\frac{1}{2}\\int \\lrp{1\u0026#43;\\frac{p_0^{\\prime}}{\\varepsilon_0^{\\prime}}} \\frac{\\varepsilon_0^{\\prime}}{\\varepsilon_0\u0026#43;p_0}d\\eta}\\Phi \\\\ \u0026amp;=\\frac{\\Phi}{(\\varepsilon_0\u0026#43;p_0)^{1/2}}. \\end{aligned} \\end{equation} $$ 其中用到了$c_s^2=p_0^{\\prime}/\\varepsilon_0^{\\prime}$，连续性方程 $\\varepsilon_0^{\\prime}=-3\\mathcal{H}(\\varepsilon_0\u0026#43;p_0)$ 。以及 $$ \\begin{equation} \\theta\\equiv \\frac{1}{a}\\lrp{1\u0026#43;\\frac{p_0}{\\varepsilon_0}}^{-1/2} =\\frac{1}{a}\\lrp{\\frac{2}{3}\\lrp{1-\\frac{\\mathcal{H^{\\prime}}}{\\mathcal{H^2}}}}^{-1/2}. \\end{equation} $$ 其中用到了背景方程 $$ \\begin{equation} \\label{eq:background-equation} \\mathcal{H^2}=\\frac{8\\pi G}{3}a^2\\varepsilon_0,\\quad \\mathcal{H^2-H^\\prime}=4\\pi Ga^2(\\varepsilon_0\u0026#43;p_0). \\end{equation} $$ 经过复杂的计算，bardeen方程$\\eqref{eq:bardeen-equation}$可以重写为不含摩擦项的MS方程 $$ \\begin{equation} u^{\\pprime}-c_s^2\\Delta u-\\frac{\\theta^{\\pprime}}{\\theta} u = 0. \\end{equation} $$接下来，只进行验证MS方程和bardeen方程$\\eqref{eq:bardeen-equation}$等价。 首先注意到 $$ \\begin{equation} \\begin{aligned} u^{\\pprime}-\\frac{\\theta^{\\pprime}}{\\theta}u \u0026amp;= \\lrp{\\frac{u}{\\theta}}^{\\pprime}\\theta\u0026#43;2\\lrp{\\frac{u}{\\theta}}^{\\prime}\\theta^{\\prime} \\\\ \u0026amp;=\\lrp{\\lrp{\\frac{u}{\\theta}}^{\\prime}\\theta}^{\\prime} \u0026#43; \\lrp{\\frac{u}{\\theta}}^{\\prime}\\theta^{\\prime} \\\\ \u0026amp;= v^{\\prime} \u0026#43; v\\frac{\\theta^{\\prime}}{\\theta}. \\quad \\text{令}(v=\\lrp{\\frac{u}{\\theta}}^{\\prime}\\theta). \\end{aligned} \\end{equation} $$ 故MS方程等价于 $$ \\begin{equation} c_s^2\\Delta u=v^{\\prime}\u0026#43;v\\fr","date":"2020-06-18","heading":"标量扰动","objectID":"/posts/physics/cosmology/mukhanov-sasaki-equation/:1:0","tags":["Cosmology"],"title":"Mukhanov-Sasaki 方程","uri":"/posts/physics/cosmology/mukhanov-sasaki-equation/#标量扰动"},{"categories":["学习"],"collections":null,"content":"曲率扰动 MS方程能够被写成更紧凑的形式 $$ \\begin{equation} \\lrb{\\lrp{\\frac{u}{\\theta}}^{\\prime}\\theta^2}^{\\prime} = c_s^2\\theta\\Delta u \\end{equation} $$ 在长波极限下，散度项$\\Delta u$可以被忽略，故而我们找到了一个在超视界区域为常数的规范不变量 $$ \\begin{equation} \\label{eq:curvature-perturbation} \\begin{aligned} \\zeta \u0026amp;\\equiv \\frac{2}{3}\\lrp{\\frac{u}{\\theta}}^{\\prime}\\theta^2 \\\\ \u0026amp;= \\frac{v}{z} \\end{aligned} \\end{equation} $$ 其中 $$ \\begin{equation} v=\\lrp{\\frac{u}{\\theta}}^{\\prime}\\theta,\\quad z = \\frac{1}{\\theta}. \\end{equation} $$ 该变量即是文献中常见的标量曲率扰动$\\mathcal{R}$，通常在暴胀过程中会计算$\\mathcal{R}$的功率谱$P_{\\mathcal{R}}(k)$。 前面验证MS方程的过程中从数学的角度看，构造出的四个规范不变量$u,v,\\theta,z$ 满足了如下两个方程 $$ \\begin{equation} \\label{eq:middle-equations} c_s^2\\Delta u=z\\lrp{\\frac{v}{z}}^{\\prime},\\quad v=\\theta\\lrp{\\frac{u}{\\theta}}^{\\prime}. \\end{equation} $$ 满足上述方程的函数$u$，在消除辅助函数$v$后自然得到MS方程。观察$\\zeta$的定义 式$\\eqref{eq:curvature-perturbation}$后，更希望找到$v$满足的方程。从方程组$\\eqref{eq:middle-equations}$出发可以得到 $$ \\begin{equation} v^{\\pprime}-c_s^2\\Delta v-\\frac{z^{\\pprime}}{z}v= 0 \\end{equation} $$ 巧合地是$u$和$v$满足的方程形式相同。实际上$v$满足的方程才是文献中提到的MS方程，$u$满足的方程只是形式相同，本质还是bardeen方程。 在暴胀过程中，根据规范不变量的定义 $$ \\begin{equation} z=\\frac{a\\lrp{\\varepsilon\u0026#43;p}^{1/2}}{H}=\\frac{a\\dot{\\varphi}}{H} =\\frac{a\\varphi^{\\prime}}{\\mathcal{H}}. \\end{equation} $$ 其中$\\varphi$是暴胀场。到这一步时，曲率扰动的功率谱就就呼之欲出了 $$ \\begin{equation} \\label{eq:curvature-perturbation-power-spectrum} P_{\\zeta}(k)=P_{\\mathcal{R}}(k) = \\frac{k^{3}}{2\\pi^2} \\left\\lvert \\frac{v_k}{z}\\right\\rvert ^2 \\end{equation} $$ 暴胀过程中，变换到傅立叶空间中，$v$的$k$模$v_k$满足贝塞尔方程 $$ \\begin{equation} \\label{eq:bessel-equation} v_k^{\\pprime}\u0026#43;\\lrp{k^2-\\frac{\\nu^2-1/4}{\\eta^2}}v_k = 0, \\end{equation} $$ 式中$\\nu=3/2\u0026#43;2\\varepsilon_H-\\eta_H$。考虑到平面波边值条件 $$ \\begin{equation} \\label{eq:plane-wave-boundary-condition} v_k(\\eta)\\rightarrow \\frac{1}{2k}e^{-ik\\eta},\\quad k\\rightarrow \\infty \\end{equation} $$ 贝塞尔方程$\\eqref{eq:bessel-equation}$的解为 $$ \\begin{equation} \\label{eq:solution-for-ms-equation-in-inflation} v_k(\\eta) =\\frac{\\sqrt{\\pi}}{2}e^{i(\\nu\u0026#43;1/2)\\pi/2}\\sqrt{-\\eta}H_{\\nu}^{(1)}(-k\\eta). \\end{equation} $$ 式中$H_{\\nu}^{(1)}$为第一类汉克尔函数。对于超视界扰动 $$ \\begin{align} \\label{eq:for-super-horizon} H_{\\nu}^{(1)}(x\\ll 1) \\sim \\sqrt{\\frac{2}{\\pi}}e^{-i\\pi/2} 2^{\\nu-3/2}\\frac{\\Gamma(\\nu)}{\\Gamma(3/2)}x^{-\\nu}, \\\\ v_k(\\eta) = e^{i(\\nu-1/2)\\pi/2}2^{\\nu-3/2} \\frac{\\Gamma(\\nu)}{\\Gamma(3/2)}\\frac{1}{\\sqrt{2k}}(-k\\eta)^{1/2-\\nu}. \\end{align} $$ 故曲率扰动的功率谱为 $$ \\begin{equation} \\begin{aligned} P_{\\mathcal{R}}(k) \u0026amp;= \\frac{k^{3}}{2\\pi^2}\\left\\lvert \\frac{\\nu_k}{z}\\right\\rvert ^2 \\\\ \u0026amp;= 2^{2\\nu-3}\\lrp{\\frac{\\Gamma(\\nu)}{\\Gamma(3/2)}}^2 \\lrp{\\frac{H}{\\dot{\\varphi}_0}}^2 \\lrp{\\frac{H}{2\\pi}}^2 \\lrp{\\frac{k}{aH}}^{3-2\\nu} \\Bigg\\lvert_{k=aH}. \\end{aligned} \\end{equation} $$","date":"2020-06-18","heading":"曲率扰动","objectID":"/posts/physics/cosmology/mukhanov-sasaki-equation/:2:0","tags":["Cosmology"],"title":"Mukhanov-Sasaki 方程","uri":"/posts/physics/cosmology/mukhanov-sasaki-equation/#曲率扰动"},{"categories":["学习"],"collections":null,"content":"逆拉普拉斯算符的定义及应用","date":"2020-06-07","objectID":"/posts/physics/inverse-laplacian-operator/","tags":["拉普拉斯算符"],"title":"逆拉普拉斯算符","uri":"/posts/physics/inverse-laplacian-operator/"},{"categories":["学习"],"collections":null,"content":"泊松方程及其解 拉普拉斯算符$\\Delta$是$n$维欧几里得空间中的一个二阶微分算符，定义为标量函数$f$的梯度的散度。 $$ \\begin{equation} \\label{eq:poisson-equation} \\Delta \\varphi = \\nabla^2 \\varphi = \\nabla\\cdot\\nabla \\varphi = f \\end{equation} $$ 物理中通常称为泊松方程，一般利用格林函数法求解。拉普拉斯算符对应的格林函数满足方程 $$ \\begin{equation} \\label{eq:green-function-for-laplacian-operator} \\nabla^2 G(\\bm{r},\\bm{r^\\prime}) = \\delta(\\bm{r}-\\bm{r}^\\prime) \\end{equation} $$ 方程$\\eqref{eq:green-function-for-laplacian-operator}$的解为 $$ \\begin{equation} G(\\bm{r},\\bm{r^\\prime}) = - \\frac{1}{4\\pi} \\frac{1}{\\abs{\\bm{r}-\\bm{r^\\prime}}} \\end{equation} $$ 因此泊松方程的特解为 $$ \\begin{equation} \\label{eq:particular-solution-for-poisson-equation} \\begin{aligned} \\varphi_p(\\bm{r}) \u0026amp;= \\int G(\\bm{r},\\bm{r^\\prime})f(\\bm{r^\\prime})d^3\\bm{r^\\prime} \\\\ \u0026amp;=-\\frac{1}{4\\pi}\\int \\frac{f(\\bm{r^\\prime})}{\\abs{\\bm{r}-\\bm{r^\\prime}}}d^3\\bm{r^\\prime} \\end{aligned} \\end{equation} $$若源项$f(\\bm{r})$在全空间积分为有限值，则 $$ \\begin{equation} \\begin{aligned} \\lim_{r\\to\\infty}\\varphi_p(\\bm{r})\u0026amp;=-\\frac{1}{4\\pi}\\lim_{r\\to\\infty} \\int\\frac{f(\\bm{r})}{\\abs{\\bm{r-r^\\prime}}} d^3\\bm{r^\\prime} \\\\ \u0026amp;=-\\frac{1}{4\\pi} \\left(\\lim_{r\\to\\infty}\\frac{1}{r}\\int f(\\bm{r^\\prime})d^3\\bm{r^\\prime} \u0026#43; \\bigo\\lrp{\\frac{1}{r^2}} \\right) \\\\ \u0026amp;=-\\frac{1}{4\\pi} \\lrp{\\lim_{r\\to\\infty}\\frac{q}{r}\u0026#43;\\bigo\\lrp{\\frac{1}{r^2}}} \\\\ \u0026amp;=0 \\end{aligned} \\end{equation} $$ 因此精确的表述为，当泊松方程$\\eqref{eq:poisson-equation}$的边界条件为：$\\varphi(\\bm{r})$在无穷远处为零时，解为$\\varphi_p(\\bm{r})$。当指定非零边界条件时，完整的解为 $$ \\begin{equation} \\varphi(\\bm{r}) =\\varphi_p(\\bm{r})\u0026#43;\\varphi_c(\\bm{r}) \\end{equation} $$ 其中$\\varphi_c(\\bm{r})$为任意拉普拉斯方程（无源泊松方程）的解 $$ \\begin{equation} \\label{eq:solution-for-boundary-laplace-equation} \\nabla^2\\varphi_c(\\bm{r}) = 0 \\end{equation} $$","date":"2020-06-07","heading":"泊松方程及其解","objectID":"/posts/physics/inverse-laplacian-operator/:1:0","tags":["拉普拉斯算符"],"title":"逆拉普拉斯算符","uri":"/posts/physics/inverse-laplacian-operator/#泊松方程及其解"},{"categories":["学习"],"collections":null,"content":"逆拉普拉斯算符 当源项$f(\\bm{r})$在全空间积分收敛，即选取无穷远处为零的边界条件时，泊松方程的解为 $$ \\begin{equation} \\varphi(\\bm{r})=\\varphi_p(\\bm{r}) \\end{equation} $$ 在上述前提条件下，定义逆拉普拉斯算符为$\\Delta^{-1}$，将其作用于泊松方程$\\eqref{eq:poisson-equation}$得到 $$ \\begin{equation} \\Delta^{-1}\\Delta\\varphi(\\bm{r})\\equiv \\varphi(\\bm{r}) = \\Delta^{-1}f(\\bm{r}) \\end{equation} $$ 根据$\\eqref{eq:particular-solution-for-poisson-equation}$可得 $$ \\begin{equation} \\boxed{ \\Delta^{-1}f(\\bm{r}) =-\\frac{1}{4\\pi}\\int\\frac{f(\\bm{r^\\prime})}{\\abs{\\bm{r-r^\\prime}}}d^3\\bm{r^\\prime}} \\end{equation} $$ 容易验证 $$ \\begin{equation} \\Delta^{-1}\\Delta f(\\bm{r})=\\Delta\\Delta^{-1}f(\\bm{r})=f(\\bm{r}) \\end{equation} $$","date":"2020-06-07","heading":"逆拉普拉斯算符","objectID":"/posts/physics/inverse-laplacian-operator/:2:0","tags":["拉普拉斯算符"],"title":"逆拉普拉斯算符","uri":"/posts/physics/inverse-laplacian-operator/#逆拉普拉斯算符"},{"categories":["学习"],"collections":null,"content":"应用 利用逆拉普拉斯算符$\\Delta^{-1}$，可以将矢量场$\\bm{V}(\\bm{r})$唯一分解为不相交的两部分 $$ \\begin{equation} \\bm{V}=\\bm{V}_{\\parallel} \u0026#43; \\bm{V}_{\\bot},\\qquad \\text{其中}\\ \\nabla\\cdot \\bm{V}_{\\bot}=\\nabla\\times \\bm{V}_{\\parallel}=0 \\end{equation} $$直接构造 $$ \\begin{equation} \\label{eq:longitudinal} \\bm{V}_{\\parallel}=\\nabla\\Delta^{-1}\\nabla\\cdot \\bm{V} \\end{equation} $$观察到$\\psi\\equiv\\Delta^{-1}\\nabla\\cdot \\bm{V}$ 是一个标量场，因此 $\\bm{V}\\times \\bm{V}_{\\parallel}=\\bm{V}\\times\\nabla\\psi=0$ 。横向部分为 $$ \\begin{equation} \\bm{V}_{\\bot}=\\bm{V}-\\bm{V}_{\\parallel}=\\bm{V}-\\nabla\\Delta^{-1}\\nabla\\cdot \\bm{V} \\end{equation} $$可以验证 $$ \\begin{equation} \\nabla\\cdot \\bm{V}_{\\bot}=\\nabla\\cdot\\lrb{\\bm{V}-\\nabla\\Delta^{-1}\\nabla\\cdot \\bm{V}} =\\nabla\\cdot \\bm{V}-\\Delta\\Delta^{-1}\\lrp{\\nabla\\cdot \\bm{V}} =\\nabla\\cdot \\bm{V}-\\nabla\\cdot \\bm{V}=0 \\end{equation} $$与$\\bm{V}_{\\parallel}$一样，$\\bm{V}_{\\bot}$也存在类似的表达式 $$ \\begin{equation} \\label{eq:transverse} \\bm{V}_{\\bot}=-\\nabla\\times\\Delta^{-1}\\lrp{\\nabla\\times \\bm{V}} \\end{equation} $$为了证明$\\eqref{eq:transverse}$成立，首先引入两个等式，不过省略证明。 $$ \\begin{aligned} \u0026amp;\\nabla\\times\\Delta^{-1}\\lrp{\\bm{A}}=\\Delta^{-1}\\lrp{\\nabla\\times \\bm{A}}\\\\ \u0026amp;\\nabla\\Delta^{-1}\\lrp{\\psi}=\\Delta^{-1}\\lrp{\\nabla\\psi} \\end{aligned} $$ 于是 $$ \\begin{aligned} \\bm{V}_{\\bot}\u0026amp;=-\\nabla\\times\\Delta^{-1}\\lrp{\\nabla\\times\\bm{V}}\\\\ \u0026amp;=-\\Delta^{-1}\\lrb{\\nabla\\times\\lrp{\\nabla\\times\\bm{V}}} \\\\ \u0026amp;=\\Delta^{-1}\\lrb{\\Delta\\bm{V}-\\nabla\\lrp{\\nabla\\cdot\\bm{V}}}\\\\ \u0026amp;=\\bm{V}-\\Delta^{-1}\\nabla\\lrp{\\nabla\\cdot\\bm{V}}\\\\ \u0026amp;=\\bm{V}-\\nabla\\Delta^{-1}\\lrp{\\nabla\\cdot\\bm{V}} \\\\ \u0026amp;=\\bm{V}-\\bm{V}_{\\parallel} \\end{aligned} $$以上说明了分解的存在性，接下来说明该分解具有唯一性，并且只需说明纵向部分具有唯一性即可。 对矢量$\\bm{V}$取散度，得到关于标量场$\\psi$的泊松方程 $$ \\begin{equation} \\nabla\\cdot \\bm{V} = \\nabla\\cdot\\bm{V_{\\parallel}} = \\Delta \\psi \\end{equation} $$ 由于泊松方程具有唯一解，因而矢量分解也具有唯一性。 ","date":"2020-06-07","heading":"应用","objectID":"/posts/physics/inverse-laplacian-operator/:3:0","tags":["拉普拉斯算符"],"title":"逆拉普拉斯算符","uri":"/posts/physics/inverse-laplacian-operator/#应用"},{"categories":["技术"],"collections":null,"content":"配置Linux的触摸板，增强手势操作体验","date":"2020-05-13","objectID":"/posts/linux/i3wm-touchpad-tips/","tags":["触摸板","手势操作"],"title":"Linux下配置触摸板手势操作","uri":"/posts/linux/i3wm-touchpad-tips/"},{"categories":["技术"],"collections":null,"content":"MacOS的触摸板手势体验没得说，在Linux下也可以借助一些软件达到相近的体验。不过最想要的三指取词还不知到如何实现😠。 首先配置触摸板的驱动程序为libinput，具体操作这里不谈。借助软件libinput-gestures可以拓展触摸板三/四指手势以及缩放操作。 i3桌面下，窗口有两种：平铺以及浮动。工作区的概念类比MacOS中的虚拟桌面，便签区的效果类似窗口最小化。 ","date":"2020-05-13","heading":"","objectID":"/posts/linux/i3wm-touchpad-tips/:0:0","tags":["触摸板","手势操作"],"title":"Linux下配置触摸板手势操作","uri":"/posts/linux/i3wm-touchpad-tips/#"},{"categories":["技术"],"collections":null,"content":"功能表 up down left right 三指 切换到上方窗口 切换到下方窗口 切换到左侧窗口 切换到右侧窗口 四指 将浮动窗口移到便签区 循环切换便签区内的窗口 切换到左侧工作区 切换到右侧工作区 如果当前获得焦点的窗口是浏览器，则三指的手势功能为 up down left right 三指 前进 后退 切换到左侧tab 切换到右侧tab 以及另外四种手势 leftup leftdown rightup rightdown 三指 打开最近关闭标签页 关闭当前标签页 左移当前便签页 右移当前标签页 ","date":"2020-05-13","heading":"功能表","objectID":"/posts/linux/i3wm-touchpad-tips/:1:0","tags":["触摸板","手势操作"],"title":"Linux下配置触摸板手势操作","uri":"/posts/linux/i3wm-touchpad-tips/#功能表"},{"categories":["技术"],"collections":null,"content":"libinput-gestures 配置文件 libinput-gestures的配置文件位于~/.config/libinput-gestures.conf。其中涉及到上述功能的配置为 三指手势 gestures swipe left 3 $HOME/Program/bin/move_window_or_tab gestures swipe right 3 $HOME/Program/bin/move_window_or_tab right gestures swipe up 3 $HOME/Program/bin/move_window_or_tab up gestures swipe down 3 $HOME/Program/bin/move_window_or_tab down 四指手势 gestures swipe left 4 i3-msg workspace prev gestures swipe right 4 i3-msg workspace next gestures swipe up 4 $HOME/Program/bin/show_or_hide_scratchpad up gestures swipe down 4 xdotool key super+shift+minus 浏览器窗口中三指手势 gestures swipe left_up 3 $HOME/Program/bin/move_window_or_tab left_up gestures swipe left_down 3 $HOME/Program/bin/move_window_or_tab left_down gestures swipe right_up 3 $HOME/Program/bin/move_window_or_tab right_up gestures swipe right_down 3 $HOME/Program/bin/move_window_or_tab right_down ","date":"2020-05-13","heading":"libinput-gestures 配置文件","objectID":"/posts/linux/i3wm-touchpad-tips/:2:0","tags":["触摸板","手势操作"],"title":"Linux下配置触摸板手势操作","uri":"/posts/linux/i3wm-touchpad-tips/#libinput-gestures-配置文件"},{"categories":["技术"],"collections":null,"content":"实现脚本 libinput-gestures能捕捉手势，但是将手势与操作绑定需要通过另一个能实现模拟按键操作的软件xdotool。 由于三指手势对应的功能较为复杂，需要区分窗口内是否为浏览器，且涉及的主要功能为切换窗口，因而统一用一个脚本move_window_or_tab实现。 #!/usr/bin/env bash 获取窗口名称 WM_NAME=\"$(xdotool getactivewindow getwindowname)\" 获取手势方向 DIRECTION=${1:-left} if [[ $WM_NAME =~ 'Firefox' ]]; then if [[ $DIRECTION == 'left' ]]; then xdotool key control+Page_Up elif [[ $DIRECTION == 'right' ]]; then xdotool key control+Page_Down ... fi else 普通i3窗口 Key number: H-43, J-44, K-45, L-46, ;-47 if [[ $DIRECTION == 'left' ]]; then xdotool key super+44 elif [[ $DIRECTION == 'right' ]]; then xdotool key super+47 ... fi fi 四指切换工作区的命令很简单，不需要借助脚本实现。但是涉及到便签区窗口的功能无法用一行命令搞定，因此写了一个脚本show_or_hide_scratchpad。 获取手势方向 DIRECTION=${1:-up} 核心逻辑部分 if [[ $DIRECTION == 'up' ]]; then if i3-msg focus floating; then i3-msg move scratchpad fi elif [[ $DIRECTION == 'down' ]]; then if i3-msg focus floating; then i3-msg move scratchpad i3-msg move scratchpad else i3-msg move scratchpad fi i3-msg [floating] resize set 1280 1200 i3-msg move position center fi","date":"2020-05-13","heading":"实现脚本","objectID":"/posts/linux/i3wm-touchpad-tips/:3:0","tags":["触摸板","手势操作"],"title":"Linux下配置触摸板手势操作","uri":"/posts/linux/i3wm-touchpad-tips/#实现脚本"},{"categories":["技术"],"collections":null,"content":"科学上网工具Trojan及V2Ray相关配置","date":"2020-05-08","objectID":"/posts/linux/vps-config/","tags":["VPS","Trojan","V2Ray"],"title":"VPS相关配置，Trojan及V2RAY脚本","uri":"/posts/linux/vps-config/"},{"categories":["技术"],"collections":null,"content":"目前在用的科学上网工具有两款：Trojan和V2Ray。V2Ray的使用方式有很多，这里不作详细记录。Trojan的优势在于配置相对简单、采用的科学上网的方法不易被发现、速度快。V2Ray的优势在于功能齐全，搭配其他工具可以实现从简单到复杂的各种科学上网方式，缺点是某些方式速度较慢。综合两者的优缺点，我一般将两者搭配使用，Trojan负责科学上网，V2Ray负责路由功能。 ","date":"2020-05-08","heading":"","objectID":"/posts/linux/vps-config/:0:0","tags":["VPS","Trojan","V2Ray"],"title":"VPS相关配置，Trojan及V2RAY脚本","uri":"/posts/linux/vps-config/#"},{"categories":["技术"],"collections":null,"content":"安装Trojan和V2Ray 是否在Debian/Ubuntu官方仓库中 Trojan: ✔ (ubuntu从19.04开始) V2Ray: ✘ V2Ray的官方安装脚本在国内下载速度很慢，经常失败。结合上面信息，应选择优先安装Trojan，然后在终端下开启科学上网模式，再安装V2Ray。安装流程全自动，一切都很Nice。 function trojan_environment_varibles() { TROJAN_PREFIX=/directory/to/trojan/real/config.json TROJAN_CERT=$TROJAN_PREFIX/cert.pem TROJAN_CERT_PATH_IN_CONFIG=$TROJAN_CERT TROJAN_CONFIG=$TROJAN_PREFIX/config.json # trojan模板配置文件 TROJAN_CONFIG_EXAMPLE=$TROJAN_PREFIX/config_example.json TROJAN_SERVER=$SERVER_NAME } function trojan_install_for_old_distributor() { # 当官方仓库中不含trojan时安装流程 # 证书 位于 /usr/local/etc/ssl/cert.pem # 配置文件 位于 /usr/local/etc/trojan/config.json # 新建trojan用户，由trojan用户开启服务 } function trojan_install() { # 设置trojan相关环境变量 trojan_environment_varibles # 若trojan配置文件不存在，则从模板配置文件复制一份 [ ! -f $TROJAN_CONFIG ] \u0026\u0026 cp $TROJAN_CONFIG_EXAMPLE $TROJAN_CONFIG if ! command -v trojan \u003e /dev/null 2\u003e\u00261; then TROJAN_CONFIG=/path/to/模板配置文件 TROJAN_CERT=/path/to/本地证书 if 满足条件：Ubuntu及版本在19.04及以上; then sudo apt install trojan sudo ln -sf $TROJAN_CONFIG /etc/trojan/config.json else trojan_install_for_old_distributor TROJAN_CERT_PATH_IN_CONFIG=/usr/local/etc/ssl/cert.pem fi fi # 设置配置文件中的证书路径 if $TROJAN_CONFIG中不含$TROJAN_CERT_PATH_IN_CONFIG; then sed -i \"s|\\\"cert.*|\\\"cert\\\": \\\"$TROJAN_CERT_PATH_IN_CONFIG\\\",|\" $TROJAN_CONFIG fi trojan_first_start } ","date":"2020-05-08","heading":"安装Trojan和V2Ray","objectID":"/posts/linux/vps-config/:1:0","tags":["VPS","Trojan","V2Ray"],"title":"VPS相关配置，Trojan及V2RAY脚本","uri":"/posts/linux/vps-config/#安装trojan和v2ray"},{"categories":["技术"],"collections":null,"content":"配置文件 这里只讨论Trojan在本地的配置以及相关脚本。Trojan的客户端配置中有三项在切换VPS时需要修改，分别是 配置选项 VPS remote_addr IP/域名 sni 域名 cert 本地证书路径 因为使用了CloudFlare家的泛域名证书，因此在多个VPS上可以使用同一套证书，意味着即使在配置文件中本地证书路径使用了软链接地址，也无需对其指向的证书文件做任何修改。因此在切换VPS时，真正需要修改的只有两个地方：remote_addr和sni。 remote_addr选择的规则为 分类 DNS Proxy remote_addr IP或者域名 IP 原因在于remote_addr必须解析到VPS自己的IP。 ","date":"2020-05-08","heading":"配置文件","objectID":"/posts/linux/vps-config/:2:0","tags":["VPS","Trojan","V2Ray"],"title":"VPS相关配置，Trojan及V2RAY脚本","uri":"/posts/linux/vps-config/#配置文件"},{"categories":["技术"],"collections":null,"content":"切换VPS并重启服务的脚本 根据上面的分析，采用模板配置文件的方式，选取特定的VPS后，用sed直接修改配置文件中上述两个选项的值，再用service命令重启服务。 ","date":"2020-05-08","heading":"切换VPS并重启服务的脚本","objectID":"/posts/linux/vps-config/:3:0","tags":["VPS","Trojan","V2Ray"],"title":"VPS相关配置，Trojan及V2RAY脚本","uri":"/posts/linux/vps-config/#切换vps并重启服务的脚本"},{"categories":["技术"],"collections":null,"content":"VPS信息 VPS的相关信息，都存放在文件server_info.ini中，如下所示： [cloudcone] ip = xxx.xxx.xxx.xxx domain = www.example.com dns_proxy = true [racknerd] ip = xxx.xxx.xxx.xxx domain = www.example2.com dns_proxy = false 借助awk定义两个函数用于读取ini文件中的服务器信息。 SERVER_INFO_FILE=/path/to/server_info.ini # 收集VPS入口信息 function read_section_ini() { INI_FILE=$1 awk -F '=' '{ if ($1 ~ /\\[/) { gsub(/[\\[\\]]/, \"\", $1) print $1 } }' $INI_FILE } # 获取特定VPS的信息 function read_ini() { INI_FILE=$1 SECTION=${2:-cloudcone} KEY=${3:-ip} awk -F '=' '{ if ($1 ~ /\\[/) if ($1 ~ /\\['$SECTION'/) { a = 1 } else { a = 0 } else if (a == 1 \u0026\u0026 $1 ~ /'$KEY'/) { gsub(/[[:blank:]]/, \"\", $2) print $2 } }' $INI_FILE } VPS_SERVERS=($(read_section_ini $SERVER_INFO_FILE)) ","date":"2020-05-08","heading":"VPS信息","objectID":"/posts/linux/vps-config/:3:1","tags":["VPS","Trojan","V2Ray"],"title":"VPS相关配置，Trojan及V2RAY脚本","uri":"/posts/linux/vps-config/#vps信息"},{"categories":["技术"],"collections":null,"content":"切换VPS脚本 接下来就涉及到Trojan和V2Ray相关的脚本了，同样定义两个函数 function choose_server_for_trojan() { TROJAN_SERVER=$1 TROJAN_CONFIG=/path/to/trojan的模板配置文件 # 获取选定VPS的具体信息 dns_proxy=$(read_ini $SERVER_INFO_FILE $TROJAN_SERVER dns_proxy) ip=$(read_ini $SERVER_INFO_FILE $TROJAN_SERVER ip) sni=$(read_ini $SERVER_INFO_FILE $TROJAN_SERVER sni) if $dns_proxy; then remote_addr=$ip else remote_addr=$sni fi # 修改模板配置文件 sed -i \"s|\\\"remote_addr.*|\\\"remote_addr\\\": \\\"$remote_addr\\\",|\" $TROJAN_CONFIG sed -i \"s|\\\"sni.*|\\\"sni\\\": \\\"$sni\\\",|\" $TROJAN_CONFIG # 重启Trojan服务 sudo service trojan restart sudo service trojan status } function choose_server_for_v2ray() { # V2Ray相关配置修改 } # 核心逻辑部分 function change_server() { # 可用的VPS都存放在数组变量 VPS_SERVERS 中，传入的参数为数组下标， # 用于选取特定 VPS VPS_SERVERS=($(read_section_ini $SERVER_INFO_FILE)) SERVER_ID=${1:-0} SERVER_NAME=$(VPS_SERVERS[$SERVER_ID]) if [ $SERVER_ID -ge 0 ] \u0026\u0026 [ $SERVER_ID -lt ${#VPS_SERVERS[*]} ]; then choose_server_for_trojan $SERVER_NAME choose_server_for_v2ray $SERVER_NAME else echo \"错误：VPS服务器不存在\" echo \"尝试：'$COMMAND -i' 查看可用服务器\" fi } 都到这个程度了，干脆写成一个简单的命令行程序。 # 除了上面提到的函数外，再定义三个函数，用于实现相应的命令参数 function show_help() { echo \"欢迎使用本脚本切换VPS服务器\" echo \"用法：vps_server 选项\" echo \"选项：\" echo \" -h [--help] 显示本帮助\" echo \" -s [--server=val] 通过val选择不同VPS服务器\" echo \" -i [--info] 显示可用服务器汇总信息\" echo \" -c [--current] 显示当前使用的VPS服务器\" } function show_servers_info() { # shellcheck disable=SC2128,2207 [ -z \"$VPS_SERVERS\" ] \u0026\u0026 VPS_SERVERS=($(read_section_ini $SERVER_INFO_FILE)) echo \"可用的服务器有：\" for i in \"${!VPS_SERVERS[@]}\"; do echo \" $i ): ${VPS_SERVERS[i]}\" done echo \"\" echo \"服务器端安装完成：\" echo \" 将证书下载至客户端所在主机并放置在正确路径\" echo \" 客户端配置文件中需指定证书（或其软链接）路径\" echo \" 若CloudFlare中开启了代理，则客户端配置文件中只能使用IP。\" echo \"\" echo \"客户端安装完成：\" echo \" 从服务器上下载证书并安装\" echo \" 配置文件中需指定证书绝对路径\" echo \" | | | remote_addr |\" echo \" | | | IP | 域名 |\" echo \" | CloudFlare | DNS | ✔ | ✔ |\" echo \" | | Proxy | ✔ | ✘ |\" echo \"\" } function get_current_server() { awk -F ':' '{ if ($1 ~ /\\\"sni/) { gsub(/\\\"/, \"\", $2) print $2 } }' $TROJAN_PREFIX/config.json } # while循环与case结构处理命令参数 while [[ $# -ge 1 ]]; do case \"$1\" in -h|--help ) show_help ;; -i|--info) show_servers_info ;; -c|--current) VPS=$(get_current_server) echo \"当前VPS服务器：\" echo \" ${VPS%%.*}\" ;; -s|--server) shift if [[ $# -ge 1 ]]; then ID=$1 else ID=0 fi change_server $ID ;; *) echo \"错误：未识别选项 '$1'\" echo \"尝试 '$COMMAND -h|--help' 查看帮助信息\" ;; esac shift done","date":"2020-05-08","heading":"切换VPS脚本","objectID":"/posts/linux/vps-config/:3:2","tags":["VPS","Trojan","V2Ray"],"title":"VPS相关配置，Trojan及V2RAY脚本","uri":"/posts/linux/vps-config/#切换vps脚本"},{"categories":["工作"],"collections":null,"content":"快手面试算法题解析","date":"2019-09-16","objectID":"/posts/interview/kuaishou/","tags":["算法题"],"title":"记快手笔试大题","uri":"/posts/interview/kuaishou/"},{"categories":["工作"],"collections":null,"content":"第一题 ","date":"2019-09-16","heading":"第一题","objectID":"/posts/interview/kuaishou/:1:0","tags":["算法题"],"title":"记快手笔试大题","uri":"/posts/interview/kuaishou/#第一题"},{"categories":["工作"],"collections":null,"content":"题目描述 输入一个英文句子，词之间有 1 个或若干个空格，句子以英文标点 \u0026ldquo;.\u0026rdquo; 结尾。 要求颠倒该句子中的词语顺序，并且词之间有且只有一个空格，结尾仍是 \u0026ldquo;.\u0026quot;，结尾的 \u0026ldquo;.\u0026rdquo; 与前一个单词之间无空格。 ","date":"2019-09-16","heading":"题目描述","objectID":"/posts/interview/kuaishou/:1:1","tags":["算法题"],"title":"记快手笔试大题","uri":"/posts/interview/kuaishou/#题目描述"},{"categories":["工作"],"collections":null,"content":"代码 #coding=utf-8 import sys line = sys.stdin.readline().strip().split() if line[-1] == '.': line = line[:-1] else: line[-1] = line[-1][:-1] print ' '.join(line[::-1]) + '.' ","date":"2019-09-16","heading":"代码","objectID":"/posts/interview/kuaishou/:1:2","tags":["算法题"],"title":"记快手笔试大题","uri":"/posts/interview/kuaishou/#代码"},{"categories":["工作"],"collections":null,"content":"第二题 ","date":"2019-09-16","heading":"第二题","objectID":"/posts/interview/kuaishou/:2:0","tags":["算法题"],"title":"记快手笔试大题","uri":"/posts/interview/kuaishou/#第二题"},{"categories":["工作"],"collections":null,"content":"题目描述 已知两个字符串strA和strB，求将strA转换成strB所需的最小编辑操作次数。许可的编辑操作包括将一个字符替换成另一个字符，插入一个字符，删除一个字符。 本题参考LeetCode 第72题——编辑距离 ","date":"2019-09-16","heading":"题目描述","objectID":"/posts/interview/kuaishou/:2:1","tags":["算法题"],"title":"记快手笔试大题","uri":"/posts/interview/kuaishou/#题目描述-1"},{"categories":["工作"],"collections":null,"content":"代码 #coding=utf-8 import sys strA = sys.stdin.readline().strip() strB = sys.stdin.readline().strip() n1, n2 = len(strA), len(strB) dp = [[0] * (n2 + 1) for _ in range(n1 + 1)] for j in range(1, n2 + 1): dp[0][j] = dp[0][j-1] + 1 for i in range(1, n1 + 1): dp[i][0] = dp[i-1][0] + 1 for i in range(1, n1 + 1): for j in range(1, n2 + 1): if strA[i-1] == strB[j-1]: dp[i][j] = dp[i-1][j-1] else: dp[i][j] = 1 + min(dp[i][j-1], dp[i-1][j], dp[i-1][j-1]) print dp[-1][-1] ","date":"2019-09-16","heading":"代码","objectID":"/posts/interview/kuaishou/:2:2","tags":["算法题"],"title":"记快手笔试大题","uri":"/posts/interview/kuaishou/#代码-1"},{"categories":["工作"],"collections":null,"content":"第三题 ","date":"2019-09-16","heading":"第三题","objectID":"/posts/interview/kuaishou/:3:0","tags":["算法题"],"title":"记快手笔试大题","uri":"/posts/interview/kuaishou/#第三题"},{"categories":["工作"],"collections":null,"content":"题目描述 我们知道每一个大于 1 的数都一定是质数或者可以用质数的乘积来表示，如 10=2*5。现在请设计一个程序，对于给定的一个 (1, N] 之间的正整数（N 取值不超过 10 万），你需要统计 (1, N] 之间所有正整数的质数分解后，所有质数个数的总和。举例，输入数据 6，那么满足 (1, 6]) 的整数为 2, 3, 4, 5, 6，各自进行质数分解后为：2=\u0026gt;2, 3=\u0026gt;3, 4=\u0026gt;2*2, 5=\u0026gt;5, 6=\u0026gt;2*3。对应的质数个数即 1, 1, 2, 1, 2。最后统计总数为 7。 本题是完全自己做出来的，复用了求质数的筛法。 关键点在 若 n = a\\times b，则 f(n) = f(a)\u0026#43;f(b)。 ","date":"2019-09-16","heading":"题目描述","objectID":"/posts/interview/kuaishou/:3:1","tags":["算法题"],"title":"记快手笔试大题","uri":"/posts/interview/kuaishou/#题目描述-2"},{"categories":["工作"],"collections":null,"content":"代码 #coding=utf-8 import sys def prime_sieve(n): \"\"\"返回所有小于 n 的质数\"\"\" sieve = [True] * (n // 2) for i in range(3, int(n**0.5) + 1, 2): if sieve[i // 2]: sieve[i *i // 2::i] = [False] * ((n - i * i - 1) // (2 * i) + 1) return [2] + [2 * i + 1 for i in range(1, n // 2) if sieve[i]] n = int(sys.stdin.readline().strip()) primes = prime_sieve(n + 1) data = {p: 1 for p in primes} def help(n): if n == 1: return 0 if n in data: return data[n] i, n0 = 0, n ans = 0 while not ans: c, p = 0, primes[i] while n % p == 0: c += 1 n = n // p if c: ans = c + help(n) i += 1 data[n0] = ans return ans ans = 0 for m in range(2, n + 1): ans += help(m) print ans ","date":"2019-09-16","heading":"代码","objectID":"/posts/interview/kuaishou/:3:2","tags":["算法题"],"title":"记快手笔试大题","uri":"/posts/interview/kuaishou/#代码-2"},{"categories":["工作"],"collections":null,"content":"第四题 ","date":"2019-09-16","heading":"第四题","objectID":"/posts/interview/kuaishou/:4:0","tags":["算法题"],"title":"记快手笔试大题","uri":"/posts/interview/kuaishou/#第四题"},{"categories":["工作"],"collections":null,"content":"题目描述 给定一个数独板的输入，确认当前的填法是否合法。 合法的输入需要满足一下三个条件： 每一行的9个格子中是1-9的9个数字，且没有重复 每一列的9个格子中是1-9的9个数字，且没有重复 9个3*3的小格子中是1-9的9个数字，且没有重复 空格用X表示 本题抄自己的代码LeetCode 第36题——有效的数独 ","date":"2019-09-16","heading":"题目描述","objectID":"/posts/interview/kuaishou/:4:1","tags":["算法题"],"title":"记快手笔试大题","uri":"/posts/interview/kuaishou/#题目描述-3"},{"categories":["工作"],"collections":null,"content":"代码 #coding=utf-8 import sys board = [] for _ in range(9): board.append(sys.stdin.readline().strip()) def help(board): board_t = zip(*board) for i in range(3): for j in range(3): if i == j: for k in range(3): col = [n for n in board[3*i+k] if n!='X'] if len(col) != len(set(col)): return False row = [n for n in board_t[3*i+k] if n!='X'] if len(row) != len(set(row)): return False block = set() for k in range(3): for s in board[3*i+k][3*j:3*j+3]: if s != 'X': if s in block: return False block.add(s) return True print 'true' if help(board) else 'false'","date":"2019-09-16","heading":"代码","objectID":"/posts/interview/kuaishou/:4:2","tags":["算法题"],"title":"记快手笔试大题","uri":"/posts/interview/kuaishou/#代码-3"},{"categories":["工作"],"collections":null,"content":"三七互娱面试算法题解析","date":"2019-09-10","objectID":"/posts/interview/37huyu/","tags":["算法题"],"title":"记三七互娱笔试大题","uri":"/posts/interview/37huyu/"},{"categories":["工作"],"collections":null,"content":"三七互娱共两道算法大题，总共就一个小时的笔试时间，总感觉时间不够用。最后一题确实因为时间不够没写完，好在思路有了用注释提了一下，不知道能不能酌情给分\u0026#x1f602;。 ","date":"2019-09-10","heading":"","objectID":"/posts/interview/37huyu/:0:0","tags":["算法题"],"title":"记三七互娱笔试大题","uri":"/posts/interview/37huyu/#"},{"categories":["工作"],"collections":null,"content":"第一题 描述：给一个数组，其中 $1 \\le A[i]\\le n$。有些元素出现两次，有些出现一次，返回所有出现两次的元素。 解答： #!/usr/bin/env python # coding=utf-8 def solution(array): count = {} for a in array: if a not in array: count[a] = 1 else: count[a] += 1 return [key for key in count if count[key] == 2] ","date":"2019-09-10","heading":"第一题","objectID":"/posts/interview/37huyu/:1:0","tags":["算法题"],"title":"记三七互娱笔试大题","uri":"/posts/interview/37huyu/#第一题"},{"categories":["工作"],"collections":null,"content":"第二题 描述：小明有想设计一个随机算法来听歌，并且希望每首歌被选中的概率正比于它的豆瓣评分。如歌 A 和 B 的评分分别为 $8.5$ 和 $9.3$，则这两首歌被选中的概率的比为 $85:93$。现给出 $1000$ 首歌的豆瓣评分，请设计一个随机算法并实现它。 解答： #!/usr/bin/env python # coding=utf-8 import random def solution(songs, scores): n = len(songs) # 假设评分只有一位小数 srange = [0] for i in range(n): srange.append(srange[-1] + int(scores[i] * 10)) # 生成 [0, srange[-1]] 范围的均匀随机数 rand rand = random.uniform(0, 1) rand = int(rand * srange[-1]) # 二分法查找 rand 落在 srange 的哪个区间中 i, j = 0, n m = (i + j) // 2 while i \u003c j: if srange[m] \u003c= rand \u003c srange[m+1]: return songs[m] if srange[m] \u003e rand: j = m else: i = m + 1 m = (i + j) // 2","date":"2019-09-10","heading":"第二题","objectID":"/posts/interview/37huyu/:2:0","tags":["算法题"],"title":"记三七互娱笔试大题","uri":"/posts/interview/37huyu/#第二题"},{"categories":["工作"],"collections":null,"content":"小米面试算法题解析","date":"2019-09-09","objectID":"/posts/interview/xiaomi/","tags":["算法题"],"title":"记小米一面算法题——人民币大写金额转换","uri":"/posts/interview/xiaomi/"},{"categories":["工作"],"collections":null,"content":"30分钟小米算法工程师一面，由于没有项目经历面试官给了一道算法题，现撸代码。毫无意外，快速码代码向来不是我的强项。 特此记录失败史 ","date":"2019-09-09","heading":"","objectID":"/posts/interview/xiaomi/:0:0","tags":["算法题"],"title":"记小米一面算法题——人民币大写金额转换","uri":"/posts/interview/xiaomi/#"},{"categories":["工作"],"collections":null,"content":"题目描述 根据人民币大写金额规范，将浮点数转换为人民币读法字符串需要注意以下几点： 数字中间有连续一个或以上个 0 时，只写一个零。如 ¥ 140,090.3，应写作人民币十四万零玖拾元叁角。 角位非 0 时，元后面可以不加零。如 ¥ 1690.32，可以写作人民币壹仟陆佰玖拾元叁角[零]贰分。 角位是 0，分位不是 0 时，大写金额元后面应写零。如 ¥ 325.04，应写为人民币叁佰贰拾元零肆分。 角和分都是 0 时，元后面应写上整或正。如 ¥ 1234.00，应写为人民币壹仟贰佰叁拾肆元（整/正）。 注意：最小单位为分。 ","date":"2019-09-09","heading":"题目描述","objectID":"/posts/interview/xiaomi/:1:0","tags":["算法题"],"title":"记小米一面算法题——人民币大写金额转换","uri":"/posts/interview/xiaomi/#题目描述"},{"categories":["工作"],"collections":null,"content":"题解 中文数字读法以 4 个数字为一个组，辅以单位如万、亿、兆、京等。这里取到最高为亿。由于每组单位不同，一度不知如何处理。因为金额大小没有限制，所以数字位数可以很大，如果根据单位用if...else...语句，是行不通的。 后来发现，还是可以使用递归来解决问题。 至于零的处理，可以设定字符串替换规则，使得处理后满足规范。 ","date":"2019-09-09","heading":"题解","objectID":"/posts/interview/xiaomi/:2:0","tags":["算法题"],"title":"记小米一面算法题——人民币大写金额转换","uri":"/posts/interview/xiaomi/#题解"},{"categories":["工作"],"collections":null,"content":"代码 ch_num = ['零', '壹', '贰', '叁', '肆', '伍', '陆', '柒', '捌', '玖'] ch_num = dict(zip('0123456789', ch_num)) ch_unit = ['', '万', '亿'] ch_unit = dict(zip(range(3), ch_unit)) def convert_int(intPart): \"\"\" 转换整数部分 intPart: str return: str \"\"\" if len(intPart) \u003c= 4: # 若长度小于 4， 则补足到 4 位。 ans = [ch_num[c] for c in '0'*(4-len(intPart))+intPart] if ans[-1] == ch_num['0']: ans = '{}仟{}佰{}拾'.format(*ans[:-1]) else: ans = '{}仟{}佰{}拾{}'.format(*ans) # 处理中间有 0 的情况 ans = ans.replace('零仟', ch_num['0']) ans = ans.replace('零佰', ch_num['0']) ans = ans.replace('零拾', ch_num['0']) # 处理中间连续多个 0，末尾有零的情况 ans = re.sub('零+', ch_num['0'], ans) ans = re.sub('零+$', ch_unit[0], ans) # 若开头有 0 且整数部分长度小于 4，则丢弃开头的’零‘（只有 1 个）。 return ans[4!=len(intPart):] else: # 金额数字长度可以写成 n = 4 * u + r u, r = len(intPart) // 4, len(intPart) % 4 # 若 r = 0，则 u = u - 1，至少先处理开头 4 位。之后再看 u， # 若 u \u003e 2，则先处理不包括最后 8 位的部分，否则就先只处理开头 4 位。 i = r * (r != 0) + 4 * (max(u - 2 - (r == 0), 0) + (r == 0)) # 于是分成两部分递归处理 intPart[:i] 和 intPart[i:] left, right = convert_int(intPart[:i]), convert_int(intPart[i:]) # 若左部分返回空字符串 ''，则不加单位，即 j = 0 and ch_unit[j] = ''。 # 又因为最大单位为“亿”，所以 0 \u003c= j \u003c= 2 j = min(u - (r == 0), 2) if left else 0 # 得到单位 unit = ch_unit[j] # 返回最终结果 return '{}{}{}'.format(left, unit, right) def convert_decimal(decimalPart): \"\"\" 处理小数部分，容易多了 decimalPart: str return: str \"\"\" ans = [ch_num[c] for c in decimalPart + '0' * (2 - len(decimalPart))] ans = '{}角{}分'.format(*ans) ans = ans.replace('零角', ch_num['0']) ans = ans.replace('零分', ch_unit[0]) ans = re.sub('零+$', ch_unit[0], ans) # 因为使用递归处理整数部分，所以整数部分单位“元”放在这里处理。 return '元{}'.format(ans or '整') def convert_RMB(money): \"\"\" 将浮点数转换为大写人民币规范读法 money: str return: str \"\"\" if '.' in money: intPart, decimalPart = money.split('.') else: intPart, decimalPart = money, ch_unit[0] return '人民币{}{}'.format(convert_int(intPart), convert_decimal(decimalPart))","date":"2019-09-09","heading":"代码","objectID":"/posts/interview/xiaomi/:3:0","tags":["算法题"],"title":"记小米一面算法题——人民币大写金额转换","uri":"/posts/interview/xiaomi/#代码"},{"categories":["技术"],"collections":null,"content":"ALE-语法检查与代码格式化插件","date":"2019-09-02","objectID":"/posts/vim/plugin/ale/","tags":["ale"],"title":"Vim 插件配置——ALE","uri":"/posts/vim/plugin/ale/"},{"categories":["技术"],"collections":null,"content":"异步检查语法 pycodestyle —— 前身为pep8，检查Python语法以及格式 安装 pip install pycodestyle 配置 配置文件在 ~/.config/pycodestyle。 ","date":"2019-09-02","heading":"异步检查语法","objectID":"/posts/vim/plugin/ale/:1:0","tags":["ale"],"title":"Vim 插件配置——ALE","uri":"/posts/vim/plugin/ale/#异步检查语法"},{"categories":["技术"],"collections":null,"content":"代码格式化 yapf —— Google出品的Python代码格式化工具 isort —— 格式化 import 语句 安装 pip install yapf pip install isort 配置 配置文件在 ~/.config/yapf/style 和 ~/.isort.cfg。 ","date":"2019-09-02","heading":"代码格式化","objectID":"/posts/vim/plugin/ale/:2:0","tags":["ale"],"title":"Vim 插件配置——ALE","uri":"/posts/vim/plugin/ale/#代码格式化"},{"categories":["学习"],"collections":null,"content":"判断模式字符串P是否是目标字符串S的字串","date":"2019-08-18","objectID":"/posts/algorithm/string/kmp/","tags":["KMP"],"title":"字符串匹配之KMP算法","uri":"/posts/algorithm/string/kmp/"},{"categories":["学习"],"collections":null,"content":" 问题 给定长度为 $n$，$m$ 的目标字符串 $\\textbf{S}$ 和模式字符串 $\\textbf{P}$，判断 $\\textbf{P}$ 是否是 $\\textbf{S}$ 的子串。若是，返回匹配位置；否则返回 $-1$。 ","date":"2019-08-18","heading":"","objectID":"/posts/algorithm/string/kmp/:0:0","tags":["KMP"],"title":"字符串匹配之KMP算法","uri":"/posts/algorithm/string/kmp/#"},{"categories":["学习"],"collections":null,"content":"暴力算法 模式串从目标串的第一个元素开始匹配 匹配失败，则目标串右移一位重新判断是否匹配模式串 暴力算法最优情况 最坏情况为 $O(mn)$。 暴力算法最坏情况 ","date":"2019-08-18","heading":"暴力算法","objectID":"/posts/algorithm/string/kmp/:1:0","tags":["KMP"],"title":"字符串匹配之KMP算法","uri":"/posts/algorithm/string/kmp/#暴力算法"},{"categories":["学习"],"collections":null,"content":"KMP算法 通过观察我们可以提问： 是否遇到不匹配时，只能右移 一位 并重新判断？ 当模式串的第 $k$ 位失配时，前 $k-1$ 位是已经匹配的。那么可以问这样一个问题： 右移多少位之后匹配的指针不必往前移动，在当前位置的基础上继续往后开始匹配，直到匹配成功或者再次遇到失配位？ kmp算法示意图 首先提出一个概念：字符串前缀和后缀的最大公共长度。假设最大公共长度为 $j$，字符串长度为 $n$，那么右移 $n-j$ 位后，前 $j$ 位是已经匹配了的。 前后缀最大公共长度 最大公共长度中的最大表示为了匹配需要右移的最少位数。也许右移更多位也能匹配，但可能会漏掉解。 ","date":"2019-08-18","heading":"KMP算法","objectID":"/posts/algorithm/string/kmp/:2:0","tags":["KMP"],"title":"字符串匹配之KMP算法","uri":"/posts/algorithm/string/kmp/#kmp算法"},{"categories":["学习"],"collections":null,"content":"Next数组 现在我们清楚了，为了使匹配指针不往前移，有效利用已经获得的匹配信息，我们需要对模式串进行预处理。直观地说就是我们需要知道当匹配到模式串的第几个元素失配时，应该右移多少位。这就是Next数组的含义。 在Python中，数组下标从 $0$ 开始，以此为前提。假设下标为 $j$（第 $j\u0026#43;1$ 位）失配，则匹配长度为 $j$。又设最大公共长度为 $i$，则应右移 $j-i$ 位，使失配位为模式串第 $i\u0026#43;1$ 位，即下标为 $i$。故有 $\\text{next}[j]=i$。 问题变为 如何快速计算最大公共长度 $i$ ？ Next数组的算法步骤如下图所示，稍后再详细讨论其过程。 Next数组算法步骤示意图 假设已知模式串 $\\text{P}$ 有 $\\text{next}[q]=k$，说明前 $q$ 位字符串的最大公共长度为 $k$。即有 $\\text{P}[0]=\\text{P}[q-k],\\cdots, \\text{P}[k-1]=\\text{P}[q-1]$。问 $\\text{next}[q\u0026#43;1]=?$ Next数组计算 若 $\\text{P}[q]=\\text{P}[k]$，则$\\text{next}[q\u0026#43;1]=\\text{next}[q]\u0026#43;1=k\u0026#43;1$。若 $\\text{P}[q]\\neq \\text{P}[k]$，那么 $\\text{next}[q]$ 等于多少？ Next数组计算 假设 $\\text{next}[q\u0026#43;1]=j\u0026#43;1$，则有 $$\\text{P}[0] = \\text{P}[q-j],\\cdots,\\text{P}[j-1]=\\text{P}[q-1],\\text{P}[j]=\\text{P}[q] $$ 又因为 $\\text{next}[q]=k$，所以有 $$ \\text{P}[k-j]=\\text{P}[q-j],\\cdots,\\text{P}[k-1]=\\text{P}[q-1] $$ 由上面两式可知， $$ \\text{P}[0]=\\text{P}[k-j],\\cdots,\\text{P}[j-1]=\\text{P}[k-1] $$这说明 $j$ 最大可以取到 $\\text{next}[k]$，也就是我们应该比较 $\\text{P}[q]$ 和 $\\text{P}[\\text{next}[\\text{next}[q]]]$。经过一点思考可知，$\\text{P}[q]$ 将依次与序列 $\\{\\text{P}[\\text{next}[q]],\\text{P}[\\text{next}[\\text{next}[q]]],\\cdots,\\text{P}[\\text{next}^{(n)}[q]] \\}$ 中的元素进行比较，直到相等或$\\text{next}^{(n)}[q]=0$。 ","date":"2019-08-18","heading":"Next数组","objectID":"/posts/algorithm/string/kmp/:3:0","tags":["KMP"],"title":"字符串匹配之KMP算法","uri":"/posts/algorithm/string/kmp/#next数组"},{"categories":["学习"],"collections":null,"content":"Python 实现 def Next(p): \"\"\" 返回模式串的Next（优化）数组 \"\"\" ans = [0 for _ in p] for i in range(1, len(p) - 1): k = ans[i] while k: if p[k] == p[i]: ans[i+1] = k + 1 k = 0 else: k = ans[k] if ans[i+1] == 0 and p[0] == p[i]: ans[i+1] = 1 # 第二个for循环给出优化KMP算法的Next数组 for i in range(2, len(p)): if ans[i] == i - 1: ans[i] = 0 return ans def KMP(s, p): \"\"\" 返回模式串 p 在目标串 s 中第一次出现的下标，否则返回 -1 \"\"\" _next = Next(p) i, j = 0, 0 ns, np = len(s), len(p) while i \u003c ns and j \u003c np: if s[i] == p[j]: i, j = i + 1, j + 1 else: if j == 0: i = i + 1 else: # 遇到失配时快速移动模式串，优于暴力算法的地方 j = _next[j] if j == np: return i - j return -1","date":"2019-08-18","heading":"Python 实现","objectID":"/posts/algorithm/string/kmp/:4:0","tags":["KMP"],"title":"字符串匹配之KMP算法","uri":"/posts/algorithm/string/kmp/#python-实现"},{"categories":["技术"],"collections":null,"content":"利用Scipy求解数值积分初值常微分方程组","date":"2019-07-09","objectID":"/posts/python/scipy-ode/","tags":["Scipy","数值计算"],"title":"Python-数值积分初值常微分方程组","uri":"/posts/python/scipy-ode/"},{"categories":["技术"],"collections":null,"content":"通常可以将常微分方程组的求解问题分成两类： 初值问题 边界问题 这里讨论初值常微分方程组的数值求解，用到的工具为scipy.integrate模块中的solve_ivp函数。 ","date":"2019-07-09","heading":"","objectID":"/posts/python/scipy-ode/:0:0","tags":["Scipy","数值计算"],"title":"Python-数值积分初值常微分方程组","uri":"/posts/python/scipy-ode/#"},{"categories":["技术"],"collections":null,"content":"参数 fun: 函数 一阶常微分方程组的右端，dy / dt = f(t, y)。t为标量，y可以是一维数组，或二维数组（每一列表示一个函数y）。若参数vectorized为真，则y可以是二维数组，这一矢量化的实现是为了在刚性问题中允许通过有限差分更快地估计雅可比矩阵。 t_span: 长度为2的浮点数元组 积分区间为[t0, tf]。 y0: 一维数组 系统初值。若要在复数域上求解，则使y0的data type为复数。 （以下为常用可选参数） method: 方法名 积分方法有如下几种： RK45（默认）：45阶龙格库塔法。 RK23：23阶龙格库塔法。 Radau：5阶隐式龙格库塔法。 RK23：23阶龙格库塔法。 Radau：5阶隐式龙格库塔法。 BDF：基于后向微分公式的隐式多步变阶法。 LSODA：Adams/BDF方法，带有刚性检测和转换功能。 非刚性问题使用 RK45或RK23，刚性问题使用Radau或BDF。也可以传递任意一个实现求解功能的OdeSolver的子类。 dense_output: 布尔值 是否计算连续解，默认为False。 t_eval: 一维数组或None 对解的值感兴趣的一组时刻。默认由solver决定。 events: 函数或函数列表 追踪的事件。默认为None，表示没有事件需要被追踪。每一个返回值为float的形如event(t, y)的函数都可以作为一个追踪器。solver会使用root-finding算法找到event(t, y) = 0的时刻t。每个事件函数可能拥有下面的两个属性： terminal: bool, optional 是否在事件发生时终止积分。无此属性等价于False。 direction: float, optional 表示穿过零点的方向。direction为正，则事件只会以负到正的方式穿过零点时才会被触发， 反之则direction为负。若为零，则已任意方式穿过零点都会被触发。无此属性等价于0。 vectorized: 布尔值 函数是否以矢量化方式实现。 ","date":"2019-07-09","heading":"参数","objectID":"/posts/python/scipy-ode/:0:1","tags":["Scipy","数值计算"],"title":"Python-数值积分初值常微分方程组","uri":"/posts/python/scipy-ode/#参数"},{"categories":["技术"],"collections":null,"content":"返回值 t: 数组 t_eval对应的时刻。 y: 数组 解在时刻t的值。 sol: OdeSolution 或 None 如果dense_output为False，则为None，否则为找到的解，作为OdeSolution的一个实例。 t_events: 列表 或 None 包含一组数组，每个数组对应一个事件，包含所有该事件发生时的时刻。 例子可在此处找到。 ","date":"2019-07-09","heading":"返回值","objectID":"/posts/python/scipy-ode/:0:2","tags":["Scipy","数值计算"],"title":"Python-数值积分初值常微分方程组","uri":"/posts/python/scipy-ode/#返回值"},{"categories":["学习"],"collections":null,"content":"次序统计量的相关函数","date":"2019-05-28","objectID":"/posts/math/statistic/order-statistics/","tags":["statistic"],"title":"次序统计量","uri":"/posts/math/statistic/order-statistics/"},{"categories":["学习"],"collections":null,"content":"设 $X_1,\\dots,X_n$ 独立同分布，概率密度函数与累积分布函数分别为 $f(x;\\theta)$ 和 $F(x;\\theta)$，$\\theta$ 为分布族的参数。为了简化表达式，后文将省略参数 $\\theta$，默认为已知常数。设 $X_{(1)}\\le \\dots\\le X_{(n)}$，为相应的次序统计量。$X_{(i)}$ 不对应某个确定的 $X_j$，因为每次试验，$x_1,\\dots,x_n$ 的相对大小关系都不同。再加上对 $X\\sim f(x)$绝对连续型分布的假设，即 $P(X_{(i)}=X_{(j)})=0,i\\ne j;P(X_{(1)}\\lt\\dots\\lt X_{(n)})=1$。下文将在前述基础上进行推导。 积分恒等式 $$ \\sum^n_{l=i}{n\\choose l}p^l(1-p)^{n-l}=i{n\\choose i}\\int^p_0dt\\ t^{i-1}(1-t)^{n-i},\\quad 0\\le t\\le p\\le 1. $$ ","date":"2019-05-28","heading":"","objectID":"/posts/math/statistic/order-statistics/:0:0","tags":["statistic"],"title":"次序统计量","uri":"/posts/math/statistic/order-statistics/#"},{"categories":["学习"],"collections":null,"content":"$X_{(i)}$的概率密度函数 $f_{(i)}(x)$ 表示 $X_{(i)}\\in[x,x\u0026#43;dx]$ 的精确到一阶的概率。这对应这样一种情况：有一个 $X$ 落在区间 $[x,x\u0026#43;dx]$，$i-1$ 个 $X$ 落在区间 $[-\\infty,x]$，$n-i$ 个 $X$ 落在区间 $[x\u0026#43;dx,\\infty]$。二阶及高阶对应的情形为，有两个及以上的 $X$ 落在区间 $[x,x\u0026#43;dx]$，计算 $f_{(i)}(x)$ 不需要考虑。因此有 $$ f_{(i)}(x)=n{n-1\\choose i-1}[F(x)]^{i-1}f(x)[1-F(x)]^{n-i} \\tag{1} $$","date":"2019-05-28","heading":"$X_{(i)}$的概率密度函数","objectID":"/posts/math/statistic/order-statistics/:1:0","tags":["statistic"],"title":"次序统计量","uri":"/posts/math/statistic/order-statistics/#的概率密度函数"},{"categories":["学习"],"collections":null,"content":"$X_{(i)}$的累积分布函数 由定义得， $$ \\begin{aligned} F_{(i)}(x)\u0026amp;=\\int^x_{-\\infty}dy\\ f_{(i)}(y) \\\\ \u0026amp;=n{n-1\\choose i-1}\\int^{F(x)}_0dF(y)\\ F(y)^{i-1}[1-F(y)]^{n-i} \\\\ \u0026amp;=n{n-1\\choose i-1}\\sum^n_{j=i}\\frac{(n-i)!}{(n-j)!}\\frac{(i-1)!}{j!}F(x)^j[1-F(x)]^{n-j}\\\\ \u0026amp;=\\sum^n_{j=i}{n\\choose j}F(x)^j[1-F(x)]^{n-j} \\end{aligned} \\tag{2} $$这个结果也很好理解，$F_{(i)}(x)$ 表示概率 $\\text{P}_\\text{r}(X_{(i)}\\le x)$。 $X_{(i)}\\le x$ 表示至少有 $j\\ge i$ 个 $X$ 落在区间 $[-\\infty,x]$，对每个 $j$，共有 ${n\\choose j}$ 个选择。 ","date":"2019-05-28","heading":"$X_{(i)}$的累积分布函数","objectID":"/posts/math/statistic/order-statistics/:2:0","tags":["statistic"],"title":"次序统计量","uri":"/posts/math/statistic/order-statistics/#的累积分布函数"},{"categories":["学习"],"collections":null,"content":"$X_{(i)},X_{(j)}$的联合概率密度函数 考虑 $X_{(i)},X_{(j)}$ 的联合概率密度函数 $f_{(i,j)}(x,y)$，不妨设 $i\\lt j$。类似 $f_{(i)}(x)$ 的推导，可以直接写出 $f_{(i,j)}(x,y)$ $$ f_{(i,j)}(x,y)=2{n\\choose i-1}{n-i\u0026#43;1\\choose n-j}{j-i\u0026#43;1\\choose 2}F(x)^{i-1}[1-F(x)]^{n-j}[F(y)-F(x)]^{j-i-1}f(x)f(y)I\\{x\\lt y\\} \\tag{3} $$此处 $$ I\\{x\\le y\\}= \\begin{cases} 1,\u0026amp; x\\le y\\\\ 0,\u0026amp; x\\gt y \\end{cases} \\tag{4} $$","date":"2019-05-28","heading":"$X_{(i)},X_{(j)}$的联合概率密度函数","objectID":"/posts/math/statistic/order-statistics/:3:0","tags":["statistic"],"title":"次序统计量","uri":"/posts/math/statistic/order-statistics/#的联合概率密度函数"},{"categories":["学习"],"collections":null,"content":"$X_{(i)},X_{(j)}$的联合累积分布函数 $$ \\begin{aligned} F_{(i,j)}(x,y)\u0026amp;=\\int^x_{-\\infty}dx\\int^y_{-\\infty}dy\\ f_{(i,j)}(x,y)\\\\ \u0026amp;=2{n\\choose i-1}{n-i\u0026#43;1\\choose j-i\u0026#43;1}{n-j\\choose2} \\int^x_{-\\infty}ds\\int^y_{-\\infty}dt\\ F(s)^{i-1}[F(t)-F(s)]^{j-i-1}[1-F(t)]^{n-i}f(s)f(t) I\\{s\\lt t\\}\\\\ \u0026amp;=2{n\\choose i-1}{n-i\u0026#43;1\\choose j-i\u0026#43;1}{n-j\\choose2} \\int^{F(x)}_0dF(s)\\ F(s)^{i-1}\\int^{F(y)}_{F(s)}dF(t)[F(t)-F(s)]^{j-i-1}[1-F(t)]^{n-j}I\\{s\\lt y\\}\\\\ \u0026amp;=2{n\\choose i-1}{n-i\u0026#43;1\\choose j-i\u0026#43;1}{n-j\\choose2} \\int^{F(x)}_0dF(s)\\ F(s)^{i-1}\\sum^{n-j}_{k=0}\\frac{(n-j)!}{(n-j-k)!}\\frac{(j-i-1)!}{(j-i\u0026#43;k)!}[F(y)-F(s)]^{j-i\u0026#43;k}[1-F(y)]^{n-j-k}I\\{s\\lt y\\}\\\\ \u0026amp;=i\\sum^n_{k=j}{n\\choose k}{k\\choose i}[1-F(y)]^{n-k} \\int^{F(x)}_0dF(s)\\ F(s)^{i-1}[F(y)-F(s)]^{k-i}I\\{x\\lt y\\}\\\\ \u0026amp;=i\\sum^n_{k=j}{n\\choose k}{k\\choose i}[1-F(y)]^{n-k} \\sum^{k-i}_{l=0}\\frac{(k-i)!}{(k-i-l)!}\\frac{(i-1)!}{(i\u0026#43;l)!}F(x)^{i\u0026#43;l}[F(y)-F(x)]^{k-i-l}I\\{x\\lt y\\}\\\\ \u0026amp;=\\sum^n_{k=j}\\sum^k_{l=i}\\frac{n!}{(n-k)!(k-l)!l!}F(x)^l[F(y)-F(x)]^{k-l}[1-F(y)]^{n-k} I\\{x\\lt y\\}\\\\ \u0026amp;=\\sum^n_{k=j}{n\\choose k}[1-F(y)]^{n-k}\\sum^k_{l=i}{k\\choose l}F(x)^l[F(y)-F(x)]^{k-l}I\\{x\\lt y\\} \\end{aligned} \\tag{5} $$同样，这个结果也很好理解， $F_{(i,j)}(x,y)=\\text{P}_\\text{r}(X_{(i)}\\le x,X_{(j)}\\le y)$。 $X_{(j)}\\le y$ 表示至少有 $k\\ge j$ 个 $X$ 落在区间 $[-\\infty,y]$，对每个 $k$，共有 ${n\\choose k}$ 个选择。然后，$X_{(i)}\\le x$ 表示至少有 $i\\le l\\le k$ 个 $X$ 落在区间 $[-\\infty, x]$，对每个 $l$，共有 ${k\\choose l}$ 个选择。再考虑到需要满足 $x\\lt y$，即得 $(5)$ 中最后一个等式。 ","date":"2019-05-28","heading":"$X_{(i)},X_{(j)}$的联合累积分布函数","objectID":"/posts/math/statistic/order-statistics/:4:0","tags":["statistic"],"title":"次序统计量","uri":"/posts/math/statistic/order-statistics/#的联合累积分布函数"},{"categories":["技术"],"collections":null,"content":"Vim配置技巧收录","date":"2019-02-18","objectID":"/posts/vim/tips/","tags":["Vim"],"title":"Vim 技巧","uri":"/posts/vim/tips/"},{"categories":["技术"],"collections":null,"content":"用法 ","date":"2019-02-18","heading":"用法","objectID":"/posts/vim/tips/:1:0","tags":["Vim"],"title":"Vim 技巧","uri":"/posts/vim/tips/#用法"},{"categories":["技术"],"collections":null,"content":"自定义映射 正确使用option/Alt键 option键在macOS下作为组合按键之一，对应于PC、Linux上的Alt键，但是功能不太一样。Mac上字母及一些标点符号与option组合会输出unicode字符。 原因在于按下按下option键发送的不是Escape Sequence。因此在vim,emacs等终端软件中，没法直接使用Alt键定义映射。 找到原因就好办，解决方法分为两步： 在终端中修改option/Alt键的行为 在vim,emacs等运行于终端下的软件中进行可能必要的设置 以终端程序kitty和alacritty为例，具体讨论解决方法中的第一步如何操作。 kitty 打开配置文件kitty.conf 找到选项macos_option_as_alt no，将no改为yes alacrity 打开配置文件alacrity.yml 定位到key_bindings 添加需要的键位设置，例如 - { key: x, mods: Alt, chars: \u0026quot;\\x1bx\u0026quot; } 以运行在终端下的vim为例，给出第二步中需要的具体操作。 function! Terminal_MetaMode(mode) set ttimeout if $TMUX != '' set ttimeoutlen=30 elseif \u0026ttimeoutlen \u003e 80 || \u0026ttimeoutlen \u003c= 0 set ttimeoutlen=80 endif if has('nvim') || has('gui_running') || $TERM_PROGRAM =~? 'iTerm2' return endif \" 👆表示只有某些终端下的vim需要👇的设置 function! s:metacode(mode, key) \" 这个函数的作用是告诉vim，的键盘序列码是多少 \" 这样vim将按照ttimeoutlen的设置来检查是否超时 \" 如果用 `noremap x ` 然后 `noremap ...`的方式， \" 则会使用timeoutlen来检查是否超时 \" 一般timeoutlen设置的相对更大，如果用它更容易误操作，降低使用体验 if a:mode == 0 exec \"set ","date":"2019-02-18","heading":"自定义映射","objectID":"/posts/vim/tips/:1:1","tags":["Vim"],"title":"Vim 技巧","uri":"/posts/vim/tips/#自定义映射"},{"categories":["技术"],"collections":null,"content":"自定义映射 正确使用option/Alt键 option键在macOS下作为组合按键之一，对应于PC、Linux上的Alt键，但是功能不太一样。Mac上字母及一些标点符号与option组合会输出unicode字符。 原因在于按下按下option键发送的不是Escape Sequence。因此在vim,emacs等终端软件中，没法直接使用Alt键定义映射。 找到原因就好办，解决方法分为两步： 在终端中修改option/Alt键的行为 在vim,emacs等运行于终端下的软件中进行可能必要的设置 以终端程序kitty和alacritty为例，具体讨论解决方法中的第一步如何操作。 kitty 打开配置文件kitty.conf 找到选项macos_option_as_alt no，将no改为yes alacrity 打开配置文件alacrity.yml 定位到key_bindings 添加需要的键位设置，例如 - { key: x, mods: Alt, chars: \u0026quot;\\x1bx\u0026quot; } 以运行在终端下的vim为例，给出第二步中需要的具体操作。 function! Terminal_MetaMode(mode) set ttimeout if $TMUX != '' set ttimeoutlen=30 elseif \u0026ttimeoutlen \u003e 80 || \u0026ttimeoutlen \u003c= 0 set ttimeoutlen=80 endif if has('nvim') || has('gui_running') || $TERM_PROGRAM =~? 'iTerm2' return endif \" 👆表示只有某些终端下的vim需要👇的设置 function! s:metacode(mode, key) \" 这个函数的作用是告诉vim，的键盘序列码是多少 \" 这样vim将按照ttimeoutlen的设置来检查是否超时 \" 如果用 `noremap x ` 然后 `noremap ...`的方式， \" 则会使用timeoutlen来检查是否超时 \" 一般timeoutlen设置的相对更大，如果用它更容易误操作，降低使用体验 if a:mode == 0 exec \"set ","date":"2019-02-18","heading":"正确使用option/Alt键","objectID":"/posts/vim/tips/:1:1","tags":["Vim"],"title":"Vim 技巧","uri":"/posts/vim/tips/#正确使用optionalt键"},{"categories":["技术"],"collections":null,"content":"用户自定义事件 自定义了切换透明和背景的函数，并绑定了快捷键。同时主题栏使用的是lightline。 我为dark和light两种背景选取不同的lightline主题，为了使背景切换之后，主题栏的切换也生效，这里使用用户自定义事件来解决问题。 当然这不是唯一的方法。 function! ToggleBackground() \" A lot of stuff is happening here. \" 定义一个自定义事件 doautocmd User ToggleBackgroundExit endfunction function! ToggleTransparent() \" A lot of stuff is happening here. \" 定义一个自定义事件 doautocmd User ToggleTransparentExit endfunction 现在可以在切换背景和透明度执行完成后做任何想做的事： autocmd User ToggleBackgroundExit call lightline#enable() ","date":"2019-02-18","heading":"用户自定义事件","objectID":"/posts/vim/tips/:1:2","tags":["Vim"],"title":"Vim 技巧","uri":"/posts/vim/tips/#用户自定义事件"},{"categories":["技术"],"collections":null,"content":"插件 插件管理器（vim-plug） vim-plug是一款异步插件管理器。具有安装速度快，延迟加载等特性。 这里要讨论的是刚发现的一个特性，可以让插件按照依赖关系进行加载。 栗子🌰：我有三款用于markdown的插件，分别是 plasticboy/vim-markdown jszakmeister/markdown2ctags iamcco/markdown-preview.nvim 原本可以在每个插件后面，用{'for':'markdown'}使其仅在编辑.md文件时才加载。但我发现 这种方法对第二款插件无效。因此想到这里的办法。 vim-plug在调用函数plug#load()加载插件之后会以插件名定义一个用户自定义事件。可以利用这一事件触发对其有依赖的插件的调用。如下为具体的使用案例： \" 以文件类型作为加载的依据 Plug 'iamcco/markdown-preview.nvim', {'for': 'markdown', 'do': 'cd app \u0026 yarn install'} \" 下面的插件无法按照文件类型进行加载 \" 故通过vim-plug 定义的用户自定义事件进行触发 Plug 'plasticboy/vim-markdown', {'on' : []} Plug 'jszakmeister/markdown2ctags', {'on': []} augroup vimplug_load_for_markdown autocmd! autocmd User markdown-preview.nvim plug#load( \\ 'vim-markdown', \\ 'markdown2ctags', \\ ) \\ | autocmd! vimplug_load_for_markdown augroup END 进一步，针对一个插件的所有配置可以做到这样：在插件被 加载前，不载入 加载后，自动载入 可以这样实现 \" 仍然以上面的插件为例 \" 在载入 vim-markdown之后自动加载相关配置。所有配置在函数SetVimmarkdown()之中 function! SetVimmarkdown() \" all configurations for vim-markdown are at here endfunction augroup load_for_vimmarkdown autocmd! autocmd User vim-markdown call SetVimmarkdown() | autocmd! load_for_vimmarkdown augroup END 中文输入法 vim 的模式切换与中文输入法并不总能够和谐共处。 英文 中文 normal \u0026#x1f642; \u0026#x1f641; insert \u0026#x1f642; \u0026#x1f642; 解决这个问题的方式大体上有两种，1、vim 提供中文输入法，那么这个问题就留给vim及其插件的开发者们去解决了；2、仍然使用外部中文输入法，如搜狗等， 那么关键在于以哪种方式自动切换输入法！ 这里沿用第二种思路。尝试过如下几种方式： 通过 vim 内置函数模拟按键操作来切换输入法。没有成功，但不排除可行性。 调用外部程序进行输入法切换。成功，但是并不完美。 所以最终采取的方案是通过调用外部程序在进入和离开insert模式时只能切换输入法。实现方式具体看下面的vim配置。 \" 定义缓冲区变量，使得可以对每个缓冲区单独监控输入法状态 au BufEnter * let b:im=0 \" 为了尽量不影响体验，通过异步机制来进行处理 \" 下面的函数用于在 job 进行过程中处理返回信息 fun! IMHandler(channel, msg) if a:msg !=? 'com.apple.keylayout.ABC' \" 切换到英文输入法之前保存当前状态 let b:im = 1 call job_start(['issw', 'com.apple.keylayout.ABC']) endif endfun fun! Lang2en() let job = job_start(['issw'], {\"out_cb\": \"IMHandler\"}) endfun \" 这里中文输入法的切换用了自己写的脚本，虽然也可以用上面的 issw 命令。 fun! Lang2zh() if b:im == 1 call job_start(['switchim']) endif endfun autocmd InsertEnter * call Lang2zh() autocmd BufEnter,InsertLeave * call Lang2en() 因为不想用破解软件来修改按键映射及快捷键，导致仍有不足之处： 调用的外部程序切换输入法不够快，仍然有一定几率产生困扰。 insert 模式下，如果要用搜狗输入法，从英文切换回中文比较麻烦。 ","date":"2019-02-18","heading":"插件","objectID":"/posts/vim/tips/:1:3","tags":["Vim"],"title":"Vim 技巧","uri":"/posts/vim/tips/#插件"},{"categories":["技术"],"collections":null,"content":"插件 插件管理器（vim-plug） vim-plug是一款异步插件管理器。具有安装速度快，延迟加载等特性。 这里要讨论的是刚发现的一个特性，可以让插件按照依赖关系进行加载。 栗子🌰：我有三款用于markdown的插件，分别是 plasticboy/vim-markdown jszakmeister/markdown2ctags iamcco/markdown-preview.nvim 原本可以在每个插件后面，用{'for':'markdown'}使其仅在编辑.md文件时才加载。但我发现 这种方法对第二款插件无效。因此想到这里的办法。 vim-plug在调用函数plug#load()加载插件之后会以插件名定义一个用户自定义事件。可以利用这一事件触发对其有依赖的插件的调用。如下为具体的使用案例： \" 以文件类型作为加载的依据 Plug 'iamcco/markdown-preview.nvim', {'for': 'markdown', 'do': 'cd app \u0026 yarn install'} \" 下面的插件无法按照文件类型进行加载 \" 故通过vim-plug 定义的用户自定义事件进行触发 Plug 'plasticboy/vim-markdown', {'on' : []} Plug 'jszakmeister/markdown2ctags', {'on': []} augroup vimplug_load_for_markdown autocmd! autocmd User markdown-preview.nvim plug#load( \\ 'vim-markdown', \\ 'markdown2ctags', \\ ) \\ | autocmd! vimplug_load_for_markdown augroup END 进一步，针对一个插件的所有配置可以做到这样：在插件被 加载前，不载入 加载后，自动载入 可以这样实现 \" 仍然以上面的插件为例 \" 在载入 vim-markdown之后自动加载相关配置。所有配置在函数SetVimmarkdown()之中 function! SetVimmarkdown() \" all configurations for vim-markdown are at here endfunction augroup load_for_vimmarkdown autocmd! autocmd User vim-markdown call SetVimmarkdown() | autocmd! load_for_vimmarkdown augroup END 中文输入法 vim 的模式切换与中文输入法并不总能够和谐共处。 英文 中文 normal \u0026#x1f642; \u0026#x1f641; insert \u0026#x1f642; \u0026#x1f642; 解决这个问题的方式大体上有两种，1、vim 提供中文输入法，那么这个问题就留给vim及其插件的开发者们去解决了；2、仍然使用外部中文输入法，如搜狗等， 那么关键在于以哪种方式自动切换输入法！ 这里沿用第二种思路。尝试过如下几种方式： 通过 vim 内置函数模拟按键操作来切换输入法。没有成功，但不排除可行性。 调用外部程序进行输入法切换。成功，但是并不完美。 所以最终采取的方案是通过调用外部程序在进入和离开insert模式时只能切换输入法。实现方式具体看下面的vim配置。 \" 定义缓冲区变量，使得可以对每个缓冲区单独监控输入法状态 au BufEnter * let b:im=0 \" 为了尽量不影响体验，通过异步机制来进行处理 \" 下面的函数用于在 job 进行过程中处理返回信息 fun! IMHandler(channel, msg) if a:msg !=? 'com.apple.keylayout.ABC' \" 切换到英文输入法之前保存当前状态 let b:im = 1 call job_start(['issw', 'com.apple.keylayout.ABC']) endif endfun fun! Lang2en() let job = job_start(['issw'], {\"out_cb\": \"IMHandler\"}) endfun \" 这里中文输入法的切换用了自己写的脚本，虽然也可以用上面的 issw 命令。 fun! Lang2zh() if b:im == 1 call job_start(['switchim']) endif endfun autocmd InsertEnter * call Lang2zh() autocmd BufEnter,InsertLeave * call Lang2en() 因为不想用破解软件来修改按键映射及快捷键，导致仍有不足之处： 调用的外部程序切换输入法不够快，仍然有一定几率产生困扰。 insert 模式下，如果要用搜狗输入法，从英文切换回中文比较麻烦。 ","date":"2019-02-18","heading":"插件管理器（vim-plug）","objectID":"/posts/vim/tips/:1:3","tags":["Vim"],"title":"Vim 技巧","uri":"/posts/vim/tips/#插件管理器vim-plug"},{"categories":["技术"],"collections":null,"content":"插件 插件管理器（vim-plug） vim-plug是一款异步插件管理器。具有安装速度快，延迟加载等特性。 这里要讨论的是刚发现的一个特性，可以让插件按照依赖关系进行加载。 栗子🌰：我有三款用于markdown的插件，分别是 plasticboy/vim-markdown jszakmeister/markdown2ctags iamcco/markdown-preview.nvim 原本可以在每个插件后面，用{'for':'markdown'}使其仅在编辑.md文件时才加载。但我发现 这种方法对第二款插件无效。因此想到这里的办法。 vim-plug在调用函数plug#load()加载插件之后会以插件名定义一个用户自定义事件。可以利用这一事件触发对其有依赖的插件的调用。如下为具体的使用案例： \" 以文件类型作为加载的依据 Plug 'iamcco/markdown-preview.nvim', {'for': 'markdown', 'do': 'cd app \u0026 yarn install'} \" 下面的插件无法按照文件类型进行加载 \" 故通过vim-plug 定义的用户自定义事件进行触发 Plug 'plasticboy/vim-markdown', {'on' : []} Plug 'jszakmeister/markdown2ctags', {'on': []} augroup vimplug_load_for_markdown autocmd! autocmd User markdown-preview.nvim plug#load( \\ 'vim-markdown', \\ 'markdown2ctags', \\ ) \\ | autocmd! vimplug_load_for_markdown augroup END 进一步，针对一个插件的所有配置可以做到这样：在插件被 加载前，不载入 加载后，自动载入 可以这样实现 \" 仍然以上面的插件为例 \" 在载入 vim-markdown之后自动加载相关配置。所有配置在函数SetVimmarkdown()之中 function! SetVimmarkdown() \" all configurations for vim-markdown are at here endfunction augroup load_for_vimmarkdown autocmd! autocmd User vim-markdown call SetVimmarkdown() | autocmd! load_for_vimmarkdown augroup END 中文输入法 vim 的模式切换与中文输入法并不总能够和谐共处。 英文 中文 normal \u0026#x1f642; \u0026#x1f641; insert \u0026#x1f642; \u0026#x1f642; 解决这个问题的方式大体上有两种，1、vim 提供中文输入法，那么这个问题就留给vim及其插件的开发者们去解决了；2、仍然使用外部中文输入法，如搜狗等， 那么关键在于以哪种方式自动切换输入法！ 这里沿用第二种思路。尝试过如下几种方式： 通过 vim 内置函数模拟按键操作来切换输入法。没有成功，但不排除可行性。 调用外部程序进行输入法切换。成功，但是并不完美。 所以最终采取的方案是通过调用外部程序在进入和离开insert模式时只能切换输入法。实现方式具体看下面的vim配置。 \" 定义缓冲区变量，使得可以对每个缓冲区单独监控输入法状态 au BufEnter * let b:im=0 \" 为了尽量不影响体验，通过异步机制来进行处理 \" 下面的函数用于在 job 进行过程中处理返回信息 fun! IMHandler(channel, msg) if a:msg !=? 'com.apple.keylayout.ABC' \" 切换到英文输入法之前保存当前状态 let b:im = 1 call job_start(['issw', 'com.apple.keylayout.ABC']) endif endfun fun! Lang2en() let job = job_start(['issw'], {\"out_cb\": \"IMHandler\"}) endfun \" 这里中文输入法的切换用了自己写的脚本，虽然也可以用上面的 issw 命令。 fun! Lang2zh() if b:im == 1 call job_start(['switchim']) endif endfun autocmd InsertEnter * call Lang2zh() autocmd BufEnter,InsertLeave * call Lang2en() 因为不想用破解软件来修改按键映射及快捷键，导致仍有不足之处： 调用的外部程序切换输入法不够快，仍然有一定几率产生困扰。 insert 模式下，如果要用搜狗输入法，从英文切换回中文比较麻烦。 ","date":"2019-02-18","heading":"中文输入法","objectID":"/posts/vim/tips/:1:3","tags":["Vim"],"title":"Vim 技巧","uri":"/posts/vim/tips/#中文输入法"},{"categories":["技术"],"collections":null,"content":"dot代码作图","date":"2019-02-16","objectID":"/posts/graphviz-introduction/","tags":["Graphviz"],"title":"Graphviz 介绍","uri":"/posts/graphviz-introduction/"},{"categories":["技术"],"collections":null,"content":"这篇文章参考自Drawing graphs with dot $dot$ 通过$DOT$语言来作图。该语言描述了三大对象：图(graphs)，节点(nodes)，边(edges)。同时支持有向图和无向图，并支持图的嵌套($subgraph$)。 默认采用有向图的布局方式，有另一个独立的布局工具，neato，用于无向图。 $dot$ 编译的方式是按顺序一行一行地处理 *.dot 文件中内容。所以各种对象的属性的有效性从从属性设置语句所在行开始直到下一次属性设置语句所在行之前。 完整的属性描述可以参考$Graphviz$官网的该页面。 ","date":"2019-02-16","heading":"","objectID":"/posts/graphviz-introduction/:0:0","tags":["Graphviz"],"title":"Graphviz 介绍","uri":"/posts/graphviz-introduction/#"},{"categories":["技术"],"collections":null,"content":"要素 一幅图可以包含如下要素： 注释 双斜杠 有向图 or 无向图 节点之间的关系 有向图：a-\u0026gt;b，节点 a 指向节点 b 无向图：a--b，节点 a 和节点 b连通 定义节点属性 形状，多边形polygon和record不知道怎么翻译 颜色 标签 等等 定义边的属性 形状 颜色 文本 等等 定义结构(record) 内部结构 方向，水平或竖直排列 标签 ","date":"2019-02-16","heading":"要素","objectID":"/posts/graphviz-introduction/:1:0","tags":["Graphviz"],"title":"Graphviz 介绍","uri":"/posts/graphviz-introduction/#要素"},{"categories":["技术"],"collections":null,"content":"样例 digraph G{ a -\u003e b -\u003e c; b -\u003e d; a [shape=polygon,sides=5,peripheries=3,color=lightblue,style=filled]; c [shape=polygon,sides=4,skew=.4,label=\"hello world\"] d [shape=invtriangle]; e [shape=polygon,sides=4,distortion=.7]; } {% asset_img graph-with-polygonal-shapes.svg %} digraph structs{ node [shape=record]; struct1 [shape=record,label=\" left| mid\\ dle| right\"]; struct2 [shape=record,label=\" one| two\"]; struct3 [shape=record,label=\"hello\\nworld | { b |{c| d|e}| f}|g|h \"]; struct1 -\u003e struct2; struct1 -\u003e struct3; } {% asset_img records-with-nested-fields.svg %} digraph G { node[shape=record,height=.1]; //定义node样式 node0[label=\" | A| \"]; //具体的一个node，含三个属性，第二个属性有名字 node0[label=\" | B| \"]; node0[label=\" | C| \"]; node0[label=\" | D| \"]; node0[label=\" | E| \"]; node0[label=\" | F| \"]; node0[label=\" | H| \"]; node0[label=\" | I| \"]; node0[label=\" | J| \"]; node0[label=\" | K| \"]; \"node0\":f2 -\u003e \"node1\": f1; //node0的第三个属性指向node1的第二个属性 \"node1\":f0 -\u003e \"node2\": f1; \"node1\":f1 -\u003e \"node3\": f2; \"node3\":f0 -\u003e \"node4\": f0; \"node3\":f1 -\u003e \"node5\": f1; \"node3\":f2 -\u003e \"node6\": f2; \"node6\":f1 -\u003e \"node7\": f1; \"node7\":f1 -\u003e \"node8\": f0; \"node2\":f2 -\u003e \"node9\": f1; } {% asset_img record-structure.svg %} ","date":"2019-02-16","heading":"样例","objectID":"/posts/graphviz-introduction/:2:0","tags":["Graphviz"],"title":"Graphviz 介绍","uri":"/posts/graphviz-introduction/#样例"},{"categories":["技术"],"collections":null,"content":"VPS开启BBR，改善网络连接","date":"2019-01-06","objectID":"/posts/linux/vultr-ubuntu16-04-bbr/","tags":["BBR"],"title":"Ubuntu 16.04 LTS 开启BBR","uri":"/posts/linux/vultr-ubuntu16-04-bbr/"},{"categories":["技术"],"collections":null,"content":"Vultr 上安装 ubuntu 16.04 并开启BBR。 ","date":"2019-01-06","heading":"","objectID":"/posts/linux/vultr-ubuntu16-04-bbr/:0:0","tags":["BBR"],"title":"Ubuntu 16.04 LTS 开启BBR","uri":"/posts/linux/vultr-ubuntu16-04-bbr/#"},{"categories":["技术"],"collections":null,"content":"更新内核 BBR只支持4.9+的Linux内核版本，因此首先检查并更新内核。 ## 查看当前内核版本 uname -r ## 安装最新版内核 sudo apt install linux-image-generic-hwe-16.04 ","date":"2019-01-06","heading":"更新内核","objectID":"/posts/linux/vultr-ubuntu16-04-bbr/:1:0","tags":["BBR"],"title":"Ubuntu 16.04 LTS 开启BBR","uri":"/posts/linux/vultr-ubuntu16-04-bbr/#更新内核"},{"categories":["技术"],"collections":null,"content":"重启 sudo reboot ","date":"2019-01-06","heading":"重启","objectID":"/posts/linux/vultr-ubuntu16-04-bbr/:2:0","tags":["BBR"],"title":"Ubuntu 16.04 LTS 开启BBR","uri":"/posts/linux/vultr-ubuntu16-04-bbr/#重启"},{"categories":["技术"],"collections":null,"content":"开启BBR 添加如下两行至文件/etc/sysctl.conf net.ipv4.default_qdisc=fq net.ipv4.tcp_congestion_control=bbr 启动bbr sysctl -p 验证是否成功 lsmod | grep bbr tcp_bbr 20480 1","date":"2019-01-06","heading":"开启BBR","objectID":"/posts/linux/vultr-ubuntu16-04-bbr/:3:0","tags":["BBR"],"title":"Ubuntu 16.04 LTS 开启BBR","uri":"/posts/linux/vultr-ubuntu16-04-bbr/#开启bbr"},{"categories":["技术"],"collections":null,"content":"Python的类与对象","date":"2018-12-14","objectID":"/posts/python/type-and-object/","tags":["类型与对象"],"title":"Python万物皆对象","uri":"/posts/python/type-and-object/"},{"categories":["技术"],"collections":null,"content":"类与对象 Python中有两种关系： 类与对象，顶点为Type。 继承，顶点为Object。 ","date":"2018-12-14","heading":"类与对象","objectID":"/posts/python/type-and-object/:1:0","tags":["类型与对象"],"title":"Python万物皆对象","uri":"/posts/python/type-and-object/#类与对象"},{"categories":["技术"],"collections":null,"content":"类与对象（Type） __class__方法查看当前对象所属的类，亦即由哪个类实例化得到。 Python 中的对象可以分成两种 不是类的对象，例如手里的一个🍎。 亦是类的对象，例如🍎，作为水果这个类的一个实例（对象）。 类比如下关系： 手里的🍎（创建自）🍎（创建自）水果（创建自）食物（创建自）\u0026hellip; 顶点 Python 版本 不是类的对象（创建自）亦是类的对象（创建自）类的类（创建自）Type（顶点） ","date":"2018-12-14","heading":"类与对象（Type）","objectID":"/posts/python/type-and-object/:1:1","tags":["类型与对象"],"title":"Python万物皆对象","uri":"/posts/python/type-and-object/#类与对象type"},{"categories":["技术"],"collections":null,"content":"继承（Object） __base__方法查看当前类的父类，亦即继承自哪个类。 Python 中的类也可以分成两种 普通类，类继承链条的末端，其实例不是类。 元类，类继承链条的非末端，其实例依然是类。 类比如下关系： 🍎（继承自）水果（继承自）食物（继承自）\u0026hellip; 顶点 Python 版本 普通类（继承自）元类（继承自）\u0026hellip;（继承自）Object（顶点） ","date":"2018-12-14","heading":"继承（Object）","objectID":"/posts/python/type-and-object/:1:2","tags":["类型与对象"],"title":"Python万物皆对象","uri":"/posts/python/type-and-object/#继承object"},{"categories":["技术"],"collections":null,"content":"Type/Object Type 作为 类，继承自 Object 对象，创建自 Type（自身） Object 作为 类，继承自 Object（自身） 对象，创建自 Type Python中类与对象的关系 老子说 道生一，一生二，二生三，三生万物 网上有人用它来理解 类对象 关系，觉得生动形象，故记录于此。 1、道 即 Type 2、一 即 元类 3、二 即 普通类 4、三 即 实例 5、万物 即 各种实例属性，方法 ","date":"2018-12-14","heading":"Type/Object","objectID":"/posts/python/type-and-object/:1:3","tags":["类型与对象"],"title":"Python万物皆对象","uri":"/posts/python/type-and-object/#typeobject"},{"categories":["技术"],"collections":null,"content":"Vim最强自动补全插件YCM的编译安装","date":"2018-12-09","objectID":"/posts/vim/plugin/youcompleteme-on-itp/","tags":["YouCompleteMe"],"title":"在集群（CentOS 6.6）上安装YouCompleteMe","uri":"/posts/vim/plugin/youcompleteme-on-itp/"},{"categories":["技术"],"collections":null,"content":"准备工作 ","date":"2018-12-09","heading":"准备工作","objectID":"/posts/vim/plugin/youcompleteme-on-itp/:1:0","tags":["YouCompleteMe"],"title":"在集群（CentOS 6.6）上安装YouCompleteMe","uri":"/posts/vim/plugin/youcompleteme-on-itp/#准备工作"},{"categories":["技术"],"collections":null,"content":"获取动态库 安装YouCompleteMe所需的其实只有一个动态库文件libclang.so.$version。由于YCM一直在升级，对clang的版本要求也越来越高。 无奈官网最新的几个版本都没有给出CentOS的预编译包，只能下载官网unknown-linux-gnu版本或自行编译clang得到库文件。 实际尝试过后发现官网unknown-linux-gnu版本虽然可以编译通过，但是无法使用。因此只能选择自行编译clang，具体编译安装过程可以{% post_link clang-from-source-on-centos6 参考 %}。 ","date":"2018-12-09","heading":"获取动态库","objectID":"/posts/vim/plugin/youcompleteme-on-itp/:1:1","tags":["YouCompleteMe"],"title":"在集群（CentOS 6.6）上安装YouCompleteMe","uri":"/posts/vim/plugin/youcompleteme-on-itp/#获取动态库"},{"categories":["技术"],"collections":null,"content":"配置gcc及g++版本 由于集群默认gcc版本太低，故需切换高版本gcc并设置CC和CXX export CC=gcc export CXX=g++ ## 不做上述设置则需要添加 CMAKE 选项 ## -DCMAKE_C_COMPILER=gcc ## —DCMAKE_CXX_COMPILER=g++ ","date":"2018-12-09","heading":"配置gcc及g++版本","objectID":"/posts/vim/plugin/youcompleteme-on-itp/:1:2","tags":["YouCompleteMe"],"title":"在集群（CentOS 6.6）上安装YouCompleteMe","uri":"/posts/vim/plugin/youcompleteme-on-itp/#配置gcc及g版本"},{"categories":["技术"],"collections":null,"content":"设置python版本 YouCompleteMe目前同时支持python2及python3，因此开启vim对python2/3的支持均可以。 ## 切换shell中python版本，使其与vim支持的一致 pyenv shell 2.7.15 ## or pyenv shell 3.6.6 问题：原本想编译vim使其同时支持python2和python3，但不知哪里出了问题，虽然显示同时支持，可实际上一个都不支持。 ","date":"2018-12-09","heading":"设置python版本","objectID":"/posts/vim/plugin/youcompleteme-on-itp/:1:3","tags":["YouCompleteMe"],"title":"在集群（CentOS 6.6）上安装YouCompleteMe","uri":"/posts/vim/plugin/youcompleteme-on-itp/#设置python版本"},{"categories":["技术"],"collections":null,"content":"安装 YCM ","date":"2018-12-09","heading":"安装 YCM","objectID":"/posts/vim/plugin/youcompleteme-on-itp/:2:0","tags":["YouCompleteMe"],"title":"在集群（CentOS 6.6）上安装YouCompleteMe","uri":"/posts/vim/plugin/youcompleteme-on-itp/#安装-ycm"},{"categories":["技术"],"collections":null,"content":"方法一 使用自带安装脚本install.py。 ## YCM 通过sha256来判断是否需要从官网下载需要的库文件 ## 为了避免自动下载，使其使用自行编译并打包的动态库 ## 需要修改如下文件 cd $HOME/.vim/vim_plugin/YouCompleteMe/third_party/ycmd/cpp/ycm/ vim CMakeLists.txt ## 修改第74行，将该sha256值替换为自己的压缩文件的sha256值，保存并推出。 ## 将打包的库文件放到如下目录中 mv /path/to/my/libclang-$version-*.tar.bz2 ../../clang_archive/ ","date":"2018-12-09","heading":"方法一","objectID":"/posts/vim/plugin/youcompleteme-on-itp/:2:1","tags":["YouCompleteMe"],"title":"在集群（CentOS 6.6）上安装YouCompleteMe","uri":"/posts/vim/plugin/youcompleteme-on-itp/#方法一"},{"categories":["技术"],"collections":null,"content":"方法二 使用我自己写的脚本进行Full Installation。 ## 自定义路径 pathbuild=$HOME/ycm_build pathdest=$HOME/.vim/vim_plugin/YouCompleteMe/third_party/ycmd/cpp ## 新建目录 build [ -d $pathbuild ] \u0026\u0026 rm -rf $pathbuild mkdir $pathbuild \u0026\u0026 cd $pathbuild ## 开始编译 cmake -DCMAKE_C_COMPILER=gcc \\ -DCMAKE_CXX_COMPILER=g++ \\ -DUSE_PYTHON2='OFF' \\ -DPYTHON_INCLUDE_DIR=$HOME/.pyenv/versions/3.6.6/include/python3.6m \\ -DPYTHON_LIBRARY=$HOME/.pyenv/versions/3.6.6/lib/libpython3.6m.so \\ -G \"Unix Makefiles\" -DEXTERNAL_LIBCLANG_PATH=/path/to/libclang.so . $pathdest cmake --build . --target ycm_core --config Release ## 删除目录 build rm -rf $pathbuild","date":"2018-12-09","heading":"方法二","objectID":"/posts/vim/plugin/youcompleteme-on-itp/:2:2","tags":["YouCompleteMe"],"title":"在集群（CentOS 6.6）上安装YouCompleteMe","uri":"/posts/vim/plugin/youcompleteme-on-itp/#方法二"},{"categories":["技术"],"collections":null,"content":"源码编译安装Vim","date":"2018-12-08","objectID":"/posts/vim/installation/","tags":["Vim"],"title":"Vim 编译安装","uri":"/posts/vim/installation/"},{"categories":["技术"],"collections":null,"content":"提前准备 为了脚本能顺利运行，需要预先安装好如下几个软件： curl jq wget ","date":"2018-12-08","heading":"提前准备","objectID":"/posts/vim/installation/:1:0","tags":["Vim"],"title":"Vim 编译安装","uri":"/posts/vim/installation/#提前准备"},{"categories":["技术"],"collections":null,"content":"下载源代码 从github下载vim，好处是可以自由切换版本。 # git clone https://github.com/vim/vim.git # 利用 curl、jq、awk 以及 GitHub 的 API 获取最新版本下载链接 # 利用 wget 下载并保存为 vim-latest.tar.gz file=vim-latest.tar.gz api=\"https://api.github.com/repos/vim/vim/tags\" download_url=$(curl -s $api | jq -r \".[] | .tarball_url\" | awk 'NR==1{print}') latest_version=$(curl -s $api | jq -r \".[] | .name\" | awk 'NR==1{print}') wget -O $file $download_url 解压，进入目录 mkdir vim-latest tar -zxf vim-latest.tar.gz -C vim-latest --strip-components=1 cd vim || exit ## 查看当前版本 git describe --tags ","date":"2018-12-08","heading":"下载源代码","objectID":"/posts/vim/installation/:2:0","tags":["Vim"],"title":"Vim 编译安装","uri":"/posts/vim/installation/#下载源代码"},{"categories":["技术"],"collections":null,"content":"编译安装 # 清除上次configure留下的痕迹 make distclean # 重新configure ./configure \\ --prefix=$HOME/Program/vim/${latest_version##*v} \\ --enable-multibyte=yes \\ --enable-gui=gtk2 \\ --with-x \\ --with-features=huge \\ --enable-python3interp=yes \\ --enable-perlinterp=yes \\ --enable-rubyinterp=yes \\ --enable-luainterp=yes \\ --with-luajit \\ --enable-cscope \\ --with-compiledby=徐武涛 \\ # 安装 make -j 16 \u0026\u0026 make install ","date":"2018-12-08","heading":"编译安装","objectID":"/posts/vim/installation/:3:0","tags":["Vim"],"title":"Vim 编译安装","uri":"/posts/vim/installation/#编译安装"},{"categories":["技术"],"collections":null,"content":"卸载 vim的Makefile提供了uninstall选项，因此卸载很容易。 cd vim \u0026\u0026 make uninstall","date":"2018-12-08","heading":"卸载","objectID":"/posts/vim/installation/:4:0","tags":["Vim"],"title":"Vim 编译安装","uri":"/posts/vim/installation/#卸载"},{"categories":["技术"],"collections":["Homebrew 系列"],"content":"macOS上Homebrew的使用","date":"2018-12-04","objectID":"/posts/macos/homebrew-guide/","tags":["Homebrew"],"title":"Homebrew 使用方法","uri":"/posts/macos/homebrew-guide/"},{"categories":["技术"],"collections":["Homebrew 系列"],"content":"查看已安装的包 brew list ","date":"2018-12-04","heading":"查看已安装的包","objectID":"/posts/macos/homebrew-guide/:1:0","tags":["Homebrew"],"title":"Homebrew 使用方法","uri":"/posts/macos/homebrew-guide/#查看已安装的包"},{"categories":["技术"],"collections":["Homebrew 系列"],"content":"更新 Homebrew 要获取最新的包列表，需先更新 Homebrew 自身。 brew update ","date":"2018-12-04","heading":"更新 Homebrew","objectID":"/posts/macos/homebrew-guide/:2:0","tags":["Homebrew"],"title":"Homebrew 使用方法","uri":"/posts/macos/homebrew-guide/#更新-homebrew"},{"categories":["技术"],"collections":["Homebrew 系列"],"content":"更新包（formula） 查看哪些包有新版本 brew outdated brew upgrade ## 更新左右包 brew upgrade $FORMULA ## 更新指定包 更新时自动清理旧版本，在brew upgrade前设置环境变量HOMEBREW_UPGRADE_CLEANUP。 ","date":"2018-12-04","heading":"更新包（formula）","objectID":"/posts/macos/homebrew-guide/:3:0","tags":["Homebrew"],"title":"Homebrew 使用方法","uri":"/posts/macos/homebrew-guide/#更新包formula"},{"categories":["技术"],"collections":["Homebrew 系列"],"content":"清理旧版本 brew cleanup ## 清理所有包的旧版本 brew cleanup $FORMULA ## 清理指定包的旧版本 brew cleanup -n ## 查看可清理的旧版本包，不会执行清除操作 彻底卸载，包括删除旧版本 brew uninstall formaula_name --force ","date":"2018-12-04","heading":"清理旧版本","objectID":"/posts/macos/homebrew-guide/:4:0","tags":["Homebrew"],"title":"Homebrew 使用方法","uri":"/posts/macos/homebrew-guide/#清理旧版本"},{"categories":["技术"],"collections":["Homebrew 系列"],"content":"锁定包 被锁定的包在更新时会被略过。 brew pin $FORMULA ## 锁定 brew unpin $FORMULA ## 取消锁定 ","date":"2018-12-04","heading":"锁定包","objectID":"/posts/macos/homebrew-guide/:5:0","tags":["Homebrew"],"title":"Homebrew 使用方法","uri":"/posts/macos/homebrew-guide/#锁定包"},{"categories":["技术"],"collections":["Homebrew 系列"],"content":"切换版本 时间一长，各个包可能都有多个版本并存。 brew switch $FORMULA $VERSION ## 切换包到指定版本 ","date":"2018-12-04","heading":"切换版本","objectID":"/posts/macos/homebrew-guide/:6:0","tags":["Homebrew"],"title":"Homebrew 使用方法","uri":"/posts/macos/homebrew-guide/#切换版本"},{"categories":["技术"],"collections":["Homebrew 系列"],"content":"查看依赖关系 brew deps --installed --tree ## 查看已安装的包的依赖关系","date":"2018-12-04","heading":"查看依赖关系","objectID":"/posts/macos/homebrew-guide/:7:0","tags":["Homebrew"],"title":"Homebrew 使用方法","uri":"/posts/macos/homebrew-guide/#查看依赖关系"},{"categories":["学习"],"collections":null,"content":"从作用量出发，推导欧拉-拉格朗日方程","date":"2018-12-02","objectID":"/posts/physics/euler-lagrange-equation/","tags":["最小作用量","诺特定理"],"title":"欧拉-拉格朗日方程","uri":"/posts/physics/euler-lagrange-equation/"},{"categories":["学习"],"collections":null,"content":"作用量 作用量 $S$ 是拉氏密度$\\mathcal{L}$ 在四维时空中的积分， $$ S = \\frac{1}{c}\\int d^4x \\mathcal{L}(\\phi,\\partial_\\mu\\phi) $$","date":"2018-12-02","heading":"作用量","objectID":"/posts/physics/euler-lagrange-equation/:1:0","tags":["最小作用量","诺特定理"],"title":"欧拉-拉格朗日方程","uri":"/posts/physics/euler-lagrange-equation/#作用量"},{"categories":["学习"],"collections":null,"content":"场的变分 对场的变分的贡献共来自两个方面，1、坐标无穷小变换；2、场自身的无穷小变换。下面分别讨论这两种情况。 1、坐标无穷小变换 $$ x^{\\mu}\\rightarrow x\u0026#39;^{\\mu}= x^{\\mu} \u0026#43; \\delta x^{\\mu} $$通常谈论的场的变分是指坐标不变时的变分 $\\bar{\\delta}$， $$ \\bar{\\delta}\\phi(x) = \\phi\u0026#39;(x) - \\phi(x) $$ 根据标量场 $\\phi(x)$ 在坐标变换下的性质 $\\phi\u0026#39;(x\u0026#39;)=\\phi(x)$，容易得到 $$ \\bar{\\delta}\\phi = -\\delta x^{\\mu}\\partial_{\\mu}\\phi $$2、场自身的无穷小变换 在没有坐标变换的情况下，场 $\\phi$ 产生了一个无穷小变换。此时坐标不变时的变分为 $$ \\bar{\\delta}\\phi = \\phi\u0026#39;(x) - \\phi(x) = \\delta\\phi $$ 注意 这里用 $\\delta$ 表示场自身的变分。通常情况下遇到的变分就是这种情况。 3、一般情况 如果同时考虑到坐标变换以及场自身的变分，则标量场 $\\phi(x)$ 满足的性质需修改为 $$ \\phi\u0026#39;(x\u0026#39;) = \\phi(x) \u0026#43; \\delta\\phi(x) $$ 故坐标不变时的变分为 $$ \\begin{aligned} \\bar{\\delta}\\phi(x) \u0026amp;= \\phi\u0026#39;(x) - \\phi(x) \\\\ \u0026amp;= \\phi(x-\\delta{x}) \u0026#43; \\delta\\phi(x-\\delta{x}) - \\phi(x) \\\\ \u0026amp;\\approx \\delta\\phi(x) - \\delta{x^\\mu}\\partial_{\\mu}\\phi(x) \\\\ \\end{aligned} $$根据上述讨论可知，场自身的变分一般情况下并非坐标不变时的变分 ($\\bar{\\delta} \\neq \\delta$)， 只有当不存在坐标变换的情况下两者才相等 ($\\bar{\\delta} = \\delta$)。 ","date":"2018-12-02","heading":"场的变分","objectID":"/posts/physics/euler-lagrange-equation/:2:0","tags":["最小作用量","诺特定理"],"title":"欧拉-拉格朗日方程","uri":"/posts/physics/euler-lagrange-equation/#场的变分"},{"categories":["学习"],"collections":null,"content":"拉氏密度的变分 一般情况下，作为标量场的拉氏密度的坐标不变时的变分为 $$ \\begin{aligned} \\bar{\\delta}\\mathcal{L}(x) \u0026amp;= \\mathcal{L}\u0026#39;(x) - \\mathcal{L}(x) \\\\ \u0026amp;= \\mathcal{L}(x-\\delta x) - \\delta\\mathcal{L}(x-\\delta x) - \\mathcal{L}(x) \\\\ \u0026amp;= \\delta\\mathcal{L}(x) - \\delta{x^\\mu}\\partial_{\\mu}\\mathcal{L} \\\\ \u0026amp;= \\frac{\\partial\\mathcal{L}}{\\partial\\phi}\\bar{\\delta}\\phi \u0026#43; \\frac{\\partial\\mathcal{L}}{\\partial\\partial_{\\mu}\\phi}\\bar{\\delta}\\partial_{\\mu}\\phi \\end{aligned} $$ ","date":"2018-12-02","heading":"拉氏密度的变分","objectID":"/posts/physics/euler-lagrange-equation/:2:1","tags":["最小作用量","诺特定理"],"title":"欧拉-拉格朗日方程","uri":"/posts/physics/euler-lagrange-equation/#拉氏密度的变分"},{"categories":["学习"],"collections":null,"content":"作用量的变分 $$ \\begin{aligned} \\delta S \u0026amp;= \\int \\delta(d^4 x)\\mathcal{L} \u0026#43; \\int d^4 x\\, \\delta{\\mathcal{L}} \\\\ \u0026amp;= \\int d^4x \\left(\\bar{\\delta}\\mathcal{L}\u0026#43;\\delta x^{\\mu}\\partial_{\\mu}{\\mathcal{L}} \u0026#43; \\mathcal{L}\\partial_{\\mu}\\delta{x^\\mu}\\right) \\\\ \u0026amp;= \\int d^4x \\left(\\bar{\\delta}\\mathcal{L} \u0026#43; \\partial_{\\mu}(\\mathcal{L}\\delta x^{\\mu})\\right) \\\\ \u0026amp;= \\int d^4x \\left(\\frac{\\partial\\mathcal{L}}{\\partial\\phi}\\bar{\\delta}\\phi \u0026#43; \\frac{\\partial\\mathcal{L}}{\\partial\\partial_{\\mu}\\phi}\\bar{\\delta}\\partial_{\\mu}\\phi \u0026#43; \\partial_{\\mu}(\\mathcal{L}\\delta x^{\\mu})\\right) \\\\ \u0026amp;= \\int d^4x \\left[\\left(\\frac{\\partial\\mathcal{L}}{\\partial\\phi} - \\partial_{\\mu}\\frac{\\partial\\mathcal{L}}{\\partial\\partial_{\\mu}\\phi}\\right)\\bar{\\delta}\\phi \u0026#43; \\partial_{\\mu}\\left(\\frac{\\partial\\mathcal{L}}{\\partial\\partial_{\\mu}\\phi}\\bar{\\delta}\\phi\u0026#43;\\mathcal{L}\\delta x^{\\mu}\\right) \\right] \\end{aligned} $$","date":"2018-12-02","heading":"作用量的变分","objectID":"/posts/physics/euler-lagrange-equation/:2:2","tags":["最小作用量","诺特定理"],"title":"欧拉-拉格朗日方程","uri":"/posts/physics/euler-lagrange-equation/#作用量的变分"},{"categories":["学习"],"collections":null,"content":"拉格朗日方程 假设坐标不变，只考虑场量的变分，则有 $$ \\begin{aligned} \u0026amp; \\bar{\\delta}\\phi = \\delta\\phi \\\\ \u0026amp; \\delta x^{\\mu} = 0 \\end{aligned} $$由表面项在4维时空边界上为零及变分 $\\bar{\\delta}\\phi$ 任意，作用量的变分取极值的条件给出拉格朗日方程 $$ \\partial_{\\mu}\\frac{\\partial\\mathcal{L}}{\\partial\\partial_{\\mu}\\phi} - \\frac{\\partial\\mathcal{L}}{\\partial\\phi} = 0 $$","date":"2018-12-02","heading":"拉格朗日方程","objectID":"/posts/physics/euler-lagrange-equation/:2:3","tags":["最小作用量","诺特定理"],"title":"欧拉-拉格朗日方程","uri":"/posts/physics/euler-lagrange-equation/#拉格朗日方程"},{"categories":["学习"],"collections":null,"content":"诺特定理 若场 $\\phi(x)$ 满足拉格朗日方程，则作用量的变分为某个表面项的4维积分。若场在变换（包括坐标变换和自身变换）前后均满足拉格朗日方程，且保持作用量不变。则 由 $$ \\delta S = \\frac{1}{c} \\int d^4x \\partial_{\\mu}j^{\\mu} = 0 $$ 以及 $$ j^{\\mu} = \\frac{\\partial\\mathcal{L}}{\\partial\\partial_{\\mu}\\phi}\\bar{\\delta}\\phi \u0026#43; \\mathcal{L}\\delta x^{\\mu} $$得到一个对应该变换的守恒流 $j^{\\mu}$。将 $j^{\\mu}$ 改写为 $$ j^{\\mu} = \\frac{\\partial\\mathcal{L}}{\\partial\\partial_{\\mu}\\phi}\\delta\\phi - \\left(\\frac{\\partial\\mathcal{L}}{\\partial\\partial_{\\mu}\\phi}\\partial_{\\nu} - \\mathcal{L}g^{\\mu}_{\\nu}\\right)\\delta x^{\\nu} $$$\\delta\\phi$ 和 $\\delta x^{\\mu}$ 现在是保持拉格朗日方程不变的变换。 ","date":"2018-12-02","heading":"诺特定理","objectID":"/posts/physics/euler-lagrange-equation/:2:4","tags":["最小作用量","诺特定理"],"title":"欧拉-拉格朗日方程","uri":"/posts/physics/euler-lagrange-equation/#诺特定理"},{"categories":["技术"],"collections":null,"content":"在Jupyter中添加虚拟环境作为新内核","date":"2018-11-26","objectID":"/posts/python/jupyter-with-virtualenv/","tags":["Jupyter","Virtualenv"],"title":"在Jupyter中添加虚拟环境作为新内核","uri":"/posts/python/jupyter-with-virtualenv/"},{"categories":["技术"],"collections":null,"content":"添加内核 终端切换python版本很容易，但在jupyter中应该如何切换环境？ 假如有一个虚拟环境science，将其导入jupyter的过程如下： 1、进入虚拟环境science pyenv active science 2、安装ipykernel pip install ipykernel 3、为jupyter安装新内核 ipython kernel install --user --name science --display-name \"python3 (science)\" 4、查看可用内核 jupyter-kernelspec list Available kernerls: science /Users/xuwutao/Library/Jupyter/kernels/science python3 /Users/xuwutao/.pyenv/versions/3.7.1/Python.framework/Versions/3.7/share/jupyter/kernels/python3 jupyter中添加虚拟环境作为新内核 jupyter中添加虚拟环境作为新内核 ","date":"2018-11-26","heading":"添加内核","objectID":"/posts/python/jupyter-with-virtualenv/:1:0","tags":["Jupyter","Virtualenv"],"title":"在Jupyter中添加虚拟环境作为新内核","uri":"/posts/python/jupyter-with-virtualenv/#添加内核"},{"categories":["技术"],"collections":null,"content":"修改内核 内核在jupyter中以json文件方式存在。每新建一个内核，jupyter会在系统的特定位置新建文件夹，存储相应的json文件。这些位置分布如下： Unix Windows System /usr/share/jupyter/kernels /usr/local/share/jupyter/kernels %PROGRAMDATA\\jupyter\\kernels Env {sys.prefix}/share/jupyter/kernels User ~/Library/Jupyter/kernels (Mac) ~/.local/share/jupyter/kernels (Linux) %APPDATA%\\jupyter\\kernels ","date":"2018-11-26","heading":"修改内核","objectID":"/posts/python/jupyter-with-virtualenv/:2:0","tags":["Jupyter","Virtualenv"],"title":"在Jupyter中添加虚拟环境作为新内核","uri":"/posts/python/jupyter-with-virtualenv/#修改内核"},{"categories":["技术"],"collections":null,"content":"树莓派打开姿势","date":"2018-11-18","objectID":"/posts/linux/raspberry-install/","tags":["raspberrypi"],"title":"树莓派新手指南","uri":"/posts/linux/raspberry-install/"},{"categories":["技术"],"collections":null,"content":"刻录系统 选好合适的系统镜像之后，用dd命令刻录系统到 sd 卡中。 dd bs=16M if=/path/to/img of=/dev/sdc Mac系统下类似，主要注意输出文件是/dev/sdc而不是sd卡上的分区。 ","date":"2018-11-18","heading":"刻录系统","objectID":"/posts/linux/raspberry-install/:1:0","tags":["raspberrypi"],"title":"树莓派新手指南","uri":"/posts/linux/raspberry-install/#刻录系统"},{"categories":["技术"],"collections":null,"content":"开启SSH 最新版树莓派系统默认不开启ssh服务。没有显示器的情况下，这是不能忍的。好在有一个简单的办法可以解决，系统刻录完成后在boot分区下新建名为ssh的空白文件。 # 挂载并进入boot分区 touch ssh ","date":"2018-11-18","heading":"开启SSH","objectID":"/posts/linux/raspberry-install/:2:0","tags":["raspberrypi"],"title":"树莓派新手指南","uri":"/posts/linux/raspberry-install/#开启ssh"},{"categories":["技术"],"collections":null,"content":"启动树莓派 用网线连接树莓派和Mac，打开Mac终端，通过ssh连接树莓派。 # 搜索raspberrypi的ip地址 arp -a # 将公钥加入远程主机的 authorized_keys 文件中，实现无密码登陆 ssh-copy-id pi@raspberrypi ","date":"2018-11-18","heading":"启动树莓派","objectID":"/posts/linux/raspberry-install/:3:0","tags":["raspberrypi"],"title":"树莓派新手指南","uri":"/posts/linux/raspberry-install/#启动树莓派"},{"categories":["技术"],"collections":null,"content":"更新源 将文件/etc/apt/sources.list中的raspbian.raspberrypi.org替换为mirrors.ustc.edu.cn/raspbian。 树莓派的archive.raspberrypi.org软件源，也即/etc/apt/sources.list.d/raspi.list。 是由树莓派基金会提供的软件源，包括ui相关程序（如Raspbian的桌面环境PIXEL DE）及部分由树莓派基金会为树莓派编写的软件，通常与archive.raspbian.org一起使用。 将archive.raspberrypi.org/替换为mirrors.ustc.edu.cn/archive.raspberrypi.org/。 ","date":"2018-11-18","heading":"更新源","objectID":"/posts/linux/raspberry-install/:4:0","tags":["raspberrypi"],"title":"树莓派新手指南","uri":"/posts/linux/raspberry-install/#更新源"},{"categories":["技术"],"collections":null,"content":"初始化配置 输入命令 sudo raspi-config 进入配置模式。常用的有扩展系统分区，修改密码，启动设置等。 ","date":"2018-11-18","heading":"初始化配置","objectID":"/posts/linux/raspberry-install/:5:0","tags":["raspberrypi"],"title":"树莓派新手指南","uri":"/posts/linux/raspberry-install/#初始化配置"},{"categories":["技术"],"collections":null,"content":"修改swap空间大小 # 修改文件 sudo vim /etc/dphys-swapfile # 把 CONF_SWAPSIZE=100 改成需要的大小，单位是m # 重启dphys-swapfile服务 sudo service dphys-swapfile restart 查看当前swap空间 free -m ","date":"2018-11-18","heading":"修改swap空间大小","objectID":"/posts/linux/raspberry-install/:6:0","tags":["raspberrypi"],"title":"树莓派新手指南","uri":"/posts/linux/raspberry-install/#修改swap空间大小"},{"categories":["技术"],"collections":null,"content":"VNC 远程桌面 树莓派自带VNC服务器，通过配置实现远程图形化界面登陆。 树莓派官方VNC教程 ","date":"2018-11-18","heading":"VNC 远程桌面","objectID":"/posts/linux/raspberry-install/:7:0","tags":["raspberrypi"],"title":"树莓派新手指南","uri":"/posts/linux/raspberry-install/#vnc-远程桌面"},{"categories":["技术"],"collections":null,"content":"debian 安装后必做的几件事 1、安装shadowsocks。注意pip安装时不要--user选项。配置服务，开机自启动。 # 安装最新版 shadowsocks pip install https://github.com/shadowsocks/shadowsocks/archive/master.zip pip更新之后可能会遇到问题can not import name main 修改 /usr/bin/pip 文件。 # 修改前 from pip import main if __name__ == '__main__': sys.exit(main()) # 修改后 from pip import __main__ if __name__ == '__main__': sys.exit(__main__.main()) 2、安装pyenv git clone https://github.com/pyenv/pyenv.git ~/.pyenv 详细内容参见文章pyenv基础教程。 3、安装字体，VNC连接时视觉上不能遭罪。 ","date":"2018-11-18","heading":"debian 安装后必做的几件事","objectID":"/posts/linux/raspberry-install/:8:0","tags":["raspberrypi"],"title":"树莓派新手指南","uri":"/posts/linux/raspberry-install/#debian-安装后必做的几件事"},{"categories":["学习"],"collections":null,"content":"矢量函数可以分解为无旋（纵向）和无源（横向）两部分","date":"2018-10-08","objectID":"/posts/math/vector-tensor-decomposition/","tags":["Vector"],"title":"矢量函数分解","uri":"/posts/math/vector-tensor-decomposition/"},{"categories":["学习"],"collections":null,"content":" 结论 纵向无旋，横向无源 任意给定一个矢量函数 $w(\\boldsymbol{x})=(w_1(\\boldsymbol{x}),w_2(\\boldsymbol{x}),w_3(\\boldsymbol{x}))$。在傅立叶空间中，$\\boldsymbol{k}$模式的系数$w(\\boldsymbol{k})=(w_1(\\boldsymbol{k}),w_2(\\boldsymbol{k}),w_3(\\boldsymbol{k}))$ 可以分解为动量$k$的平行和垂直方向。 $$ w = w^{\\parallel} \u0026#43; w^{\\bot} $$且有 $w^{\\parallel} // \\boldsymbol{k}$ 及 $w^{\\bot}\\cdot{\\boldsymbol{k}} = 0$ 回到实空间中，发现 $w(\\boldsymbol{x})$ 可以分解为纵向和横向两部分，$w^{\\parallel}(\\boldsymbol{x})$ 和 $w^{\\bot}(\\boldsymbol{x})$。分别有如下性质： 纵向无旋 $$ \\nabla \\times w^{\\parallel}(\\boldsymbol{x}) = 0 $$ 横向无源 $$ \\nabla \\cdot w^{\\bot}(\\boldsymbol{x}) = 0 $$","date":"2018-10-08","heading":"","objectID":"/posts/math/vector-tensor-decomposition/:0:0","tags":["Vector"],"title":"矢量函数分解","uri":"/posts/math/vector-tensor-decomposition/#"},{"categories":["学习"],"collections":null,"content":"投影算符 傅立叶空间中，$\\boldsymbol{k}$ 模的纵向（横向）投影算符分别为 $$ \\begin{aligned} P^{\\parallel}(\\boldsymbol{k}) \u0026amp;= \\boldsymbol{\\hat{k}}\\otimes\\boldsymbol{\\hat{k}} \\\\ P^{\\perp}(\\boldsymbol{k}) \u0026amp;= 1 - \\boldsymbol{\\hat{k}}\\otimes\\boldsymbol{\\hat{k}} \\\\ \\end{aligned} $$ 或者分量表示 $$ \\begin{aligned} P^{\\parallel}_{ij}(\\boldsymbol{k}) \u0026amp;= \\boldsymbol{\\hat{k}}_i\\boldsymbol{\\hat{k}}_j \\\\ P^{\\perp}_{ij}(\\boldsymbol{k}) \u0026amp;= \\delta_{ij} - \\boldsymbol{\\hat{k}}_i\\boldsymbol{\\hat{k}}_j \\\\ \\end{aligned} $$","date":"2018-10-08","heading":"投影算符","objectID":"/posts/math/vector-tensor-decomposition/:1:0","tags":["Vector"],"title":"矢量函数分解","uri":"/posts/math/vector-tensor-decomposition/#投影算符"},{"categories":["学习"],"collections":null,"content":"矢量函数可以分解为无旋（纵向）和无源（横向）两部分","date":"2018-10-08","objectID":"/posts/physics/vector-tensor-decomposition/","tags":["Vector"],"title":"矢量函数分解","uri":"/posts/physics/vector-tensor-decomposition/"},{"categories":["学习"],"collections":null,"content":" 结论 纵向无旋，横向无源 任意给定一个矢量函数 $w(\\boldsymbol{x})=(w_1(\\boldsymbol{x}),w_2(\\boldsymbol{x}),w_3(\\boldsymbol{x}))$。在傅立叶空间中，$\\boldsymbol{k}$模式的系数$w(\\boldsymbol{k})=(w_1(\\boldsymbol{k}),w_2(\\boldsymbol{k}),w_3(\\boldsymbol{k}))$ 可以分解为动量$k$的平行和垂直方向。 $$ w = w^{\\parallel} \u0026#43; w^{\\bot} $$且有 $w^{\\parallel} // \\boldsymbol{k}$ 及 $w^{\\bot}\\cdot{\\boldsymbol{k}} = 0$ 回到实空间中，发现 $w(\\boldsymbol{x})$ 可以分解为纵向和横向两部分，$w^{\\parallel}(\\boldsymbol{x})$ 和 $w^{\\bot}(\\boldsymbol{x})$。分别有如下性质： 纵向无旋 $$ \\nabla \\times w^{\\parallel}(\\boldsymbol{x}) = 0 $$ 横向无源 $$ \\nabla \\cdot w^{\\bot}(\\boldsymbol{x}) = 0 $$","date":"2018-10-08","heading":"","objectID":"/posts/physics/vector-tensor-decomposition/:0:0","tags":["Vector"],"title":"矢量函数分解","uri":"/posts/physics/vector-tensor-decomposition/#"},{"categories":["学习"],"collections":null,"content":"投影算符 傅立叶空间中，$\\boldsymbol{k}$ 模的纵向（横向）投影算符分别为 $$ \\begin{aligned} P^{\\parallel}(\\boldsymbol{k}) \u0026amp;= \\boldsymbol{\\hat{k}}\\otimes\\boldsymbol{\\hat{k}} \\\\ P^{\\perp}(\\boldsymbol{k}) \u0026amp;= 1 - \\boldsymbol{\\hat{k}}\\otimes\\boldsymbol{\\hat{k}} \\\\ \\end{aligned} $$ 或者分量表示 $$ \\begin{aligned} P^{\\parallel}_{ij}(\\boldsymbol{k}) \u0026amp;= \\boldsymbol{\\hat{k}}_i\\boldsymbol{\\hat{k}}_j \\\\ P^{\\perp}_{ij}(\\boldsymbol{k}) \u0026amp;= \\delta_{ij} - \\boldsymbol{\\hat{k}}_i\\boldsymbol{\\hat{k}}_j \\\\ \\end{aligned} $$","date":"2018-10-08","heading":"投影算符","objectID":"/posts/physics/vector-tensor-decomposition/:1:0","tags":["Vector"],"title":"矢量函数分解","uri":"/posts/physics/vector-tensor-decomposition/#投影算符"},{"categories":["技术"],"collections":null,"content":"pyenv 是一个python版本管理软件。受到ruby的同类软件 rbenv和 ruby-build的启发。 ","date":"2018-09-30","heading":"","objectID":"/posts/python/pyenv-guide/:0:0","tags":["Pyenv"],"title":"Pyenv基础教程","uri":"/posts/python/pyenv-guide/#"},{"categories":["技术"],"collections":null,"content":"安装 pyenv ","date":"2018-09-30","heading":"安装 pyenv","objectID":"/posts/python/pyenv-guide/:1:0","tags":["Pyenv"],"title":"Pyenv基础教程","uri":"/posts/python/pyenv-guide/#安装-pyenv"},{"categories":["技术"],"collections":null,"content":"macOS 使用homebrew安装 brew update brew install pyenv ","date":"2018-09-30","heading":"macOS","objectID":"/posts/python/pyenv-guide/:1:1","tags":["Pyenv"],"title":"Pyenv基础教程","uri":"/posts/python/pyenv-guide/#macos"},{"categories":["技术"],"collections":null,"content":"Linux git clone https://github.com/pyenv/pyenv.git ~/.pyenv ","date":"2018-09-30","heading":"Linux","objectID":"/posts/python/pyenv-guide/:1:2","tags":["Pyenv"],"title":"Pyenv基础教程","uri":"/posts/python/pyenv-guide/#linux"},{"categories":["技术"],"collections":null,"content":"配置 在.bashrc中添加下列内容： export PYENV_ROOT=\"$home/.pyenv\" export PATH=\"$PYENV_ROOT/bin:$path\" ## 上面两行不需要，如果采用homebrew安装方式 eval \"$(pyenv init -)\" ","date":"2018-09-30","heading":"配置","objectID":"/posts/python/pyenv-guide/:2:0","tags":["Pyenv"],"title":"Pyenv基础教程","uri":"/posts/python/pyenv-guide/#配置"},{"categories":["技术"],"collections":null,"content":"依赖环境 pyenv可以用于安装多个python版本，并对其进行管理。为了避免出现可能的错误，安装之前需要准备好依赖环境。 macos brew install openssl readline sqlite xz zlib 如果运行 Mojave 或更高版本，还需额外安装 sudo installer -pkg /Library/Developer/CommandLineTools/Packages/macOS_SDK_headers_for_macOS_10.14.pkg -target / Debian/Ubuntu apt-get install -y make build-essential libssl-dev zlib1g-dev libbz2-dev libreadline-dev libsqlite3-dev wget curl llvm libncurses5-dev xz-utils tk-dev libxml2-dev libxmlsec1-dev libffi-dev 在服务器上import pandas as pd可能会出现lzma module could not found, your python was incomplete这样的错误。解决办法是安装库liblzma-dev。 apt install liblzma-dev ","date":"2018-09-30","heading":"依赖环境","objectID":"/posts/python/pyenv-guide/:3:0","tags":["Pyenv"],"title":"Pyenv基础教程","uri":"/posts/python/pyenv-guide/#依赖环境"},{"categories":["技术"],"collections":null,"content":"安装python pyenv install命令用于安装指定python版本。 为了添加framework支持，安装代码应如下 ## macOS env python_configure_opts=\"--enable-framework\" pyenv install 3.6.6 ## Linux env python_configure_opts=\"--enable-shared\" pyenv install 3.6.6 若提示错误no module named pyexpat，则用如下方式安装 SDKROOT=/Applications/Xcode.app/Contents/Developer/Platforms/macOSX.platform/Developer/SDKs/macOSX10.14.sdk MACOSX_DEPLOYMENT_TARGET=10.14 pyenv install 2.7.16 ","date":"2018-09-30","heading":"安装python","objectID":"/posts/python/pyenv-guide/:4:0","tags":["Pyenv"],"title":"Pyenv基础教程","uri":"/posts/python/pyenv-guide/#安装python"},{"categories":["技术"],"collections":null,"content":"参考命令 ","date":"2018-09-30","heading":"参考命令","objectID":"/posts/python/pyenv-guide/:5:0","tags":["Pyenv"],"title":"Pyenv基础教程","uri":"/posts/python/pyenv-guide/#参考命令"},{"categories":["技术"],"collections":null,"content":"查看已安装python版本 pyenv versions system * 2.7.15 (set by /users/xuwutao/.python-version) 3.6.6 ","date":"2018-09-30","heading":"查看已安装python版本","objectID":"/posts/python/pyenv-guide/:5:1","tags":["Pyenv"],"title":"Pyenv基础教程","uri":"/posts/python/pyenv-guide/#查看已安装python版本"},{"categories":["技术"],"collections":null,"content":"版本切换 pyenv总共设置了三个命令用于python的版本控制，按优先级由高到低以此为 pyenv shell pyenv local pyenv global pyenv shell 效果等价于设定环境变量pyenv_version。 pyenv shell 3.6.6 # unset pyenv shell --unset pyenv local 在当前目录下新建文件.python-version，记录python版本号，一行一个。如果当前目录下没有该文件，则一路往上寻找直到系统根目录。 pyenv local 2.7.15 # 等价于 touch .python-version \u0026\u0026 echo '2.7.15' \u003e !$ pyenv global 和pyenv global对应的文件为$(pyenv root)/version。 pyenv global 2.7.10 # 等价于 touch ~/.pyenv/version \u0026\u0026 echo '2.7.10' \u003e !$ 有一个特殊的版本名system是指pyenv之外的python（macos自带，homebrew安装版本等）。 三个命令都可以后接多个版本号。 ","date":"2018-09-30","heading":"版本切换","objectID":"/posts/python/pyenv-guide/:5:2","tags":["Pyenv"],"title":"Pyenv基础教程","uri":"/posts/python/pyenv-guide/#版本切换"},{"categories":["技术"],"collections":null,"content":"版本切换 pyenv总共设置了三个命令用于python的版本控制，按优先级由高到低以此为 pyenv shell pyenv local pyenv global pyenv shell 效果等价于设定环境变量pyenv_version。 pyenv shell 3.6.6 # unset pyenv shell --unset pyenv local 在当前目录下新建文件.python-version，记录python版本号，一行一个。如果当前目录下没有该文件，则一路往上寻找直到系统根目录。 pyenv local 2.7.15 # 等价于 touch .python-version \u0026\u0026 echo '2.7.15' \u003e !$ pyenv global 和pyenv global对应的文件为$(pyenv root)/version。 pyenv global 2.7.10 # 等价于 touch ~/.pyenv/version \u0026\u0026 echo '2.7.10' \u003e !$ 有一个特殊的版本名system是指pyenv之外的python（macos自带，homebrew安装版本等）。 三个命令都可以后接多个版本号。 ","date":"2018-09-30","heading":"pyenv shell","objectID":"/posts/python/pyenv-guide/:5:2","tags":["Pyenv"],"title":"Pyenv基础教程","uri":"/posts/python/pyenv-guide/#pyenv-shell"},{"categories":["技术"],"collections":null,"content":"版本切换 pyenv总共设置了三个命令用于python的版本控制，按优先级由高到低以此为 pyenv shell pyenv local pyenv global pyenv shell 效果等价于设定环境变量pyenv_version。 pyenv shell 3.6.6 # unset pyenv shell --unset pyenv local 在当前目录下新建文件.python-version，记录python版本号，一行一个。如果当前目录下没有该文件，则一路往上寻找直到系统根目录。 pyenv local 2.7.15 # 等价于 touch .python-version \u0026\u0026 echo '2.7.15' \u003e !$ pyenv global 和pyenv global对应的文件为$(pyenv root)/version。 pyenv global 2.7.10 # 等价于 touch ~/.pyenv/version \u0026\u0026 echo '2.7.10' \u003e !$ 有一个特殊的版本名system是指pyenv之外的python（macos自带，homebrew安装版本等）。 三个命令都可以后接多个版本号。 ","date":"2018-09-30","heading":"pyenv local","objectID":"/posts/python/pyenv-guide/:5:2","tags":["Pyenv"],"title":"Pyenv基础教程","uri":"/posts/python/pyenv-guide/#pyenv-local"},{"categories":["技术"],"collections":null,"content":"版本切换 pyenv总共设置了三个命令用于python的版本控制，按优先级由高到低以此为 pyenv shell pyenv local pyenv global pyenv shell 效果等价于设定环境变量pyenv_version。 pyenv shell 3.6.6 # unset pyenv shell --unset pyenv local 在当前目录下新建文件.python-version，记录python版本号，一行一个。如果当前目录下没有该文件，则一路往上寻找直到系统根目录。 pyenv local 2.7.15 # 等价于 touch .python-version \u0026\u0026 echo '2.7.15' \u003e !$ pyenv global 和pyenv global对应的文件为$(pyenv root)/version。 pyenv global 2.7.10 # 等价于 touch ~/.pyenv/version \u0026\u0026 echo '2.7.10' \u003e !$ 有一个特殊的版本名system是指pyenv之外的python（macos自带，homebrew安装版本等）。 三个命令都可以后接多个版本号。 ","date":"2018-09-30","heading":"pyenv global","objectID":"/posts/python/pyenv-guide/:5:2","tags":["Pyenv"],"title":"Pyenv基础教程","uri":"/posts/python/pyenv-guide/#pyenv-global"},{"categories":["学习"],"collections":null,"content":"阻尼振动的定量分析","date":"2018-09-24","objectID":"/posts/physics/oscillation/","tags":["oscillation"],"title":"阻尼振动","uri":"/posts/physics/oscillation/"},{"categories":["学习"],"collections":null,"content":"参考 以弹簧为模型讨论，当考虑空气阻力时，运动方程为 $$ ma = -kx - bv $$ 阻尼系数$b$可以随时间变化 阻尼振动的结果 振动为周期运动，周期由劲度系数$k$和阻尼系数$b$共同决定 $$ \\omega = \\sqrt{\\dfrac{k}{m}}\\rightarrow \\omega_N = \\sqrt{\\dfrac{k}{m}-\\dfrac{\\gamma^2}{4}} $$ 振幅随时间减小 $$ A \\rightarrow A_N = Ae^{-\\gamma t / 2} $$ 图像表示 阻尼振动 ","date":"2018-09-24","heading":"","objectID":"/posts/physics/oscillation/:0:0","tags":["oscillation"],"title":"阻尼振动","uri":"/posts/physics/oscillation/#"},{"categories":["学习"],"collections":null,"content":"定量分析 运动方程可以改写为 $$ \\dfrac{d^2x}{dt^2} \u0026#43; \\gamma\\dfrac{dx}{dt} \u0026#43; \\omega^2x = 0， \\gamma=\\dfrac{b}{m}\\text{和}\\omega=\\sqrt{\\dfrac{k}{m}} $$考虑同形式的复数方程 $$ \\dfrac{d^2Z}{dt^2} \u0026#43; \\gamma\\dfrac{dZ}{dt} \u0026#43; \\omega^2Z = 0 $$ $Z$ 的实数部分即为阻尼方程的解。令$Z = Ce^{At}$，$A$和$C$为复常数，代入上式得到$A$的方程 $$ A^2 \u0026#43; \\gamma A \u0026#43; \\omega^2 = 0 $$对应的解为 $$ A=-\\gamma/2\\pm i\\sqrt{\\omega^2-\\dfrac{\\gamma^2}{4}} $$ 假设阻力不大，即$\\omega\\gt\\gamma/2$。 由$Z$的通解 $$ Z=e^{-\\gamma t/2}(C_1e^{\u0026#43;i\\omega_N t} \u0026#43; C_2e^{-i\\omega_N t}) $$ 得阻尼方程的解为 $$ x = Re(Z) = e^{-\\gamma t/2}[D\\cos(\\omega_N t) - E\\sin(\\omega_N t)] $$ $D$ 和 $E$ 为常数。 令初始相位为$\\theta_0=\\tan^{-1}(\\dfrac{D}{E})$，$L=\\sqrt{D^2-E^2}$，$x$可以化简为 $$ x = Le^{-\\gamma t/2}\\cos(\\omega_N t \u0026#43; \\theta_0) $$","date":"2018-09-24","heading":"定量分析","objectID":"/posts/physics/oscillation/:1:0","tags":["oscillation"],"title":"阻尼振动","uri":"/posts/physics/oscillation/#定量分析"},{"categories":["技术"],"collections":null,"content":"强大的Python数据处理库————Pandas","date":"2018-09-21","objectID":"/posts/python/pandas-guide/","tags":["Pandas"],"title":"Pandas 简介","uri":"/posts/python/pandas-guide/"},{"categories":["技术"],"collections":null,"content":"学习目标 大致了解pandas库的DataFrame和Series数据结构 存取和处理DataFrame和Series中的数据 将CSV数据导入pandas库的DataFrame 对DataFrame重建索引来随机打乱数据 pandas 是一种存列数据分析API。它是用于处理和分析输入数据的强大工具，很多机器学习框架都支持将pandas数据结构作为输入。本文只介绍它的核心概念。有关更完整的参考，请访问 pandas文档网站。 ","date":"2018-09-21","heading":"学习目标","objectID":"/posts/python/pandas-guide/:1:0","tags":["Pandas"],"title":"Pandas 简介","uri":"/posts/python/pandas-guide/#学习目标"},{"categories":["技术"],"collections":null,"content":"基本概念 导入pandas API 并输出相应的 API 版本： from __future__ import print_function import pandas as pd pd.__version__ u'0.22.2' pandas中主要的数据被实现为以下两类： DataFrame，可以想象成一个Excel表，包含多个行和已命名的列。 Series，它是单一列。DataFrame 中包含一个或多个 Series，每个 Series 均有一个名称。 数据框架是用于 数据操控的一种常用抽象实现形式。 Spark和 R中也有类似的实现。 创建 Series 的一种方法时构建 Series 对象。例如： pd.Series(['San Francisco', 'San Jose', 'Sacramento']) DataFrame 和字典很像，Series 对应字典的 键-值对，Series 的名称为键，数据为值。例如像下面这样创建 DataFrame 对象： city_names = pd.Series(['San Francisco', 'San Jose', 'Sacramento']) population = pd.Series([852469, 1015785, 485199]) pd.DataFrame({'City name': city_names, 'Population': population}) 但在大多数情况下，是将整个文件加载到 DataFrame 中。 data = pd.read_csv('path or url', *args, **kwargs) ","date":"2018-09-21","heading":"基本概念","objectID":"/posts/python/pandas-guide/:2:0","tags":["Pandas"],"title":"Pandas 简介","uri":"/posts/python/pandas-guide/#基本概念"},{"categories":["技术"],"collections":null,"content":"访问数据 前面提到，DataFrame 和字典很像，还体现在访问数据的方式上。 cities['City name'] ## 返回名称为 City name 的 Series cities[0:2] ## 返回所有 Series 的前两个值构建的 DataFrame 此外，pandas针对高级 索引和选择提供了机器丰富的 API。 ","date":"2018-09-21","heading":"访问数据","objectID":"/posts/python/pandas-guide/:3:0","tags":["Pandas"],"title":"Pandas 简介","uri":"/posts/python/pandas-guide/#访问数据"},{"categories":["技术"],"collections":null,"content":"操控数据 可以向 Series 应用 Python 的基本运算指令。例如： population / 1000. Numpy是一种用于进行科学计算的常用工具包。pandas Series 可用作大多数 Numpy 函数的参数： import numpy as np np.log(population) 对于更复杂的单列转换，可以使用 Series.apply。像 Python 映射函数一样，Series.apply 将以参数形式接受 lambda函数，而该函数会应用于每个值。 下面的示例创建了一个指明 population 是否超过 100 万的新 Series： population.apply(lambda val: val \u003e 1000000) 0 False 1 True 2 False dtype: bool DataFrame 的修改方式也非常简单。例如，以下代码向现有 DataFrame 添加了两个 Series： cities['Area square miles'] = pd.Series([46.87, 176.53, 97.92]) cities['Population density'] = cities['Population'] / cities['Area square miles'] cities","date":"2018-09-21","heading":"操控数据","objectID":"/posts/python/pandas-guide/:4:0","tags":["Pandas"],"title":"Pandas 简介","uri":"/posts/python/pandas-guide/#操控数据"},{"categories":["技术"],"collections":null,"content":"Python绘图库matplotlib的学习笔记","date":"2018-09-19","objectID":"/posts/python/matplotlib-tips/","tags":["Matplotlib"],"title":"Matplotlib 技巧","uri":"/posts/python/matplotlib-tips/"},{"categories":["技术"],"collections":null,"content":" 收集各种技巧、解决问题的办法。不断加深理解、熟悉matplotlib的使用。 ","date":"2018-09-19","heading":"","objectID":"/posts/python/matplotlib-tips/:0:0","tags":["Matplotlib"],"title":"Matplotlib 技巧","uri":"/posts/python/matplotlib-tips/#"},{"categories":["技术"],"collections":null,"content":"中文 查看支持的中文字体 运行如下代码 from matplotlib.font_manager import FontManager import subprocess fm = FontManager() mat_fonts = set(f.name for f in fm.ttflist) output = subprocess.check_output( 'fc-list :lang=zh -f \"%{family}\\n\"', shell=True) zh_fonts = set(f.split(',', 1)[0] for f in output.split('\\n')) available = mat_fonts \u0026 zh_fonts # 可用中文字体 for f in available: print f 尝试发现matplotlib自带了一个可以显示中文的字体Arial Unicode MS。于是可以如下设置，不用下载字体，不用修改matplotlib配置文件。 from matplotlib import rcParams rcParams['font.sans-serif'] = ['Arial Unicode MS'] 搞定中文乱码！ ","date":"2018-09-19","heading":"中文","objectID":"/posts/python/matplotlib-tips/:1:0","tags":["Matplotlib"],"title":"Matplotlib 技巧","uri":"/posts/python/matplotlib-tips/#中文"},{"categories":["技术"],"collections":null,"content":"Figure ","date":"2018-09-19","heading":"Figure","objectID":"/posts/python/matplotlib-tips/:2:0","tags":["Matplotlib"],"title":"Matplotlib 技巧","uri":"/posts/python/matplotlib-tips/#figure"},{"categories":["技术"],"collections":null,"content":"设置图片大小 px(pixel), inch, pt(point)三者之间的关系为 1 px = constant size on screen 1 inch = dpi px # dpi is variable 1 inch = 72 pt # 确定关系 Figure size (figsize) 决定figure的尺寸，单位为inches。默认大小为[6.4, 4.8]。 Dots per inches (dpi) 决定figure包含多少像素。默认大小为100。设置为figsize=(w,h)的figure包含 px, py = w * dpi, h * dpi # pixels # e.g. # 6.4 inches * 100 dpi = 640 pixels 因此若要获得例如(1200, 600)大小的figure，单位为pixel，可以有各种不同的组合方式 figsize = (15. 7.5), dpi = 80 figsize = (12. 6) , dpi = 100 figsize = (8, 4) , dpi = 150 figsize = (6, 3) , dpi = 200 以上组合方式的不同之处在哪里？ 差异在图形中线条、文字的粗细不同。figure中大多数元素如线条、标记、文本的size的单位为points。Matplotlib设置 Points per inch (ppi) 等于72。因此无论 dpi 等于多少，一英寸只画72个点。或者说 dpi 决定线条粗细，figsize决定内容多少。 ","date":"2018-09-19","heading":"设置图片大小","objectID":"/posts/python/matplotlib-tips/:2:1","tags":["Matplotlib"],"title":"Matplotlib 技巧","uri":"/posts/python/matplotlib-tips/#设置图片大小"},{"categories":["学习"],"collections":null,"content":"关于波的一些关系","date":"2018-09-19","objectID":"/posts/physics/wave-theory/","tags":["Cosmology"],"title":"波的性质","uri":"/posts/physics/wave-theory/"},{"categories":["学习"],"collections":null,"content":"Note 公式 波数$k$ 波长$c$ $k=2\\pi/\\lambda$ 角速度$\\omega$ 周期$T$ $\\omega=2\\pi/T=2\\pi f$ 波速$c$ 波长$\\lambda$ $cT=\\lambda$ 波数$k$ 角速度$\\omega$ $ck=\\omega=2\\pi f$ 频率$f$ 波数$k$ $f=ck/\\lrp{2\\pi}=c/\\lambda$ ","date":"2018-09-19","heading":"Note","objectID":"/posts/physics/wave-theory/:1:0","tags":["Cosmology"],"title":"波的性质","uri":"/posts/physics/wave-theory/#note"},{"categories":["技术"],"collections":null,"content":"Linux文本处理三剑客之sed学习笔记","date":"2018-09-18","objectID":"/posts/bash/sed/","tags":["Bash","Sed"],"title":"Sed 学习笔记","uri":"/posts/bash/sed/"},{"categories":["技术"],"collections":null,"content":"sed 简介 Stream EDitor，一种流编辑器，强大的文本编辑工具。所有的操作都在模式空间进行，且每次只会将一行文本复制到其中等待 sed 命令对其操作。因此，从第二条命令开始，操作的对象都是上一条命令作用后的文本。 sed工作原理图 ","date":"2018-09-18","heading":"sed 简介","objectID":"/posts/bash/sed/:1:0","tags":["Bash","Sed"],"title":"Sed 学习笔记","uri":"/posts/bash/sed/#sed-简介"},{"categories":["技术"],"collections":null,"content":"命令格式 sed [option] 'COMMANDS' file 常用的option有 选项 含义 -e 表明后接sed命令。命令多于一条时不能省略。 -i 直接对文件进行操作。 -f 后接包含一列sed命令的脚本文件。 命令COMMANDS 通常格式为 'address[,address]cmd[options]' ## 三部分组成：地址、命令、具体操作 地址：右行号、正则表达式给出，用于指定需要操作的行。有些命令只能操作单行，有些可以操作连续多行。地址对所有的命令都是可选项，如果不指定，则作用在所有的行上。 命令：有追加（a）、改变（c）、插入（i）、替换（s）等最常用。 选项：格式由前面的命令决定。 ","date":"2018-09-18","heading":"命令格式","objectID":"/posts/bash/sed/:2:0","tags":["Bash","Sed"],"title":"Sed 学习笔记","uri":"/posts/bash/sed/#命令格式"},{"categories":["技术"],"collections":null,"content":"替换（s） options的格式为/pattern/replacement/flags。 pattern与replacement都支持正则表达式。 flags选项有 n：1~512的整数，表示只替换第n个匹配项。 g：替换全部匹配项 默认替换第一个匹配项 技巧 pattern和replacement中包含Shell变量$var。如果命令用单引号括起来，那么美元符号$分别表示行尾和符号本身。只有用双引号，$var才会被替换为变量值。 #!/bin/bash var=LINUX sed 's/linux/$var/' sonfile ...$var... ## linux 被替换为 $var var=LINUX sed \"s/linux/$var\" somefile ...LINUX... ## linux 被替换为 LINUX 替换命令（s）格式中的斜杠/作为定界符，可以换成任何其它符号（好像有一个例外），并且必须出现三次。通常当匹配项或替换项中出现斜杠/时会变更定界符，如#。 's#pattern#replacement#' 例子 删除空白行 sed -i '/^$/d' input.xxx ## -i, 修改源文件 输出某一行到其他文件 sed -n '3p' input.xxx \u003e\u003e output.xxx","date":"2018-09-18","heading":"替换（s）","objectID":"/posts/bash/sed/:2:1","tags":["Bash","Sed"],"title":"Sed 学习笔记","uri":"/posts/bash/sed/#替换s"},{"categories":["技术"],"collections":null,"content":"替换（s） options的格式为/pattern/replacement/flags。 pattern与replacement都支持正则表达式。 flags选项有 n：1~512的整数，表示只替换第n个匹配项。 g：替换全部匹配项 默认替换第一个匹配项 技巧 pattern和replacement中包含Shell变量$var。如果命令用单引号括起来，那么美元符号$分别表示行尾和符号本身。只有用双引号，$var才会被替换为变量值。 #!/bin/bash var=LINUX sed 's/linux/$var/' sonfile ...$var... ## linux 被替换为 $var var=LINUX sed \"s/linux/$var\" somefile ...LINUX... ## linux 被替换为 LINUX 替换命令（s）格式中的斜杠/作为定界符，可以换成任何其它符号（好像有一个例外），并且必须出现三次。通常当匹配项或替换项中出现斜杠/时会变更定界符，如#。 's#pattern#replacement#' 例子 删除空白行 sed -i '/^$/d' input.xxx ## -i, 修改源文件 输出某一行到其他文件 sed -n '3p' input.xxx \u003e\u003e output.xxx","date":"2018-09-18","heading":"技巧","objectID":"/posts/bash/sed/:2:1","tags":["Bash","Sed"],"title":"Sed 学习笔记","uri":"/posts/bash/sed/#技巧"},{"categories":["技术"],"collections":null,"content":"替换（s） options的格式为/pattern/replacement/flags。 pattern与replacement都支持正则表达式。 flags选项有 n：1~512的整数，表示只替换第n个匹配项。 g：替换全部匹配项 默认替换第一个匹配项 技巧 pattern和replacement中包含Shell变量$var。如果命令用单引号括起来，那么美元符号$分别表示行尾和符号本身。只有用双引号，$var才会被替换为变量值。 #!/bin/bash var=LINUX sed 's/linux/$var/' sonfile ...$var... ## linux 被替换为 $var var=LINUX sed \"s/linux/$var\" somefile ...LINUX... ## linux 被替换为 LINUX 替换命令（s）格式中的斜杠/作为定界符，可以换成任何其它符号（好像有一个例外），并且必须出现三次。通常当匹配项或替换项中出现斜杠/时会变更定界符，如#。 's#pattern#replacement#' 例子 删除空白行 sed -i '/^$/d' input.xxx ## -i, 修改源文件 输出某一行到其他文件 sed -n '3p' input.xxx \u003e\u003e output.xxx","date":"2018-09-18","heading":"例子","objectID":"/posts/bash/sed/:2:1","tags":["Bash","Sed"],"title":"Sed 学习笔记","uri":"/posts/bash/sed/#例子"},{"categories":["技术"],"collections":null,"content":"Python中的元类（Metaclass）","date":"2018-01-01","objectID":"/posts/python/metaclass/","tags":["Metaclass"],"title":"Python中的元类（Metaclass）","uri":"/posts/python/metaclass/"},{"categories":["技术"],"collections":null,"content":"元类（Metaclass） 面向对象编程语言中，元类是指实例为类的类。定义确定的类及其实例的行为。并非所有面向对象的语言都支持元类。每一种语言都拥有他自己的元对象原型，为一系列描述对象、类、元类之间如何相互作用的规则。 ","date":"2018-01-01","heading":"元类（Metaclass）","objectID":"/posts/python/metaclass/:1:0","tags":["Metaclass"],"title":"Python中的元类（Metaclass）","uri":"/posts/python/metaclass/#元类metaclass"},{"categories":["技术"],"collections":null,"content":"例子 在Python里，内置的类type是一个元类。考虑这样一个简单的Python类： class Car(object): def __init__(self, make, model, year, color): self.make = make self.model = model self.year = year self.color = color @property def description(self): \"\"\" Return a description of this car. \"\"\" return \"%s %s %s %s\" % (self.color, self.year, self.make, self.model) 在程序运行时，Car本身是类type的一个实例，类Car的源代码如上所示，有很多内容没有直接呈现出来，例如Car对象的大小、在内存中的二进制布局、它们是如何被分配内存的、每当一个Car实例被创建时初始化__init__方法会被自动调用等等。这些细节不仅在一个新的Car实例被创建时，还会在每次访问car的属性时起作用。没有元类的语言，这些细节是被语言本身所确定的，且无法被重载。在Python中，元类type控制了这些细节是实现的方式。具体的控制方式可以用另一个元类代替type来改变。 class AttributeInitType(type): def __call__(self, *args, **kwargs): \"\"\" Create a new instance. \"\"\" ## First, create the object in the normal default way. obj = type.__call__(self, *args) ## Additionally, set attributes on the new object. for name, value in kwargs.items(): setattr(obj, name, value) ## Return the new object. return obj 这个元类只重载了类的创建方式，类的其他方面及对象的行为仍然由type进行处理。 现在类Car可以使用新定义的元类重写。在Python2中通过在类定义中赋值给__metaclass__来实现： class Car(object): __metaclass__ = AttributeInitType @property def description(self): \"\"\" Return a description of this car. \"\"\" return \" \".join(str(getattr(self, attr, \"Unknown\")) for attr in self.__dict__) 在Python3中需要提供一个命名参数： class Car(object, mateclass=AttributeInitType): @property def description(self): \"\"\" Return a description of this car. \"\"\" return \" \".join(str(value) for value in self.__dict__.values()) Car的实例于是可以像下面这样实例化： new_car = Car(make='Toyota', model='Prius', year=2005, color='Green') old_car = Car(make='Ford', model='Prefect', year=1979) 关键字参数实例化的效果也可以不用元类实现： class Car(object): def __init__(self, **kwargs): for name, value in kwargs.items(): setattr(self, name, value) @property def description(self): return \" \".join(str(value) for value in self.__dict__.values())","date":"2018-01-01","heading":"例子","objectID":"/posts/python/metaclass/:1:1","tags":["Metaclass"],"title":"Python中的元类（Metaclass）","uri":"/posts/python/metaclass/#例子"},{"categories":["技术"],"collections":null,"content":"Bash编程技巧收录","date":"2017-06-09","objectID":"/posts/bash/bash-tips/","tags":["Bash"],"title":"有用的Bash编程例子","uri":"/posts/bash/bash-tips/"},{"categories":["技术"],"collections":null,"content":"持续更新 Bash 小技巧 技巧 检测程序是否已安装 #!/bin/bash check_software(){ local software=(vim git tmux npm) # 待测程序名 for soft in ${software[@]} do type $soft 2\u003e\u00261 \u003e /dev/null # 已安装，则返回零 if [ $? -ne 0 ]; then echo \"ERROR: **$soft** is not installed!\" exit 1 fi echo \"Checking $soft...ok!\" done } check_software 运行结果 bash check_software.sh Checking vim...ok! Checking git...ok! Checking tmux...ok! Checking npm...ok!","date":"2017-06-09","heading":"","objectID":"/posts/bash/bash-tips/:0:0","tags":["Bash"],"title":"有用的Bash编程例子","uri":"/posts/bash/bash-tips/#"}]