Categories
不学无术

Problem Solving (CS271)

Problem solving works when:

  • The domain must be fully observable: we must be able to see what initial state we start out with;
    全局都是可见的
  • The domain must be known: we have to know the set of available actions to us;
    知晓可进行的操作
  • The domain must be discrete: there must be a finite number of actions to be chose from;
    可进行的操作是有限多的
  • The domain must be deterministic: we have to know the result of taking an action;
    进行一项操作带来的结果是可知的
Categories
不学无术

RFC1928 SOCKS5协议 – 部分内容翻译

Reference: https://www.ietf.org/rfc/rfc1928.txt
只是部分章节的翻译


3. TCP客户端的连接流程

当某一基于TCP的客户端想要经由防火墙(取决于具体实现)与远端对象建立连接时,它必须首先开启与SOCKS服务器上对应端口的TCP连接。通常情况下SOCKS服务器监听的TCP端口号是1080. 若连接请求成功,客户端将进入授权模式协商阶段,随后使用所选的模式进行授权,继而开始连接。SOCKS服务器对请求进行评估,继而建立指定的连接或拒绝连接。
除非另有说明,下文数据报中提及的十进制数字均为对应字段的字节长度。当某个字节必须是一个具体数值时,以X’hh’来表示这一字段的具体值。当使用’Variable'(可变长)时,表示这一字段拥有可变长度的内容,具体长度通过一个关联的长度字段表示,或根据某钟具体的数据类型字段决定。
客户端连接至服务器并发送一个标识符/方法选择信息:

                   +----+----------+----------+
                   |VER | NMETHODS | METHODS  |
                   +----+----------+----------+
                   | 1  |    1     | 1 to 255 |
                   +----+----------+----------+

其中,字段VER设置为X’05’以表明此协议的版本。NMETHODS字段包含了METHODS字段中所填鉴权方法(method)的个数,以字节为长度单位——METHODS中的一个方法标识符占一个字节。
服务器从METHODS给定的方法中选择一种,回传一个方法选择消息:

                         +----+--------+
                         |VER | METHOD |
                         +----+--------+
                         | 1  |   1    |
                         +----+--------+

如果选择的方法值为X’FF’,则表明服务器不接受客户端能提供的任一种鉴权方法,客户端必须在收到此消息后断开连接。
METHOD中可以定义的方法为:

  • X’00’ 无需鉴权方法
  • X’01’ GSSAPI
  • X’02’ 用户名/密码
  • X’03’ 至 X’7F’ IANA指定
  • X’80’ 至 X’FE’ 私有方法保留
  • X’FF’ 没有可接受的方法

随后,服务器与客户端进入协议特定的子协商(sub-negotiation)阶段。
协议特定的子协商过程在描述于不同备忘录中。
本协议新方法的开发人员必须向IANA申请一个新的METHOD号……(省略两段合规相关的话)

4. 请求(Requests)

一旦完成了协议特定的子协商过程,客户端便发送请求细节。如果方法的协商包括了完整性检查或保密相关的封装,那么这些请求必须以指定方式进行相同的封装。
SOCKS的请求,其格式如下:

        +----+-----+-------+------+----------+----------+
        |VER | CMD |  RSV  | ATYP | DST.ADDR | DST.PORT |
        +----+-----+-------+------+----------+----------+
        | 1  |  1  | X'00' |  1   | Variable |    2     |
        +----+-----+-------+------+----------+----------+

其中:

  • VER: 协议版本号:X’05’
  • CMD
    • CONNECT X’01’
    • BIND X’02’
    • UDP ASSOCIATE X’03’
  • RSV 保留字段
  • ATYP 地址类型
    • IP V4 地址: X’01’
    • 域名: X’03’
    • IP V6 地址: X’04’
  • DST.ADDR 期望的目标地址
  • DST.PORT 期望的目标端口号,以网络字节序表示

5. 地址(Addressing)

一个地址字段(DST.ADDR, BND.ADDR)的具体类型由ATYP字段的值来表示:

  • X’01’ 为4个字节表示的IPV4地址
  • X’03’ 表示一个域名,地址字段中的第一个字节表示后续字符的总长度(字节数),地址字段不包含NUL结束符
  • X’04’ 为16字节表示的IPV6地址

