计算理论笔记 01A:有限自动机和正则语言

从这里开始,我们要进入计算理论的世界。计算理论(theory of computation)研究的核心问题就是计算(computation)。我们需要思考的问题是:什么是计算;什么可以计算或不可以计算;如果可以计算,这个问题有多难计算;如果不可以计算,这个问题不可以计算的程度有多少。这个系列(前半部分的)主要框架来自于浙江大学计算理论课程毛宇尘老师的授课,其中插入了大量笔者个人的想法和来源于其他参考资料的结论和思路,应当完全覆盖并超越这门课程本身。这门课程事实上只部分地回答了前两个问题,对于第三个问题一带而过,而对于第四个问题几乎完全没有讨论,但是在这个系列里,我会尽可能完整地展现整个理论从新生到现代的全貌。笔者才疏学浅,如有疏漏,望指正。

阅读更多

大概可能不难上手的 Vulkan 教程(1) 计算管线

在这里,我们将使用 Rust 作为主要的编程语言开始对 Vulkan API (Home | Vulkan | Cross platform 3D Graphics) 的介绍。当然,许多其他语言都是可用的,例如更古老的 C++ + OpenGL/Vulkan 等等, Python + ModernGL 也是一个非常常见且易学的组合。这里之所以使用 Rust 是因为我喜欢,而且它的许多语言特性使得我们可以更少被语言本身的表达方式所限制,同时减少出错的可能。关于 Rust 的资料不多,可供参考的有 The Rust Programming Language - The Rust Programming Language (rust-lang.org) 以及 CS 110L: Safety in Systems Programming (stanford.edu) 等。在前几篇文章中,我们将首先使用 Vulkano 库 (vulkano - Rust (docs.rs)) 来引入对基本流程的介绍,并且完成两个小例子作为开胃小菜。然后,我们将从 Vulkano 库深挖下去,一方面分析它的原始代码,另一方面尝试引入更多图形学的概念来实现更加复杂的任务。前者更加偏向于底层架构,而后者更加偏向于上层的代码实现。

接下来,我们将假定读者已经对 Rust 的基础语法有所了解,并且已经完成了对 Vulkan SDK 的安装。我们使用的 rustc 版本为 1.66.2,Vulkano 版本为 0.32.3,Vulkan SDK 版本为 1.3.236,均为笔者写作时的最新版本。本文的主要参考资料来自 Vulkano 的官方文档:Vulkano

阅读更多

抽象代数续论笔记 02 一些可解群、有限单群分类与范畴

第二次课延续了上一次课的内容,首先介绍了一些关于可解群的结论和有限单群分类的大致思路,然后开始范畴论部分,这一部分总体上维持了较为完整的框架,还是比较清晰的。

如果有时间可能补一个 1.5 期,用来介绍一下循环群,Sylow 定理及一些简单的内容,并且添加一些关于群结构描述的部分,但是现在先把这些写完吧。

阅读更多

抽象代数续论笔记 01 群、幺半群、可解群以及一些例子

因为这门课是续论课,所以对于很多基本的概念性内容都报以回顾性的态度,在这里也同样会过得很快,简单的证明也会略过。第一次课回顾了群的一些基本概念,给出了一些例子;定义了可解群且给出了一个矩阵群中的例子。

这门课的思路比较混乱,因为是一门拾遗类型的课程,主要是起到衔接的作用,因此这里的笔记也不算太有条理。

阅读更多

GDB 源码分析 03:函数前导代码分析(下)

在上一次分析中,我们基本上通读了对前导代码分析实现通用的支持的代码,在这里,我们将针对多种不同架构的代码进行分析。

这篇文章将不可避免地要求对一些架构中汇编指令有一定的熟悉程度,但基本上只需要了解汇编指令的含义即可,可以参考相关的开发者指南。我们将首先从相对简单的 RISC-V 架构的分析开始。

阅读更多

局部哈密顿问题的复杂性 01

原文为:arXiv:quant-ph/0406180

这篇文章挺难读的,但是也很有意思。大概会分成四次把解读更新完,其实原文在大部分地方已经讲的很清楚了,但是一些计算过程可能需要澄清,毕竟文中的证明有些给的实在简洁。

这篇文章主要讨论的问题是 $\text{2-}\large\text{L}\small{\text{OCAL }}\large{\text{H}}\small{\text{AMILTONIAN}}$ 问题的复杂度。作为第一部分这里主要把论文的前四节的比较复杂的证明过了一遍,主要是投影引理和对 Kitaev 构造的重述,虽然还没进入正题但也并不算简单,还是很有仔细研究的价值的。

阅读更多

GDB 源码分析 01:函数前导代码分析(上)

代码:gdb/prologue-value.hgdb/prologue-value.c

函数前导代码其实很简单也很固定,它主要就是进入一个函数时进行的代码操作,用来建立栈帧,并为临时变量开好空间。这里展示一个 x86 汇编的最简单情形:

1
2
3
push ebp
mov ebp, esp
sub esp, N

很简单,很友好,不是吗?

那么为什么要对这块代码进行重点分析呢?一方面,这是一个函数开头的部分,可以用来分析这个函数调用栈的情况,并分析这个函数所使用的临时变量地址;另一方面,虽然看起来这段代码很可爱,但是随着编译器的复杂化和在调度指令时日益激进的策略,它往往会变得面目全非。但无论如何,这段代码总归是相对简单的部分。

事实上,在现代的 gcc 编译器中往往会给出调用栈信息(call frame information, CFI),其中描述了如何寻找栈的基地址和存储的寄存器等信息,但是这些信息并不总是存在。如果它们不存在,我们就必须采用一些策略来试图解读这些信息。为了解读这些信息,我们就采用一种对指令“抽象解读”的策略,也就是下面将介绍的模糊计算策略。

阅读更多

量子计算与计算复杂度(M. N. Vyalyi) Lecture 1

在之前(还没更新)的课程中,主要讨论了量子计算的几种范式,并对其进行了一些形式化。从这里开始,我们将尝试对量子计算的复杂性理论做出详细的解释和说明,并表明其与经典计算复杂度类的关系。首先,有必要快速回顾一下经典复杂性理论带给我们的一些结果。

阅读更多