自学日记
Section 1
注:本意是记录每一门课的学习情况,但在后面由于越学越杂就干脆改成了记日记的形式。
大一前的暑假,大概复健了一下 ACM ,看了 CS50x 的课,但这些都没有在此记录。这里记录的都是 CS61A 的学习情况。看的和做的都是 24sp 的版本(传送门)。
2025.8.30
总算开始学CS61A了,祝我好运。
仅 Project 而言,正反馈真的很强,只需要按照要求把代码写完就行了,其他的已经被提供好了,真的是保姆级教程啊。
- HW 01 done
- Lab 00,01 done
- Lecture 1,2,3,4 done
- Hog Phase 1 done (Problem 0,1,2,3,4,5)
笔记
有趣的例子:输入 print(print(1),print(2)) 会输出
1
2
None None
这是因为非纯函数 print 会返回 None 。
Doctest 感觉很牛啊。现有如下代码( test.py ):
from operator import floordiv, mod
def divide_exact(n, d=10):
"""Return the quotient and remainder of dividing N by D.
>>> q, r = divide_exact(2013, 10)
>>> q
201
>>> r
3
"""
return floordiv(n, d), mod(n, d)
若运行指令 python -m doctest test.py -v 时,会返回如下信息:
Trying:
q, r = divide_exact(2013, 10)
Expecting nothing
ok
Trying:
q
Expecting:
201
ok
Trying:
r
Expecting:
3
ok
1 item had no tests:
test
1 item passed all tests:
3 tests in test.divide_exact
3 tests in 2 items.
3 passed.
Test passed.
高阶函数,感觉很牛。
def cube(k):
return pow(k, 3)
def summation(n, term):
"""
>>> summation(5,cube)
225
"""
total, k = 0, 1
while k <= n:
total, k = total + term(k), k + 1
return total
def make_adder(n):
"""
>>> add_three=make_adder(3)
>>> add_three(4)
7
"""
def adder(k):
return k + n
return adder
2025.8.31
这个可选的 Lecture 6 完全不知道在干嘛啊,大雾,就莫名其妙地听完了,之后可能再回过去看看吧
- Lecture 5,6 done
2025.9.1
稀里糊涂地学函数式编程,稀里糊涂地写完了第一个 Project ,但正反馈还是很强的。
装饰器也是个很牛的东西,记一下。
- Lab 02 done
- Lecture 7,8,9,10,11,12 done
- Hog done (Phase 2(Problem 6,7,8,9,10,11,12))
笔记
*args 语法表示传递给函数的所有参数, 然后,我们可以将 *args 传递给另一个函数,从而使用相同的参数调用该函数。请注意,柯里化阶段只是配置,不涉及 *args ,调用阶段才真正使用 *args 处理参数。
以 Problem 8 为例:
def make_averaged(original_function, samples_count=1000):
"""Return a function that returns the average value of ORIGINAL_FUNCTION
called SAMPLES_COUNT times.
To implement this function, you will have to use *args syntax.
>>> dice = make_test_dice(4, 2, 5, 1)
>>> averaged_dice = make_averaged(roll_dice, 40)
>>> averaged_dice(1, dice) # The avg of 10 4's, 10 2's, 10 5's, and 10 1's
3.0
"""
# BEGIN PROBLEM 8
"*** YOUR CODE HERE ***"
def f(*args):
total = 0
for _ in range(samples_count):
result = original_function(*args)
total += result
return total / samples_count
return f
# END PROBLEM 8
假设我们有一个函数 say_hello :
def say_hello():
print("Hello!")
如果,我们想在执行这个函数前打印一条日志,比如 “Function is called” ,最直接的方法是修改这个函数。但如果有很多函数都需要这个功能,逐个修改就非常麻烦且容易出错。这时,我们就可以用装饰器的思想。
我们创建一个新函数 log ,它接受一个函数作为参数,并返回一个新的函数。
def log(func): # func 是要被装饰的函数,比如 say_hello
def wrapper(): # 定义一个内部函数,它将“包装”原函数并添加新功能
print(f"Function {func.__name__} is called") # 新增的日志功能
return func() # 调用原始函数并返回其结果
return wrapper # 返回这个包装后的新函数
现在,我们可以手动“装饰” say_hello 函数:
def say_hello():
print("Hello!")
# 把 say_hello 传递给 log,log 返回 wrapper 函数
# 再将 wrapper 函数重新赋值给 say_hello
say_hello = log(say_hello)
# 现在调用 say_hello,实际上调用的是 wrapper 函数
say_hello()
# 输出:
# Function say_hello is called
# Hello!
Python 提供了 @ 符号作为装饰器的语法糖,让代码更简洁、更优雅。看到 @log 放在函数定义上面,我们就知道 say_hello 被 log 这个装饰器增强了。上面的手动赋值过程等价于:
@log # 这行代码等同于:say_hello = log(say_hello)
def say_hello():
print("Hello!")
say_hello()
2025.9.2
HW 03 里有附加挑战题 Q6 ,涉及到了“Y-组合子”,就单纯记一下,感觉很夸张,应该也用不上。
- HW 02,03 done
- Lab 03 done
- Cats Phase 1 done (Problem 0,1,2,3,4)
2025.9.3
看似两天,其实是一晚上写完了 Cats 。
数据抽象这个东西感觉确实挺抽象的,我听得似懂非懂感觉大概理解是个什么思想,后面学到 OOP 的时候关于会有更深刻的体会吧。
树这一课讲的也是真的抽象,神秘的表示方式看得我两眼一黑。
- HW 04 done
- Lab 04,05 done
- Lecture 13,14,15,16 done
- Cats done (Phase 2(Problem 5,6,7,8,9,10))
笔记
学到了怎么定义无穷大:
inf = float('inf')
简记一点列表的操作吧:
- 创建列表
基本创建
# 空列表
empty_list = []
empty_list = list()
# 有初始值的列表
numbers = [1, 2, 3, 4, 5]
fruits = ['apple', 'banana', 'orange']
mixed = [1, 'hello', 3.14, True]
二维/多维列表
# 2x3 二维列表
matrix = [
[1, 2, 3],
[4, 5, 6]
]
# 3x3x3 三维列表
cube = [
[[1, 2, 3], [4, 5, 6], [7, 8, 9]],
[[10, 11, 12], [13, 14, 15], [16, 17, 18]],
[[19, 20, 21], [22, 23, 24], [25, 26, 27]]
]
- 添加元素
append() - 在末尾添加单个元素
fruits = ['apple', 'banana']
fruits.append('orange') # ['apple', 'banana', 'orange']
extend() - 在末尾添加多个元素
fruits = ['apple', 'banana']
fruits.extend(['orange', 'grape']) # ['apple', 'banana', 'orange', 'grape']
insert() - 在指定位置插入元素
fruits = ['apple', 'banana']
fruits.insert(1, 'orange') # ['apple', 'orange', 'banana']
- 删除元素
remove() - 删除指定值的元素
fruits = ['apple', 'banana', 'orange']
fruits.remove('banana') # ['apple', 'orange']
pop() - 删除指定位置的元素并返回
fruits = ['apple', 'banana', 'orange']
removed = fruits.pop(1) # removed = 'banana', fruits = ['apple', 'orange']
del - 删除指定位置的元素
fruits = ['apple', 'banana', 'orange']
del fruits[1] # ['apple', 'orange']
- 访问元素
索引访问
fruits = ['apple', 'banana', 'orange']
print(fruits[0]) # 'apple' (第一个元素)
print(fruits[-1]) # 'orange' (最后一个元素)
切片访问
numbers = [0, 1, 2, 3, 4, 5]
print(numbers[1:4]) # [1, 2, 3] (索引1到3)
print(numbers[:3]) # [0, 1, 2] (前3个)
print(numbers[3:]) # [3, 4, 5] (从索引3开始)
- 修改元素
fruits = ['apple', 'banana', 'orange']
fruits[1] = 'grape' # ['apple', 'grape', 'orange']
- 常用方法
len() - 获取长度
fruits = ['apple', 'banana', 'orange']
print(len(fruits)) # 3
index() - 查找元素索引
fruits = ['apple', 'banana', 'orange']
print(fruits.index('banana')) # 1
count() - 统计元素出现次数
numbers = [1, 2, 2, 3, 2, 4]
print(numbers.count(2)) # 3
sort() - 排序
numbers = [3, 1, 4, 1, 5]
numbers.sort() # [1, 1, 3, 4, 5] (升序)
numbers.sort(reverse=True) # [5, 4, 3, 1, 1] (降序)
reverse() - 反转
numbers = [1, 2, 3, 4, 5]
numbers.reverse() # [5, 4, 3, 2, 1]
- 列表操作
+ - 列表拼接
list1 = [1, 2, 3]
list2 = [4, 5, 6]
combined = list1 + list2 # [1, 2, 3, 4, 5, 6]
* - 列表重复
numbers = [1, 2, 3]
repeated = numbers * 3 # [1, 2, 3, 1, 2, 3, 1, 2, 3]
in - 检查元素是否存在
fruits = ['apple', 'banana', 'orange']
print('apple' in fruits) # True
print('grape' in fruits) # False
- 列表推导式
# 创建平方数列表
squares = [x**2 for x in range(5)] # [0, 1, 4, 9, 16]
# 带条件的列表推导式
even_squares = [x**2 for x in range(10) if x % 2 == 0] # [0, 4, 16, 36, 64]
# 二维列表推导式
matrix = [[i*j for j in range(3)] for i in range(3)]
# [[0, 0, 0], [0, 1, 2], [0, 2, 4]]
- 遍历列表
fruits = ['apple', 'banana', 'orange']
# 直接遍历元素
for fruit in fruits:
print(fruit)
# 遍历索引和元素
for i, fruit in enumerate(fruits):
print(f"索引 {i}: {fruit}")
# 遍历二维列表
matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
for row in matrix:
for element in row:
print(element, end=' ')
print()
2025.9.4
又学到新东西了,生成器函数有点意思的。
目前为止学到的 OOP 感觉就是封装一个类,和之前的数据抽象类似的感觉。这个 Lab 的强度上来一点了,主要是要对着输出示例去推测代码咋写。
题外话,拿 Jupyter 去调试真好用吧。
- HW 05 done
- Lab 06 done
- Lecture 17,18,19,20,21 done
笔记
生成器函数使用 yield 可返回单个值,而用 yield from 可迭代生成器函数并返回所有值。值得注意的是,生成器函数直接返回的是生成器对象,也就是迭代器。任何对象只要实现了以下两个方法,就是迭代器:
__iter__()- 返回迭代器自身__next__()- 返回下一个值,如果没有更多值则抛出StopIteration
2025.9.5
迭代器这一块属实有点不会做。以及终于知道为什么 python 里树和列表都那么抽象了,原来是因为没有指针。呃呃。
- HW 05 done
- Lecture 22,23,24,25 done
- Ants Phase 1(Problem 0,1,2,3) done
2025.9.6
没忍住熬夜写 Ants 了。其实逐步实现一些功能以后,剩下的类似的功能也就是缝缝补补就行了,反而感觉这个 Project 没那么困难,比抽象迭代器、生成器好懂多了。
晚上又把剩下的也写完了,姑且先到此为止了。
- Ants done (Phase 2,3(Problem 4,5,6,7,8,9,10,11,12,Optional1,Optional2))
Section 2
终于结束了大一的区域赛之旅,哎,可惜就打了一场,但也终于有时间去学想学已久的 CS188 了。看的是 18fa 的版本(传送门),做的是 24sp 的版本。
2025.11.20
迷迷糊糊地听了 Intro ,看 Project 0 配置了一下环境,了解了一下 autograder ,虽然其实还是有点懵的。
- Lecture 1 done
- Project 0 done
2025.11.21
听着听着就睡着了,感觉这点东西都是大概了解的,但遇到各种新的名词还是有点云里雾里,一听一个不吱声。但看了 note 又感觉了解地清晰了一点,期待正式写 pacman 的时候。
- Lecture 2,3,4 done
2025.11.22
依旧是看 lec 看不懂,看 note 才看懂。
- Lecture 5 done
2025.11.23
和昨天一样,学得不多。
- Lecture 6 done
2025.11.24
又学了两课,然后看代码看得想死,感觉没有一个很好的阅读代码的能力啊,仅仅是这样量级的代码我就已经在开始的时候觉得无从下手了,哎哎,先写个 Q1 大致熟悉一下。
笔记
创建一个空集合:
s = set()
注意不能写:
s = {}
因为这指的是创建一个空字典。
新语法:
def my_function(a: int, b: Tuple[int, int], c: List[List], d: Any, e: float=1.0):
这用于指定 Python 应该为该函数传入的参数类型。在下面的示例中, a 应该是一个整数( int ), b 应该是一个包含两个 int 的 tuple , c 应该是一个任意类型的 Lists 的 List (即一个二维数组), d 与未指定类型相同,可以是任何值, e 应该是一个 float )。如果没有传入任何参数, e 值也会被设置为 1.0 。
- Lecture 7,8 done
- Project 1 Q1 done
2025.11.25
猛然发现 sp24 和 fa18 的课程安排顺序上有差异,不过也无所谓了,依旧是听 lec 听睡过去了,之后补 note 再细读。至于代码实现,因为遇到了许多问题,所以笔记上倒是写了不少较为具体的内容。之后也多在笔记里写点东西吧,照这么看 lec 就当打发时间水过去了(划掉)。
笔记
集合去重且有 add 方法,没有 append 方法。 append 是列表( list )的方法,列表不会自动去重,查找是否已访问需要 \(O(n)\) 。
Python 的 list 是动态数组,调用 list.insert(i, x) 会在索引 \(i\) 位置前插入元素,并把后面的元素整体向右移动。所以 insert(0, item) 把新元素放在索引 \(0\)(头部),原先第 \(0\) 个元素移到索引 \(1\) ,依次类推,实现“头部插入”,时间复杂度 \(O(n)\) 。
UCS 需要记录当前最优累计代价,发现更低代价要重新开放节点。因为在非统一步代价环境中,第一次访问某节点的路径代价可能不是全局最优。若一旦把节点标记且之后不再访问,就可能丢失后来出现的更低价路径,最终产生错误解。实际上,UCS 本质即无负边权的 Dijkstra 算法。
创建字典并赋值有两种写法:
dict = {key: value}
等价于
dict = {}
dict[key] = value
前者是创建字典的同时放入键值,后者是先建空字典再赋值。
get 是 Python 字典 dict 的内置实例方法 dict.get(key, default=None) ,这表示:查找键 key 对应的值;若键不存在返回提供的 default(未提供则返回 None )。我们可以写 dict.get(key, float("inf")) ,这样在不存在时会返回 inf 。
A* 搜索的优先级是 \(g(n) + h(n)\) ,其中: \(g(n)\) 是起点到当前状态的真实累计代价, \(h(n)\) 是启发式估计当前状态到目标的代价。和 UCS 不同的是,我们在优先队列的优先级判断里使用 \(f\) ,在节点上依然只存储 \(g\) 。
enumerate 是 Python 的一个内置函数,用于将一个可遍历的数据对象(如列表、元组或字符串)组合为一个索引序列,同时列出数据和数据下标。
for i, x in enumerate(arr):
print(i, x)
- Lecture 9 done
- Project 1 Q2,3,4,5 done
2025.11.26
今天又看了个 lec ,又没看懂,这都啥啊。把 Project 1 写完了,打算总结一下。同时又把到目前为止的 note 全看了一遍, sp24 和 note 不一致,但 fa18 是一致的,那好吧, note 也打算什么时候总结一下。
- Lecture 10 done
- Project 1 done (Q6,7,8)
CS188 Q1 小结
- Q1,2 是 DFS 和 BFS ,只需要用栈和队列就可以实现了。Q3 是 UCS ,和 Dijkstra 区别不大,都是用优先队列来维护一个最小值。而 Q4 则是 A* ,在 UCS 的基础上用启发值来作为优先队列的判断值。关于前 \(4\) 问的内容在上一天的笔记里写得更为详细。
- Q5 要求在 Q2 的基础上,找到角落的所有点。阅读代码会发现
PositionSearchProblem类,它也是从search.SearchProblem继承来的,我们参考该类就可以完成实现。 - Q6 要求在 Q4 的基础上,实现找到角落的所有点的启发式搜索。由于 Q4 里 A* 使用的是曼哈顿距离来作为 \(h\) ,所以我们在 Q6 中也要用曼哈顿距离。不难想到一个合法的启发式策略:先找到距离起点最近的角落点,然后再按照对所有角落点做最小生成树。注意,在该策略里的距离都是曼哈顿距离,显然这样是符合启发式的要求的。
- Q7 要求实现找到所有食物的启发式搜索。我们不妨对 Q6 进行一个拓展,我们可以先找到一个最近点,然后再对所有点做最小生成树。在尝试后发现在题目数据下会拓展 \(7000+\) 个节点这只能得 \(7/7\) 分,没能获得附加的第 \(8\) 分。阅读代码会发现,在最后还有一个
mazeDistance的函数,这可以求解出两点之间在迷宫里的实际距离,显然这比曼哈顿距离更为精确,我们将该距离作为启发式搜索的依据。此时在题目数据下仅拓展 \(255\) 个节点,远优于第 \(8\) 分要求的 \(7000\) 个节点。 - Q8 要求判断某个位置的食物是否被吃了,并且找到当前的最近点并给出路径。前者可以看到
pacman.py里的GameState类里的getFood函数,这就告诉了我们本题的做法。后者因为只要找到当前最近点,直接用 UCS 搜索当前最近即可。
整体上还是比较传统算法的,但要阅读那么多的代码去找是否有自己需要的功能,去理解代码的意思还是挺难的qwq哎哎
2025.11.27
随地大小睡。好在今天学的概率部分都没那么难,可以说本来就会了。
- Lecture 11,12,13 done
2025.11.29
两天合砍一个 lec ,这一课真有点烧脑了,主要还是英语不好的锅。但看明白了就很爽。
笔记
一条路径是否能传递变量间的依赖,核心看路径内所有连续三元组的激活状态:
若路径中存在至少一个非激活三元组(三元组中间节点被观测)→ 整条路径非激活(依赖传递被阻断);
若路径中所有三元组都激活(三元组中间节点未观测)→ 整条路径激活(依赖可传递)。
变量 \(X\) 和 \(Y\) 是否条件独立,取决于所有路径的整体激活状态:
若存在至少一条激活路径(可传递依赖)→ \(X\) 和 \(Y\) 不保证条件独立(存在潜在依赖关系);
若所有路径都非激活(依赖传递被全部阻断)→ \(X\) 和 \(Y\) 条件独立(无依赖关系)。
- Lecture 14 done
2025.11.30
依旧烧脑这一块,总算赶在这个周末把贝叶斯网络大致结束了。
- Lecture 15,16 done
2025.12.1
摸鱼。
- Project 2 Q1 done
2025.12.2
整体上重复性有点强(?)但就这么稀里糊涂地完成了。发现没写 HW 之后课程量直线减少。
- Project 2 done (Q2,3,4,5)
CS188 Q2 小结
- Q1 要求要对一个状态做出行动后的状态进行评估。题目提示了要参考食物位置和幽灵位置,并用倒数来计算。于是我们可以区分一下:靠近食物加分,靠近幽灵扣分,特别地,幽灵被惊吓时靠近也是加分的。不过距离幽灵太近时死亡的风险较大,我们可以添加一个距离幽灵过近的惩罚,降低死亡的风险。于是,评估函数就做完了。
- Q2 要求实现 Minimax 算法。因为不含任何剪枝,所以我们直接区分吃豆人和幽灵暴力分层就行了。
- Q3 要求对 Minimax 算法实现 α-β 剪枝。只需要额外添加两个参量 \(α,β\) ,并在实现细节上有所改动即可。
- Q4 要求实现 Expectimax 算法。题目说所有的幽灵不会按照最小值进行游戏,而是会随机游走,故我们只需考虑其期望,换言之,我们只需要在 Q2 的基础上对 Min 层改动返回值为期望即可。
- Q5 要求对一个状态进行评估。由于我们的 Q1 实现已经足够优秀,所以我们实际上直接沿用 Q1 就能得到满分了,因为 Q1 里虽说是在评估某个行动,但我们也是得出行动后的状态再进行评估的。
2025.12.4
今天有点摆,但还是把 Decision Network and VPIs 这一个 Lecture 和 Note 看完了,感觉这一节还是挺好理解的。
- Lecture 17 done
2025.12.5
这两节的公式看的眼花缭乱,稀里糊涂,推导又推得真的太神秘了,还用了一些很巧妙的技巧,太神奇了。
- Lecture 18,19 done
2025.12.8
由于期末周临近且学的课程变多变杂,所以还是以类似记日记的形式记吧。
考虑到 CS188 后续有深度学习以及用 pytorch 的 Project ,所以打算先看看B站的深度学习教程(传送门),先学习一下。卸载了以前杂七杂八的环境,重新配置了一下环境,下了点软件,同时又复习了一下 python 语言基础,明天开始正式学习。
2025.12.9
学了 Numpy,Pandas,Matplotlib 的语法,从看得津津有味到压根看不下去,哎哎。然后又试图看这个课的 DNN 部分,代码是完整地跟着跑了一遍,但这个着实太抽象了完全不理解啊,还是得学原理,哎,CNN 的代码部分还是之后再看吧。今天还问了两个学长一些学习路径,继续努力吧,还是得科学地学习。睡前学了会线代。
2025.12.10
学了一下李沐的 cs329p(传送门)的第3章,把机器学习的最基础的模型都简略介绍了一下。然后又看了 3B1B 的DL视频(传送门),对这些东西反复看几遍总是能加深理解的吧。文盲又在歹毒地学习。今天总结一下就是浅显地学了各种模型。
2025.12.11
今天在学 d2l ,把前面的几节属于导论和前置知识的课都看了。但大概在期末周之前都不会再学这些了,或将一转文化课日记。今天被C语言老师询问未来意向了,听我想科研说可以进他的组,然后我就找学长学姐了解了情况,感觉水还是太深了,哎哎。感觉只能先像做题区一样把绩点考出来。

浙公网安备 33010602011771号