6. 回复(Replies)

一旦SOCKS客户端与服务器建立连接,SOCKS请求即被发送,随即进行鉴权方式的协商。服务器端对请求进行识别,并以下属形式回复:

        +----+-----+-------+------+----------+----------+
        |VER | REP |  RSV  | ATYP | BND.ADDR | BND.PORT |
        +----+-----+-------+------+----------+----------+
        | 1  |  1  | X'00' |  1   | Variable |    2     |
        +----+-----+-------+------+----------+----------+

其中:

  • VER:协议版本号: X’05’
  • REP:回复字段:
    • X’00’ 成功
    • X’01’ 一般SOCKS服务器错误
    • X’02’ 规则集(ruleset)不允许该连接
    • X’03’ 网络不可用
    • X’04’ 主机不可达
    • X’05’ 连接被拒绝
    • X’06’ TTL超时
    • X’07’ 不支持该命令
    • X’08’ 不支持该地址类型
    • X’09’ 至 X’FF’ 尚未指定
  • RSV:保留字段
  • ATYP:后续地址的类型
    • IPV4地址: X’01’
    • 域名:X’03’
    • IPV6地址: X’04’
  • BND.ADDR: 服务器绑定地址
  • BND.PORT:服务器绑定端口号,网络字节序表示

CONNECT

对CONNECT(连接)请求的回复中,BND.PORT包含服务器指派的用于连接远程主机的端口号,BND.ADDR为对应的IP地址。指派的BND.ADDR通常与客户端请求的目标地址不同,因为目标主机可能拥有多组地址(multi-homed)。预期情况下SOCKS服务器使用DST.ADDR与DST.PORT,连同客户端的地址、端口号,一起确定一个CONNECT请求。

BIND

BIND(绑定)请求通常用在那些客户端需要接受(accept)服务器连接的协议中。FTP便是一个令人熟知的例子,它主要使用客户端->服务器连接来发送命令、报告状态,但同时也会用服务器->客户端连接来传输数据(e.g. LS, GET, PUT)。
预期情况下,客户端一侧的应用协议仅在使用过CONNECT请求完成一个主连接的情况下,再使用BIND请求请求一个二次连接。预期情况下SOCKS服务器使用DST.ADDR与DST.PORT来确定一个BIND请求。
在一次BIND操作过程中,SOCKS服务器将给客户端回复两条消息。第一条将会在服务器建立连接并绑定一个新的socket口时发送。BND.PORT字段包含了一个端口号,该端口号是SOCKS服务器指派的用于接收传入连接的。BND.ADDR包含了对应的IP地址。通常情况下,客户端根据这些信息来通知(经由主连接)应用程序服务器(目标主机)这一地址的存在。第二次应答只发生与预期的传入连接成功或失败发生时。在二次应答中,BND.PORT及BND.ADDR字段包含了目标主机(connecting host)的地址和端口号。

UDP ASSOCIATE

UDP ASSOCIATE请求用作UDP中继过程中建立关联以处理UDP数据报文。DST.ADDR和DST.PORT字段包含了客户端期望关联的,用于发送UDP数据报的目标地址及端口号。服务器或能使用这一信息来限制连接请求。如果客户端在UDP ASSOCIATE阶段尚不能拥有这些信息,则它必须使用全零的端口号及地址。
当UDP ASSOCIATE请求到达的TCP连接终止时,UDP关联终止。
在对UDP ASSOCIATE请求的应答中,BND.PORT和BND.ADDR字段表明了客户端必须发送UDP中继请求的端口号/地址。

应答处理

当应答失败时(REP值不等于X’00’),SOCKS服务器必须在发送应答消息后立即关闭TCP连接。这一操作必须在检测到异常状态后不超过10秒时执行。
如果应答码标识为成功(REP==X’00’),并且请求既不是BIND也不是CONNECT,则客户端可以开始传输数据。如果协商的传输方式包含对于数据完整性,鉴权或保密性的封装要求,其传输的数据也应按此要求封装。类似操作也许是实施于反向的传输过程中。

