编程原本(1~3)读书笔记

本书是来自STL之父的大作,作者的观念是编程源于数学,没有好的数学基础,只能玩玩程序的小技巧,很难写出STL这样伟大的程序,书的英文名称叫做elements of programming, elements 其实就是事物背后的本质的意思,发人深省。


第一章: 基础

本章作者介绍了一个数学系统,理解这个系统才获取了看懂这本书的钥匙。

过程(Procedure): 改变对象状态的一组指令序列,通俗的来讲就是一个函数,比如比较相等不相等,析构赋值等对对象操作的函数都叫过程。

规范类型:相等检查,赋值,析构,默认构造,拷贝,全序判断,基础类型。这个和设计C++的class的概念不谋而合。

规范过程(Regular):就是保证输入输出的一致性的过程,理解这个概念要从它的反面了解,比如返回对象的指针就是一个非规范类型,因为相同的对象可能返回不同的指针,返回当前的时间,返回随机数。

函数过程(functional procedure):是在规范类型的基础上定义的规范过程,有多个参数和一个返回值,输入分成两大类,一类是数值的直接传递,一类是间接传递,比如引用指针。

类型函数:一个类型到另一个从属类型的映射,比如T到T*的映射。


下面介绍一些数学术语

F:过程; 

Arity(F):过程的输入参数个数;

Codomain(F): 这个函数映射表明,F过程的返回的结果的类型;

InputType(F, i): 返回其第i个参数的类型;


下面描述一个一元函数过程:

UnaryFunction(F)=

        FunctionalProcedure(F)

        ^Arity(F)=1

        ^Domain:  UnaryFunction->Regular

            F->InputType(F,0)

下面描述一个同源的函数过程:(同源函数的意思就是输入类型都是同一参数)

HomogenenousFunction(F)=

        FunctionalProcedure(F)

        ^Arity(F) > 0

       ^({i,j,N})(i,j<Arity(F))=>(InputType(F,i)=InputType(F,j))     // {i,j,N} i j 都是自然数(N)

       ^Domain: HomogenenousFunction->Regular 

              F->InputType(F, 0)



第二章 变换和其轨道(Orbit)

这里的轨道指的是一系列持续的变换过程组成的轨道的意思。

Predicate(P)=    // 谓词: 就是返回Bool的函数

        FunctionalProcedure(P)

        ^Codomain(P) = bool

HomogeneousPredicate(P)=  // 同源谓词

        Predicate(P)

        ^HomogeneousFunction(P)


运算是一种同源函数,这是很显然的定义

Operation(Op)=

        HomogeneousFunction(Op)

        ^Codomain(Op) = Domain(Op) // 运算的值域等于定义域


Transformation(F) = // 变换,一种值域等于定义域的一元运算

        Operation(F)

        ^UnaryFunction(F)

        ^DistanceType:Transformation->Interger  // Distancetype 就是变换的作用域


变换的轨道就是从一个元素出发,反复的使用同一个变换所能达到所有的点的集合。

若x=f^n (x), 就是说x在f下面是环路的(cyclic),如果x不在f的定义域之内,那么x就是f的中止点(terminal).

变换的轨道有4种:1. 无穷   2. 中止  3. 环路   4.P 型(勺子形状的轨道)

碰撞点(快慢车):用来测试轨道的大致形状,y=f^n (x)=f^2n+1 (x)

 

第三章  可结合的运算

二元运算

BinaryOperation(Op)=

         Operation(Op)

        ^Arity(Op)=2

很多二元元算都是可结合的,比如加法乘法之类的

下面利用可结合性来优化幂的运算,下面的算法可以讲幂的运算提高到logN,注意这里不是2倍

a^n=a   (n=1)

a^n=(a^2)^(n/2) (n 是偶数)

a^n=(a^2)^(n/2)a (n是奇数,这里的n/2要向上取整)

template<typename I, typename Op>
     requries(Integer(I)&&BinaryOperation(Op))
Domain(Op) pow_0(Domain(Op), I n, Op op){
    if(n==I(1)) return a;
    if(n%I(2) == I(0)) return pow_0(op(a,a), n/I(2),op);
    return op(pow_0(op(a,a),n/I(2),op),a); 
}
优化:除去递归,书中详细介绍了如何一步步的分析去除递归,这个方法很有用,但是我现在这里指写一下最后的优化结果。

template<typename I, typename Op>
    requires(Integer(I)&&BinaryOperation(Op))
Domain(Op) power_accumulate_positive(Domain(Op) r, Domain(Op) a, I n, Op op)
{
    while(true){
        if(odd(n)){
            r=op(r,a);
            if(one(n)) return r;
        }
        a = op(a,a);
        n = half_nonnegative(n);
    }
}

template<typename I, typename Op>
    requires(Integer(I)&&BinaryOperation(Op))
Domain(Op) pow_accumulate(Domain(Op) r, Domain(Op) a, I n, Op op){
    if(zero())  return r;
    return power_accumulate_positive(r, a, n, op);
}

template<typename I, typename Op>
    require(Integer(I)&&BinaryOperation(Op))
Domain(Op) power(Domain(Op) a, I n , Op op){
    while(even(n)){
        a=op(a,a);
        n=half_nonnegative(n);
    }
    n=half_nonnegative(n);
    if(zero(n))  return a;
    return power_accumulate_positive(a, op(a,a),n,op);
}
template<typename I, typename Op>
    requires(Integer(I)&&BinaryOperation(Op))
Domain(Op) power(Domain(Op) a, I n, Op op, Domain(Op) id){
    if(zero(n)) return id;
    return power(a,n,op);
}

上面把最基本的类型都函数化了,因为这部分可能使用一些更好的办法来优化比如位运算。



线性递归

线性递归就是递归的规则是线性的。

下面讲一个算Fiduccia的例子,算法很先进,理解起来有难度。

F={{1,1},{1,0}};   F^n={{fn+1, fn},{fn, fn-1}}

template<typename I>
    requires(Integer(I))
pair<I, I>fibonacci_matrix_multiply(const pair<I, I>&x, √const pair<I, I>&y ){
	return pair<I, I>(x.m0*(y.m1+y.m0)+x.m1+y.m0,   x.m0*y.m0+x.m1*y.m1);
}
template<typename I>
     requires(Integer(I))
I fibonacci(I n){
	if(n==I(0)) return I(0);
	return power(pair<I, I>(I(1), I(0)), n,fibonacci_matrix_multiply<I> ).m0;
}





你可能感兴趣的:(编程原本(1~3)读书笔记)