Introduction to C++ for Financial Engineers-Study Notes 2(Ch8-Ch)

来源:互联网 发布:辉煌中国 知乎 编辑:程序博客网 时间:2024/06/08 07:24

1.p140

设计继承层次时需要小心,否则可能陷入一些隐蔽的错误。

比如我们设计一个Rectangular的父类,然后把Square作为派生类继承Rectangular类

Rectangular基类有三个数据成员分别是Point类型的变量bp,double height,double width,这三个成员的访问级别都是protected。我们为Rectangular类定义了默认构造函数,复制构造函数,以及setHeight函数等等,这时候Square类应该如何设计?

如果我们Square类直接继承Rectangular的数据成员。并且派生类继承了基类的默认构造函数。这时候会破坏consistency。因为此时的Square并不是Rectangualar的特殊情况。

另一种方式是定义Square类的一个Rectangular类型的数据成员并且重写默认构造函数和其他构造函数


2.一些原则

1)avoid deep inheritance hierarchies

2)try to creat base classes with as few member data as possible

注意声明非虚函数是为了让派生类继承函数接口和一份强制性实现。


3.一个例子 Two-factor payoff functions and classes 

这个类的设计与之前的single factor是类似的,比较好的两种做法是

定义一个仅包含一个函数成员而无数据成员的抽象类,并把该函数成员声明为纯虚函数。然后派生类继承该函数的接口并分别定义自己的payoff版本。

如果每个Payoff函数所需要的参数都相同,那么我们可以不定义抽象类和利用继承关系而是直接定义一个具体类,该类的数据成员包含一个函数指针,再分别定义一系列Payoff的函数放在namespace中。更多的想法,可以参考胡扯OO和泛型编程

上面的抽象类可以写为:

class MultiAssetPayoffStrategy{//interface specificationpublic:virtual double payoff(double S1,double S2) const=0;};


4.回顾C++ Primer中Base和Bulk_Item的例子,当派生类定义了自己的复制构造函数和赋值操作符时,派生类要显示调用基类的复制构造函数初始化对象的基类部分;要使用基类的该操作符来初始化对象的基类部分。

Bulk_Item Bulk_Item::operator=(const Bulk_Item &rhs){

Base::operator=(rhs);

};

Bulk_Item::Bulk_Item(const Bulk_Item &rhs){

Base(rhs);

};


5.Casting between types

casting is the process of converting an object pointer of one class to an object pointer of another class. 

必须区分static casting(作用于非多态类),以及dynamic casting(作用于多态类,也就是说其成员函数至少有一个是虚函数),回顾动态绑定的两个条件。以及派生类的实例也是基类的实例这一基本原则。

dynamic_cast<T*>(p);

D1 d1A;Base *base2=&d1A;D1 *d1Cast=dynamic<D1*>(base2);


6.try,throw and catch: Ingredients of the C++ exception mechanism ,p141起

也可以看C++ Primer  p186起

异常就是运行时的不正常,例如运行时耗尽了内存或遇到意外的非法输入。异常存在于程序的正常功能之外,并要求程序立刻处理。

异常检测提供了程序中错误检测与错误处理部分之间的通信。

C++的异常处理包括:

1)throw表达式,错误检测部分使用这种表达式说明遇到了不可处理的错误。例如,

if(!item1.same_isbn(item2)){

throw runtime_error("Data must refer to same ISBN");

}

比如我们设计矩阵乘法时,可以使用throw避免维数不符合条件的矩阵相乘

2)try block,错误处理部分用它来处理异常。 

try块后面是一个或多个catch子句。每个catch子句包括三个部分:关键词catch,圆括号内单个类型或者单个对象的声明——称为异常说明符,以及花括号括起来的语句块。如果try块中的代码产生了异常,而且该异常类型与catch子句匹配,则执行这个catch子句的语句处理这个异常。否则,异常将由外围try块处理或者终止程序。

还可以使用预处理器进行调试,assert

assert(p!=NULL);

STL的异常类exception classes


7.Chapter10 p153 Templates

C++ was the first mainstream object-oriented language to support the generic programming.

模板是泛型编程的基础。在利用泛型编程时,we create classes that do not use a concrete underlying data type for their representation but rather use an unspecified data type that will later be replaced by concrete type.