7. UDP客户端的操作

一个UDP客户端必须将自己的数据报发送至指定的UDP中继服务器,其端口号是在UDP ASSOCIATE应答中由BND.PORT字段指定的端口号。其数据报中鉴权、一致性及加密性封装必须与协商时表明的相同。每个UDP数据报包含一个UDP请求头:

      +----+------+------+----------+----------+----------+
      |RSV | FRAG | ATYP | DST.ADDR | DST.PORT |   DATA   |
      +----+------+------+----------+----------+----------+
      | 2  |  1   |  1   | Variable |    2     | Variable |
      +----+------+------+----------+----------+----------+

该请求头中包含的字段为:

  • RSV:保留字段 X’0000′
  • FRAG:当前分段号
  • ATYP:下述地址类型:
    • IPV4地址: X’01’
    • 域名:X’03’
    • IPV6地址:X’04’
  • DST.ADDR:期望的目标地址
  • DST.PORT:期望的目标端口号
  • DATA:用户数据

当一个UDP中继服务器决定中继一个UDP数据报时,它静默地执行上述操作,不通知任何客户端。类似的,它会丢弃它无法中继的数据报。当一个UDP中继服务器收到远程主机传来的应答数据报时,它必须使用上述报头格式封装数据报,若有必要同时将依据之前协商的鉴权方法再封装。
UDP中继服务器必须从SOCKS服务器中获取客户端期望发送数据报的IP地址,客户端用该地址加上UDP ASSOCIATE应答中提及的BND.PORT来发送数据报。它必须丢弃任何在关联之外的,从源IP地址传输过来的数据报。
FRAG字段表明了这个数据报是不是数据报段中的一部分。如果分段实施,则最高阶的位(bit)表明是否分段结束,值X’00’表明这是个独立的数据报。在1与127之间的数值表明这个数据报在分段中的顺序。每个接收者必须实现一个与之关联的REASSEMBLY QUEUE(重组队列)与REASSEMBLY TIMER(重组计时器)。重组队列必须在计时器超时时,亦或是传入数据报FRAG字段值小于当前最大字段值时,丢弃那些尚未组合成功的数据报,并且重新初始化。重组计时器必须设置不超过5秒的超时值。一般建议,只要条件允许,不启用分段。
分段的实施时可选的;一个不允许分段操作的实现,它必须丢弃任何FRAG值不为X’00’的任何数据报。
一个SOCKS-可感知的UDP程序接口必须报告一个可用的UDP数据报缓存长度,且其必须小于操作系统允许的空间:

  • 若ATYP==X’01’  < 10 + method_dependent 字节
  • 若ATYP==X’03’ : < 262 + method_dependent 字节
  • 若ATYP==X’04’ : < 20 +  method_dependent 字节

 

Categories
不学无术

Basic Risks on Options 期权的基本风险类型

书里看到的,摘记一下。

  • Delta (Directional) Risk. Underlying的价格向某一侧移动的风险,主要做法是构造delta中性的position;
  • Gamma (Curvature) Risk. Underlying价格发生大幅波动的风险,这种风险并非是delta那种方向性的。承受这种风险的position是Gamma为负数的那些期权,因为由定义可以推出来,+Gamma的那些期权,在价格发生波动时其价值反而会增加;
  • Theta (Time Decay) Risk. 与Gamma风险是反向的一对,能通过Gamma获得价值的期权,他们的价值随着时间而逝去…
  • Vega (Volatility) Risk. 我们给模型输入的那个volatility可能是错的,错误的输入将得到错误的模型(概率分布),产生错误的结果;或者,期权市场自身的变化导致的隐含波动率的改变;
  • Rho (Interest-Rate) Risk. 期权有效期内利率的变化对其价值的影响。Rho>0说明利率上升能增加期权的价值,反之亦然。但通常而言,这个风险应当是在前面几种风险之后才考虑的;

 

Categories
不学无术

Volatility Spreads – Two General Guidance

Here I excerpt two useful sections in Chapter 12 of the book Option Volatility & Pricing.

Choosing an Appropriate Strategy

If implied volatility is low, such that options generally appear underpriced, look for spreads with a positive vega. If implied volatility is high, such that options generally appear overpriced, look for spreads with a negative vega.

 

