启发式搜索和强化学习

启发式搜索和强化学习 The Pac-Man Projects 是 UC Berkeley CS 188 的课程项目,本文以该项目为例介绍启发式搜索和强化学习。 1 盲目搜索 盲目搜索(Blind Search)指不利用任何额外信息(输入数据,或辅助函数),只依赖于算法本身的搜索,例如 BFS,DFS,Dijkstra 等; DFS The Pac-Man Projects 已经实现了吃豆人游戏的后台逻辑和图形渲染框架,我们只需要在 search.py 文件中实现具体的搜索算法,并根据搜索算法生成寻路路径,即可让吃豆人移动,先来实现一个简单的 DFS: def DepthFirstSearch(problem): from util import Stack open_list = Stack() visited = [] open_list.push((problem.getStartState(), [])) while not open_list.isEmpty(): current_node, path = open_list.pop() if problem.isGoalState(current_node): return path if current_node in visited: continue visited.append(current_node) for next_node, action, cost in problem.getSuccessors(current_node): if next_node not in visited: open_list.push((next_node, path + [action])) dfs = DepthFirstSearch 在吃豆人游戏的框架下,为寻路函数传入的 problem 参数可以理解为一个 class SearchProblem 类型的抽象基类,实际的问题有 PositionSearchProblem(找到单个终点),FoodSearchProblem(找到所有食物),CapsuleSearchProblem(找到增益药丸和所有食物)等,这些子类都需要实现以下函数: ...

December 10, 2020 · 9 min

C++ 闭包和匿名函数

C++ 闭包和匿名函数 本文主要介绍了 C++ 中闭包和仿函数,以及匿名函数相关的概念。 1 闭包和仿函数 闭包(Closure)可以被理解为一个附带数据的操作,WikiPedia 对闭包的定义是 “In programming languages, a closure, also lexical closure or function closure, is a technique for implementing lexically scoped name binding in a language with first-class functions.",其中有两层含义: 词法作用域(lexically scoped)的名字绑定(name binding):在词法作用域(C++ 的词法作用域是静态绑定的,包括块、函数、类、命名空间、全局作用域等)中,变量名与其词法上下文的标识符相关联,而独立于运行时的调用栈; 函数被当作头等公民(first-class citizen):在运行时可以构造一个函数对象并将其作为参数传递给其他函数; 显然 C++ 98 并不符合这两点定义,因此 C++ 98 中并没有严格意义上的闭包,但我们可以用仿函数(Functor)来模拟闭包的行为;仿函数即一个重载了小括号操作符的类,这个类拥有与函数相近的行为方式,它拥有自己的私有成员变量,例如: class Adder { public: int operator()(int num) { sum += num; return sum; } Adder() : sum(0) {} Adder(int num) : sum(num) {} private: int sum; }; int main() { Adder adder(0); cout << adder(1) << endl; cout << adder(2) << endl; cout << adder(3) << endl; } $ g++ -std=c++98 -o adder adder.cpp $ ./adder 1 3 6 相比之下 golang 中真正的闭包显得简洁很多: ...

November 14, 2020 · 5 min

负载均衡和一致性哈希

负载均衡和一致性哈希 反向代理 reverse proxy 是指以代理服务器来接收由客户端发送来的请求,并通过一定的策略将其转变发给实际处理请求的后端服务器;主要应用于负载均衡、动态缓存、安全认证、内网穿透、SSL 加密等;而负载均衡 load balancing 是指在多个 slot(槽,一般是某种计算资源)中分配负载,以优化资源利用率和避免单点故障问题的方法,是高可用性分布式系统的必备中间件;常用的开源 load balancer 有 nginx,LVS,Haproxy 等;负载均衡可以视为反向代理的一种应用,负载均衡的方法大致可以分为传统负载均衡算法和哈希算法两种,本文简单地总结了这些算法的原理。 1 传统负载均衡算法 随机 random:将 key 随机分配到某一个 slot 上,根据概率论可知,吞吐量越大,随机算法的效果越好; 加权随机 weighted random:为每一个 slot 分配一个权重,在随机的时候考虑权重的影响;可以通过在所有 slot 的权重总和中随机出一个数字 k,找到 k 所在的 slot 位置来实现; 轮询 round robin:按顺序依次将 key 分配给每一个 slot; 加权轮询 weighted round robin:为每一个 slot 分配一个权重,在按序分配时为权重更高的 slot 分配更多的 key; 平滑加权轮询 smooth weighted round robin:一种能够均匀地分散调度序列的加权轮询方法,分为以下几个步骤: 选出当前权重最高的 slot,将 key 分配给它; 将选出的 slot 的权重数值减去其初始权重; 将所有 slot 的权重数值都加上它们的原始权重; 重复以上步骤; 最少连接数 least connections:将 key 分配给当前具有最少连接数量的 slot; 2 Mod-N 哈希 在有些场景下,传统负载均衡算法无法满足我们的需求,例如: ...

October 25, 2020 · 4 min

C++ 并发入门:以 LeetCode 1114 为例

C++ 并发入门:以 LeetCode 1114 为例 题目 直接做题:1114 按序打印 解法 1. std::mutex 如果你对 c++ 11 略为熟悉的话,应该能够想到用 std::mutex 来解这道题,在函数构造时(主线程)对 std::mutex 进行 lock,然后在各个线程调用的函数中依次对 std::mutex 对象进行 unlock: class Foo { mutex mtx1, mtx2; public: Foo() { mtx1.lock(), mtx2.lock(); } void first(function<void()> printFirst) { printFirst(); mtx1.unlock(); } void second(function<void()> printSecond) { mtx1.lock(); printSecond(); mtx1.unlock(); mtx2.unlock(); } void third(function<void()> printThird) { mtx2.lock(); printThird(); mtx2.unlock(); } }; Mutex 即 mutual exclusion,是用来防止多个线程同时访问共享资源对象的机制,在同一时间只有一个线程可以拥有一个 mutex 对象,其他线程调用 std::mutex::lock 函数时会阻塞直到其获取锁资源。 ...

September 30, 2020 · 3 min

Effective C++ notes

Effective C++ 笔记 0 导言 1 构造函数 default 构造函数:可被调用而不带任何实参的构造函数,这样的构造函数要么没有参数,要么每个参数都带有默认值,例如 class Bar { public: // explicit Bar(); // 是 default 构造函数 // explicit Bar(int x = 0) // 不是 default 构造函数 explicit Bar(int x = 0, bool b = true); // 是 default 构造函数 private: int x; bool b; }; explicit 关键字:阻止执行隐式类型转换,其优点是禁止了编译器执行非预期的类型转换,例如 void Foo(Bar obj); // Foo 函数的参数是一个类型为 Bar 的对象 Bar obj_1; // 构造一个 Bar 类型的对象 Foo (obj_1); // 没问题,传递一个 Bar 类型的对象给 Foo 函数 Foo (Bar()); // 没问题,构造一个 Bar 类型的对象,并传递给 Foo 函数 Foo (2); // 如果 Bar 的构造函数没有被声明为 explicit,那么会调用 Bar 的构造函数构造一个成员变量 x = 2 的对象,也就是说发生了隐式类型转换;如果其构造函数被声明为 explicit,那么就不会构造出 Bar 类型的对象 copy 构造函数:用同类型的对象初始化新的对象,它定义了一个对象如何 pass by reference。 ...

September 24, 2020 · 6 min