比如我们要建立一个Lattic类模板

template<class I,class V>class Lattice{//};Lattice<int,double> Mybinomial();Lattice<DatasimDate,Array> myCallableBond();


8.自定义Range类模板

template<typename Type>class Range{private:Type lo;Type hi;Range();public:Range(const Type &low,const Type &high);Range(const Range<Type> &rhs); //copy constructor,仅有一个实参是对该类型对象的引用,要记住类型是Range<Type>virtual ~Range();};

template<typename Type>Range<Type>::Range(const Type& l,const Type &h){if(l<h){lo=l;hi=h;}else{lo=h;hi=l;}}

一定要注意

1).类名是Range<Type>而不是Range,所以返回类型是Range<Type>,形参类型也是Range<Type>

2).template关键词必须用于每一个成员函数。

并且除了code file要包含相应模板的header file外,source file必须include code file,i.e. cpp file而不是header file,这是类模板和函数模板的头文件包含规则与普通类和函数的区别。


9.chapter11 Generic Data Structures and Standard Template Library(STL)

主要的几类数据结构:

lists and vectors (linear sequence data)

maps and multimaps (key-value pairs)

sets and multisets

specially adapted containers such as stacks and queues

上述的容器十分有用,比如我们可以把他们作为类的成员,存放不同类型的数据,利用STL中的容器存放自定义的数据类型

复杂性分析与渐进符号,这部分可看原本的Weblog。下面就是对几种数据类型的介绍


标准库定义了三种顺序容器类型:vector、list 和 deque(是双端队列“double-ended queue”的简写)。它们的差别在于访问元素的方式(顺序迭代器与随机迭代器),以及添加或删除元素相关操作的运行代价。
标准库还提供了三种容器适配器(adaptors)。 实际上,适配器是根据原始的容器类型所提供的操作,通过定义新的操作接口,来适应基础的容器类型。顺序容器适配器包括 stack、queue 和 priority_queue 类型 

适配器就是指使得一种抽象数据类型所支持的操作用另一种抽象数据类型的方式加以实现。


1)Lists

lists是sequential container即顺序容器的一种,包括以下几种具体类型

Singly linked list  

Doubly linked list (supported in STL) 

Circular list  

Skip list

singly linked list的每一个节点包含该节点的值和指向下一个节点的指针。回顾我们使用二叉树统计输入的单词的词频时,每一个二叉树的节点包含两个三个指针成员,其中两个是指向左子树和右子树的指针。

doubly linked list的每一个节点包含两个指针,分别指向successor and to a predecessor,对于double linked list,以下操作只需要常数时间O(1)

Insert an element at the beginning/end of the list  

Delete the first/last element  

Merge two lists  

Insert/delete an element at any position in the list

The following operations have O(n) complexity: 下面操作具有O(n)复杂度

Reverse the order of elements in the list  Delete identical consecutive elements except the first one

也就是说在任意位置插入或删除元素对于doubly linked list都只要常数时间,这就是C++ Primer上说的List支持快速插入和删除,List中的数据不是连续存储的。


2)Stacks and Queues后进先出栈和先进先出队列 

我们可以把栈这一数据结构理解为桌上堆叠的盘子,所以是后进先出。对于栈,我们常常使用以下的几种操作

清楚所有元素,判断栈是否为空,在栈顶添加元素push,去除栈顶元素pop,即 Put an element on the top of the stack (Push)  Take the topmost element from the stack (Pop)

标准库支持三种类型的queue,包括

Double-ended queue (called deque) 

FIFO (First In, First Out) queue  

Priority queue

注意deque和List类似,在头尾插入和删除元素都只要常数时间,但是其他地方则要O(n),因为这时需要移动元素。

由于队列是先进先出的,因此队列的接口的限制比deque更强,It can be modelled by adapting the interface of a deque. 即queue可以是容器deque的适配器


In STL, a priority queue always gives the element with the highest priority. We specify the priority criteria when creating the queue. This could be the greatest or smallest number in the queue, for example. In general, however we characterise the criterion by using a suitable function object for comparison.We have included some examples on the CD to show how this technique works.