Long calendar spreads are likely to be profitable when implied volatility is low but is expected to rise; short calendar spreads are likely to be profitable when implied volatility is high but is expected to fall.

Categories
不学无术

Risk Measurement I (the Greeks,希腊值)

希腊值(The Greeks)是期权或衍生品定价用的比较重要的指标,简单来说有了希腊值以后可以拿到某个产品的理论价格(theo),有了理论价格我们就有了交易的方向了。
目前常用的希腊值有下面几种:

  • Delta (Δ):measure of an option’s risk with respect to the direction of movement in the underlying contract. 通俗的讲就是标的物(underlying)的价值变化1个单位的话,期权的价格相应地也会变化Δ,当然这里的“变化”是有方向的,这个取决于期权的性质(call, put)以及当时的其他情况;如果以underlying的价值为横轴,option的价值为纵轴,那delta值就是所对应曲线的斜率。还有另一种解释,如果不管Δ的正负号的话,即|Δ|其实是约等于所对应期权最后成为实值期权(in-the-money)的概率

    有一个种基础的对冲方法就叫Δ对冲(delta hedging)。
  • Gamma(Γ): 是Δ的变化与underlying价格的变化率,或者说underlying价格变化1个单位,Δ会变化多少。当我们买入期权时,就构造了+Γ值;反之卖出期权时-Γ。如果Δ看做“速度”的话,那Γ就是“加速度”了。
  • Theta(Θ): 反映时间流逝带来的期权价格变化。这个值基本是负数。
  • Vega(Κ):因为没有对应的希腊字母,所以用Kappa代替了。这个是波动率变化对期权理论价值的影响。举个例子,如果一个期权Κ=0.15,那么波动率每±1%,期权的理论价相应地会±0.15。
  • Rho(Ρ): 期权的理论价对利率变动的敏感程度。 这个值对于定价模型的影响是这几个希腊值里最小的,一般不用强调或者直接忽略这个变化。

下面总结一下各个希腊值头寸(position)与期权买卖的关系,方便起见直接用英文了:

-------------------------------------------------------------------
If you are...   |  Δ pos. |  Γ pos. |  Θ pos. |  Κ pos. |  Ρ pos.
-------------------------------------------------------------------
Long the under- |   +     |    0    |    0    |    0    |    0
lying contract  |         |         |         |         |
-------------------------------------------------------------------
Short the under |   +     |    0    |    0    |    0    |    0
lying contract  |         |         |         |         |
-------------------------------------------------------------------
Long Calls      |   +     |    +    |    -    |    +    | + S
                |         |         |         |         | - F
-------------------------------------------------------------------
Short Calls     |   -     |    -    |    +    |    -    | - S
                |         |         |         |         | + F
-------------------------------------------------------------------
Long Puts       |   -     |    +    |    -    |    +    | - S
                |         |         |         |         | - F
-------------------------------------------------------------------
Short Puts      |   +     |    -    |    +    |    -    | - S
                |         |         |         |         | + F
-------------------------------------------------------------------
-/+ S = -/+ on Stocks
-/+ F = -/+ on Futures

很显然的是,Long和Short是一对,符号全是反向的。
本文内容来自一本很不错的书:《Option Volatility and Pricing (Second Edition)》

Categories
不学无术

C++堆内存相关问题小结

C++中遇到的堆内存相关问题,基本可以归为下述三类:

  • 野指针
    • 一些内存单元已被释放,之前指向它的指针还在被使用。这会导致无法预测的运行时错误。
  • 重复释放
    • 试图释放已经被释放过的内存单元,或者释放已被重新分配的内存单元。这会导致运行时错误。
  • 内存泄漏
    • 不再需要的内存单元一直驻留没有释放。这会导致内存占用剧增,直至堆内存无法分配,运行时错误。

 
 
摘自《深入理解C++11》

Categories
不学无术

C++11 继承构造函数、委派构造函数

C++11标准中引入了两种构造函数的活用方法,可以适当地减轻一些情景下的码字负担。

1.继承构造函数

以往情况下,在派生类中调用基类的构造函数,一般需要在派生类中显式调用:

struct A
{
    A(int i) {}
};
struct B
{
    B(int i) : A(i) {}
};

当构造函数的花式(签名)很多的时候,继承类写起来就比较辛苦了。
在C++11标准中,可以通过using声明来简化这种操作(using声明在以前的标准中已经可以用在非构造函数里)

struct A
{
    A(int i) {}
    A(double d, int i) {}
    A(float f, double d, int i) {}
    //...
};
struct B
{
    using A::A;    //继承构造函数
    //...
};

需要注意的是,采用这种using声明有两个规矩:

  • 如果基类的构造函数是private或是派生类是从基类中虚继承的,不能用这种方法;
  • 一旦用了继承构造函数,编译器就不会为派生类生成默认构造函数(Visual Studio报error C2280),除非基类自己没有定义任何构造函数而触发了默认构造函数生成;

2.委派构造函数

委派构造函数常用在减少初始化过程的代码冗余问题,比如下面这个经典例子(我就这么干过):

class Info
{
public:
    Info() { InitRest(); }
    Info(int i) : type(i) { InitRest(); }
    Info(char c) : name(c) { InitRest();}
private:
    void InitRest ()
    {
        // 初始化其他东西
    }
    int type {1};
    char name {'a'};
    //...
};

使用一般方法,我们无法在自己构造函数里又调用自己的构造函数(突然想起了python的import this,哈哈),纵使函数签名不同也会报编译错误。
C++11中,可以这么写:

class Info
{
public:
    Info() { InitRest(); }
    Info(int i) : Info() { type = i; }
    Info(char c) : Info() { name = c; }
private:
    void InitRest ()
    {
        // 初始化其他东西
    }
    int type {1};
    char name {'a'};
    //...
};

但是要注意,委派构造函数不能和初始化列表并用,不然依旧编译错误。
比如我们不能搞Info(int i) : Info(), type(i) { } 。
不过有解决办法,比如可以声明一个私有的构造函数,它包括“完整”的参数输入,然后公有的其他构造函数调用它:

class Info
{
public:
    Info() : Info(1, 'a') { } // 或采用链状委派: Info(1) 或 Info('a')
    Info(int i) : Info(i, 'a') { }
    Info(char c) : Info(1, c) { }
private:
    Info(int i, char e) : type(i), name(c) { }
    int type {1};
    char name {'a'};
    //...
};

关于函数执行顺序,目标函数执行总是先于委派构造函数的。

参考文献

《深入理解C++11 (C++11新特性解析与应用)》

Categories
不学无术

C++模板元编程 习题: add_const_ref

要学的东西还有很多啊…
习题2-0. 编写一个一元元函数add_const_ref<T>,如果T是一个引用类型,就返回T,否则返回T const&.

#include "add_const_ref.h"
int main()
{
	int a = 5;
	add_const_ref<int>::type c = a;
	add_const_ref<int&>::type d = a;
	return 0;
}

 

#ifndef ADD_CONST_REF
#define ADD_CONST_REF
template <class T>
struct add_const_ref;
template<class T>
struct add_const_ref
{
	typedef T const& type;
};
template<class T>
struct add_const_ref<T&>
{
	typedef T type;
};
#endif

 

Categories
不学无术

翻译:次梯度以及一阶最优性条件(Subgradient and First-order Optimality Condition)

本文是对文章Basics of Convex Analysis的部分翻译,若本文对您的任何权益造成侵犯,请联系我。
This article is a partial translation of Basics of Convex Analysis, if this infringes any of your rights, please contact me.


次梯度以及一阶最优性条件