10.We define the class P to consist of those decision problems that can be solved on a deterministic sequential machine in the amount of time that is a polynomial function of the size of the input. The classNP (non-deterministic polynomial time) consists of all those decision problems whose positive solutions can be verified in polynomial time given the right information, or equivalently, whose solution can be found in polynomial time on a non-deterministic machine. In complexity theory, the NP-complete problems are the most difficult to solve inNP in the sense that they are the ones most likely not to be in P. An example of an NP-complete problemis the subset sum problem: given a finite set of integers, determine whether any non-empty subset of them sums to zero.

复杂度类P包含所有那些可以由一个确定型图灵机在多项式表达的时间内解决的问题;类NP由所有其肯定解可以在给定正确信息的多项式时间内验证的决定问题组成,或者等效的说,那些解可以在非确定图灵机上在多项式时间内找出的问题的集合。
要解决P = NP问题,NP完全的概念非常有用。不严格的讲,NP完全问题是NP类中“最难”的问题,也就是说它们是最可能不属于P类的。这是因为任何NP中的问题可以在多项式时间内变换成为任何特定NP完全问题的一个特例。例如,旅行商问题的判定问题版本是NP完全的。所以NP中的任何问题的任何特例可以在多项式时间内机械地转换成旅行商问题的一个特例。所以若旅行商问题被证明为在P内,则P = NP!旅行商问题是很多这样的NP完全的问题之一。若任何一个NP完全的问题在P内,则可以推出P = NP。不幸的是,很多重要的问题被证明为NP完全,但没有一个有已知快速的算法。


11.算法的类别

一些重要的算法:

B1
find: locate an element in a container  

count: number of elements whose value equals a target value  

search: search for a subsequence within a sequence

B2
copy: make a copy of a container  

swap: swap the contents of two sequences  

transform: modify a sequence and copy it into another container  

replace: replace a value by another value  

unique: delete identical consecutive elements  

reverse: ‘first will be last and last will be first’  remove: delete an element having a certain value

算法是通过迭代器作用于不同类型的容器的,迭代器是算法和数据结构之间的媒介。

STL中的find算法

template<typename _InputIterator, typename _Tp>    inline _InputIterator    __find(_InputIterator __first, _InputIterator __last,     const _Tp& __val, input_iterator_tag)    {      while (__first != __last && !(*__first == __val))  ++__first;      return __first;    }

12.迭代器 

迭代器可以视为算法和数据结构之间的媒介,回顾STL并没有为顺序容器添加很多操作,即成员函数,而很多操作是通过定义一组算法实现的,这些算法通过迭代器来实现对容器的操作,而与具体的容器类型无关。

迭代器的类型

 Forward and backward iterators  

Sequential and random access iterators  

Iterators that read from, and iterators that write to the data structure

比如vector容器的迭代器就支持快速随机访问,而List容器的迭代器则不支持。还有只读不写迭代器const_iterator

一些C++ Primer书上内容的整理可以参见链接 


13.一个数据结构和算法的简单例子

vector,在尾部插入或删除元素需要常数时间,其他位置需要O(n)时间,长度可变且内存管理是自动的。支持快速随机访问。

书写vector容器的打印操作,使用函数模板和只读不写迭代器

template<class T>