若下述不等式成立:
[latex]f(z) \geqslant{f(x)} + <z-x, y>, \forall z \in X.[/latex]
则我们说[latex]y\in X[/latex]是函数[latex]f \in \Gamma _0(X)[/latex]在[latex]x \in X[/latex]点上的一个次梯度,并且属于[latex]f[/latex]在该点(用[latex]\partial f(x)[/latex]表示)的一个次微分。
上述不等式表明函数[latex]f[/latex]的图像(graph)是由不等式右侧定义的超平面所(hyperplane)支撑的。一个次梯度因此也是这众多支持超平面其中一个的“斜率(slope)”。如果该函数在点[latex]x[/latex]处可微,则这样的次梯度有且仅有一个(即标准梯度),与此对应地,也只有一个支持超平面。相对地,如果一个函数在点[latex]x[/latex]处不可谓(比如,在[latex]x[/latex]处有个扭曲)那么就能有无穷多个支持超平面,并且相对应地,在该点的次微分是一个连续的次梯度的集合。
一个典型的例子是绝对值函数[latex]f(x)=|x|[/latex],它在0点是不可导的。但在这个点上,它可以由[latex]l(x)=\alpha x, \alpha \in [-1,1][/latex]组成的所有直线支持。这个集合即该函数在0点的次微分,用[latex]\partial f(0)[/latex]表示。

函数[latex]f(x)=|x|[/latex]的演示(粗线表示)以及其中两个支持直线(虚线表示)。这两条支持直线都有次微分[latex]\partial f(0)[/latex]中的斜率。注意,那条水平线也是支持超平面之一,表明[latex]0 \in \partial f(0)[/latex]。并且因此由一阶条件(下文定义),这个函数在原点有一个极小值。
现在,通过定义非限制问题中的一个最优点[latex]x^{\#}[/latex],必有[latex]f(z) \geqslant{f(x^{\#}) + <z-x, 0>}, \forall z \in x [/latex],并且因此0必须是函数[latex]f[/latex]在[latex]x^{\#}[/latex]点处的一个子梯度。
这就是一阶最优性条件(FOC):
[latex]x^{\#} \in arg min_{x}{f(x)} \Longleftrightarrow 0 \in \partial f(x^{\#})[/latex]
如果我们将次微分看做一个运算符那么,直观地,寻找极小可以看做“逆转”次微分并计算它在点0的值的过程,即[latex]x^{\#}=(\partial f)^{-1} (0)[/latex]。我们稍后再进一步介绍,但这个逆转次微分运算符的思路是非常重要的。
在继续之前,有必要提一下(并且这并不难理解)下述包含关系对于次微分的和是成立的:
[latex]\sum_{i}{\partial f_i} \subseteq \partial \sum_{i}{f_i} [/latex]
对关注的大部分问题而言,上述关系可以强化为相等的关系, 但注意如果[latex]0 \in \sum_{i}{\partial f_i (x^\dagger)}[/latex],则上述包含关系意味着[latex]0 \in \partial \sum_{i}{f_i (x^\dagger)}[/latex],这对于证明[latex]x^\dagger[/latex]是个极小值而言(这正是我们最感兴趣之处)足矣。


 

Categories
不学无术

LeetCode 47. Permutations II

用回溯法完成,跟普通排列问题不同的是,需要排除掉一些重复“路径”。
可以这样做,先把输入数组排个序,这样重复的数就在相邻位置了。随后在回溯探求的过程中,我们要保证相同值的两个数nums[i], nums[j],如果i<j,那遍历只能有一种次序:nums[i]先,nums[j]后。
换句话说,就是判断这种情况是需要提前终止的:两个数nums[i]==nums[j] && i<j,但是nums[i]还没有被遍历。所以找个东西记录一下访问就行了。
时间复杂度是O(N!),空间复杂度是O(N)

class Solution {
public:
    void permSub(vector<int>& nums, vector<int>& sub, vector<vector<int>>& results, vector<bool>& used){
        if(sub.size() == nums.size()){
            results.push_back(sub);
            return;
        } else {
            for(int i=0; i<nums.size(); i++){
                if(used[i])
                    continue;
                if(i>0 && nums[i]==nums[i-1] && !used[i-1])
                    continue;
                sub.push_back(nums[i]);
                used[i] = true;
                permSub(nums, sub, results, used);
                sub.pop_back();
                used[i] = false;
            }
        }
    }
    vector<vector<int>> permuteUnique(vector<int>& nums) {
        sort(nums.begin(), nums.end());
        vector<vector<int>> results;
        vector<bool> used(nums.size(), false);
        vector<int> sub;
        permSub(nums, sub, results, used);
        return results;
    }
};