void print(const vector<T> &myVec,){for(vector<T>::const_iterator iter=myVec.begin();iter!=myVec.end();++itercout<<*iter<<" ";cout<<endl;}



14.
对之前的一些问题的回顾:

计算一个数乘以另一个数30*x,需要灵活使用位运算,我们已经知道右移一位>>1,则原来的数变为1/2

更多内容可以参考链接,以及位运算判断奇偶等等 & 01

每个类都定义了一个接口和实现。接口由使用该类的代码需要执行的操作构成。实现一般包括该类所需要的数据。回顾函数的接口和实现,函数的接口可以理解为函数原型。纯虚函数是为了让派生类获得可供继承的接口。回顾纯虚函数,抽象类。

const指针数组的使用, 

char *const color[6] = { "BGC""BCG""CBG""CGB""GBC""GCB" }; //const指针数组,回顾const指针和指向const对象的指针

动态数组的使用见链接第16条


14.QF定义自己的数据类型,Set<T>,AssocArray(V,AI),AssocMatrix(V,AI1,AI2)。其中The last two structures are called associative arrays and matrices, respectively because a noninteger index is associated with a value in the array or matrix structure. We use Set<T> as a ‘control’ in these classes because we wish to ensure that the set(s) of indices remain unique.

灵活地使用map来实现QF中的应用,首先一些基本知识的回顾,关于map类型对象的初始化,赋值和删除操作。pair<K,V>是非抽象数据类型,本质是struct。回顾C++中的struct和struct是两回事。
map<char,double> map1;map1["A"]=1.0;pair<char,double> tmp("B",2.1);//也可以用模板函数make_pair,pair<char,double> tmp=make_pair("B",2,1);map1.insert(tmp);//还可以使用anonymous objectmap1.insert(pair<char,double>("B",2.1));

对map对象进行遍历,类似于顺序容器,只不过访问顺序容器的元素,其元素的顺序是根据存入顺序容器的顺序所决定的,而关联容器则是根据值所决定的。比如我们要打印map类型的对象,可以写为
void print(const map<char,double> &myMap){cout << "Number of elements in map: " << myMap.size() << endl;// Iterating in the mapmap<char, double>::const iterator i = myMap.begin();while (i != myMap.end()){// NOTE THIS SYNTAX: IMPORTANTcout << (*i).first << ", " << ((*i).second) << endl;i++;}cout << endl;}

当然最好写为模板函数,template<typename K,typename V>
上面的first,second,是因为myMap的每个元素我们都定义了两个值。

清除map中的元素,一共有四种方法,其一是利用key,其二是利用迭代器,其三是利用迭代器区间,其四是清楚整个map的元素。
mymap.erase("B");mymap.erase(mymap.begin());mymap.erase(mymap.begin(),mymap.end());maymap.clear()

15.map有很多应用,比如:
Matrix algebra  
Creating spreadsheet-like functionality (ranges, cells and matrices)  
Modelling three-dimensional surfaces 
Modelling volatility surfaces
其中,matrix algebra又包括以下一些问题
Lower-triangular and upper triangular matrices  
Symmetric matrices  
Sparse matrices  
Band matrices that occur when modelling multi-dimensional partial differential equations using the finite difference method  
Property sets (we discuss this topic in Chapter 13)
就是用map来描述一些特殊的矩阵。我们以sparse matrix为例。实际上就是让key代表矩阵的坐标,然后使用map的嵌套来表示二维矩阵。
typedef map<int,double> SparseRow;  //该类型表示矩阵的每一行template<int N> //模板非类型形参来表示矩阵的维数,行数struct SparseMatrix{map<int,SparseRow> data;} ;
const int N = 5;SparseRow current;current.insert(pair<int, double> (0, -2.0)); //使用anonymous object进行初始化current.insert(pair<int, double> (1, 1.0));SparseMatrix<N> sparseMat;sparseMat.data[0] = current;int currentIndex = 0;for (int row = 1; row < N-1; row++){ // Insert 3 elementscurrent.clear();current.insert(pair<int, double> (currentIndex, 1.0));current.insert(pair<int, double> (currentIndex+1, -2.0));current.insert(pair<int, double> (currentIndex+2, 1.0));sparseMat.data[row] = current;currentIndex++;}   //这是对sparsematrix的每一行进行赋值,每一行都是SparseRow类型。current.clear();current.insert(pair<int, double> (N-2, 1.0));current.insert(pair<int, double> (N-1, -2.0));sparseMat.data[N-1] = current;print(sparseMat);

上面这部分内容非常重要,任何使用map来构造的特殊矩阵类型,进行该类型对象的初始化时,我们都可以先定义一个相应的Row类型,再定义一个以Row类型为基础的矩阵类型,并且利用Row类型对象的赋值和erase,clear操作实现初始化。而且上卖弄的内容也揭示了如何使用模板非类型形参来表征矩阵的维数(行数)


使用模板函数来实现对上面定义的SparseMatrix类型对象的打印,非常重要

template <int N>void print(SparseMatrix<N>& sm){SparseRow sr;SparseRow::const iterator it;for (int row = 0; row < N; row++){SparseRow sr = sm.data[row];// Now iterate over rowfor (it = sm.data[row].begin(); it != sm.data[row].end();it++){cout << (*it).second << ", ";}cout << endl;}}

这些代码都可以类似地拓展到别的特殊矩阵上面去。


16.set

STL中的set<T>容器,insert()用于加入元素,remove()用于清除元素

比如

set<string> mySet;mySet.insert("r");mySet.insert("d");set<string> vset(mySet); //copy constructor

我们可以利用set写一些模板函数,比如两个set的并集

template<class T>void UnionSet(const set<T> &s1,const set<T> &s2,set<T> &myunion){set<T>::iterator i=myunion.begin();insert_iterator<set<T>> insertier(myunion,i);set_union(s1.begin(),s1.end(),s2.begin(),s2.end(),insertiter);}

以及书写判断是否是子集或者超集的代码,见p211

重写stl的set,使其更加友好

template<class V> class SetThing{};tempalte<class V> class Set: public SetThing<V>    //回顾SetThing是类的名称,而具体的类型是SetThing<V>,类的派生列表和类的成员函数的书写要注意{private:set<V> s;public:typedef set<V>::iterator iterator;typedef set<V>::const_iterator const_iterator;public:Set();Set(const Set<V> & stlSet); //create a set from STL setSet(const Set<V> & s2); //copy constructor//construct a set from V that has STL-compatible iteratorsSet(const list<V> & con);Set<V> operator=(const Set<V> &s2);virtual ~Set();//Standard operationsfriend Set<V> Intersection(const Set<V> &s1,const Set<V> &s2)friend Set<V> Union(const Set<V> &s1,const Set<V> &s2)Set<V> operator + (const Set<V> &s2);friend Set<V> Difference(const Set<V> &s1,const Set<V> &s2)Set<V> operator - (const Set<V> &s2);Set<V> SymmetricDifference(const Set<V> &s1,const Set<V> &s2);Set<V> operator % (const Set<V> &s2);template<class V2>Set<pair<V,V2>> operator * (const Set<V2> & s2);iterator Begin();const_iterator Begin() const;iterator End();const_iterator End() const;//operations on a single setlong Size() const;void Insert(const V& v); void Insert(const set<V>& v); void Remove(const V &v);void Clear();...//Relations between setsbool Subset(const Set<V> &s2) const;bool Superset(const Set<V> &s2) const;bool Intersects(const Set<V> &s2) const;}


17.下面就用之前写的Set类型和Map来定义AssocArray和AssocMatrix类型,

template<class V,class AI=string>class AssocArray{private:map<AI,V> str;Set<AI> keys;public://};

对于AssocMatrix类型,有三个模板类型形参,即row data type,column data type,values data type

template<class V,class AI1=string,class AI2=string>class AssocMatrix{};

回顾用map类型来实现matrix algebra。


18.Data structures for financial engineering application ch13

在金融工程中有几种常用的数据结构,包括

1)Lattice and other related structures(for example,implied trees)

2)Properties,property sets and flexible "member data" for financial instruments

3)vectors and matrix

这些数据结构将应用于求解线性方程,数据插值,求解收益率曲线


我们在定义类的时候经常将类的数据成员私有化,这样只有类的成员可以访问这些数据成员,而类的使用者则不能。也就是说这些hard-coded member data remain a compile time phenomenon, it is not possible to query an object for its member data at run-time. 我们也不能在运行时添加member data.

对此,我们可以使用Property Pattern,使得我们能够在运行的时候让对象访问其状态the ability of objects to query their own state at run time.

回顾struct可以利用初值表进行初始化,表达类似于数组。

我们在class的公共访问区域定义一个property,这样类的用户就可以直接获得这些Property.

书P222给出了一个Property Pattern的例子,Deep copy以及Visitor Pattern


19.Property Sets

现实问题:对于像期权这样的金融资产,包含一系列的Property,这些property都可以看成key-value pair,所以我们定义一种结构描述a list of key-value pairs,称之为simple property set.,该数据类型包含三个成员,Set<T>表示所有的property的键,然后nam表示property set的名字。

template<class N=string,class V=double>:public PropertyThing<N,V>{private:N nam;map<N,V> s1;Set<N> keys;public:};


20.Lattice

三叉树和二叉树是常见的数据结构,我们要写recombining trees。lattice node是泛型的,我们可以在节点存放不同类型的对象。这部分内容见书。


21. 设计模式(重点 )

首先,我们



0 0
原创粉丝点击