九天雁翎的博客
如果你想在软件业获得成功,就使用你知道的最强大的语言,用它解决你知道的最难的问题,并且等待竞争对手的经理做出自甘平庸的选择。 -- Paul Graham

增加无限长精确整数(infinite precision integer)给C++标准库的提案 简单翻译稿 N1692

A Proposal to add the Infinite Precision Integer to the

C++ Standard Library N1692 1 July 2004

原作者:M.J. Kronenburg

e-mail: M.Kronenburg@inter.nl.net

 原文链接:http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2004/n1692.pdf

九天雁翎简单翻译稿:

增加无限长精确整数 infinite precision integer)C++标准库的提案**

1 动机:

对整数的需要不再适应于处理器数据长度的增加。比如,int类型的范围在一个32位电脑上是±2^31≌ ±10^9.对于超出这个范围的情况,程序员往往创建一个用unsigned int 和符号位组成的C++类,这个类的数据长度仅仅受可用内存的限制。并且重载了数学操作符。这个类的整数范围是±2^8m,m是以字节(byte)度量的最大可用内存。以现在的内存大小来说,这个范围实际上是无限的。这个类就是一个无限长精确整数,我提议这个类的名字为integer。

目前有很多无限长精确整数的实现存在,以下给出一些总的来说比较好的支持,实现和设计方案:

 

1. Gnu C++ library 中的 Integer 类

(C++, 限制在10^5 位以内 [7]).

 

2. The Gnu Multiple Precision Arithmetic Library

(C with assembler, 无限[6]).

 

3. 我自己设计的Integer class

(C++ with assembler, 无限)

 

在以后,我提到实现1,2,3就是分别指以上几种。

 

2           对标准的影响

作为一个有自己内存管理的独立类,没有对标准产生影响。

 

3           设计结果

有很多设计结果,但是选择通常就是在我在上面列出来的几种中进行。

 

3.1       类结构

这个被提议的C++类将是integer类。实现这个类,使用int的函数和操作符是需要被重载的,并且重载求最大公约数和最小公倍数的函数也是必须的,而且一些类似setbit,clearbit,getbit,highestbit,lowestbit等的位级(bit level)操作也是需要重载的。在实现1里面的Integer类是用包含一个struct的方式表示的。这意味着,每个类函数或者操作符调用的函数都是作用在这个struct上面。实现2是一个在C上的实现。所以要在C++上面使用,需要一个C++的封装。在实现3里,这个Integer类是一个内部没有用struct表示的简单的C++类。这里有一个从string创建对象的构造函数,因此任何数字可以由一个任意长度的string指定。

 

3.2       数据结构

在所有以上我提到的实现中,数据都是一个连在一起的可变长二进制块。一个可以保存-1(小于0),0(零),和1(大于0)值的符号被存储在一个分开的地方。一个可行的方法是把数据放在一个标准库容器vector中,但是像许多integer操作在位级上实现一样,对vector容器实现的依赖是不可接受的。因此,integer不会使用任何标准库容器或者函数。

 

3.3  数据粒度(Data Granularity)

为了操作integer数据,它的粒度必须被定义,即一次操作多宽的数据块。在32位处理器上,实现1用short 16位粒度,因为这意味着所有的移位(shift)和传送(carry)操作可以用32位的int实现,那么就不需要使用汇编。在32位处理器上,其他的实现用32位的粒度,这意味着汇编是不可避免的了。目前我还不清楚,是否64位处理器可以控制64位粒度的integer。

 

3.3       性能

所有的算术计算操作性能在使用32位粒度和汇编时都好很多。对于两个大数的乘法,很多算法存在(N 是数据的十进制或二进制位数(number of decimals or bits):

Algorithm            implementation     order                           decimals               reference

Basecase                     1,2,3              N^2                                      1 − 10^2

Karatsuba                    2,3                N^1.585                              10^2 − 10^3               [1,4]

3-way Toom-Cook        2                  N^1.465                               > 10^3                        [1,4]

16-way Toom-Cook      3                  N^1.239                               > 10^3                        [1]

Sch¨onhage NTT          2                  N logN log logN          > 10^3                        [2,3,4]

Strassen FFT            -                      N log^2 N                    > 10^3                        [1,3,5]

(NTT是数理论上的一种转换,FFT是快速傅利叶变换)。Starassen  FFT算法因为是使用浮点计算法(arithmetic),所以对于非常大的参数它的准确性无法保证[4],因此没有被我提到实现使用,但是他的是最好的。实现1只使用了basecase乘法,因此对于大参数来说它的性能很差。在实现3里面,我发现16-way Toom-Cook算法比Sch¨onhage NTT更快(直到一些我不知道的非常大的参数)。

对于除法和残余递归算法(remainder recursive algorithms)对于大参数有着更好的性能,而且在输入输出流(instream and outstream)来说也是一样的。意味着从二进制转换到十进制也是一样有着好的性能,而且反之亦然。

 

3.4       汇编的使用

在实现1里没有使用汇编,因此可以运行在任何平台上。实现2用了汇编,而且它是一个为了在类Unix平台运行的C语言编译器,它使用的汇编也是。实现3用了汇编,而且它是为了在Borland C++ Bulider 和 它的Turbo汇编编译器使用的。

 

3.5       总结

当的确需要从上述3个存在的实现中选一个的话,以下的情况可能要被考虑。因为实现1仅仅受限于大概10^5位(我不清楚为什么有这样的限制存在),这个实现作为无限制精确整数可能不是那么有用。而且对于很大的整数它的性能不好。实现2可能更多的在类Unix平台被选择,所有它需要的看起来是一个C++的封装。实现3可能更多地在Wintel平台被选择。另一个选项是与商业的代数运算程序开发商合作。

 

4           提案正文

4.1       需求

integer类的需求由包含在提案头文件的界面(interface)提供。这些断言(assertion)和之前之后的情况(pre/post-condition)都是很明显的,对于平均复杂度的需要也提供了,N 是数据的十进制或二进制位数:

class integer
{
private:
    unsigned int *data, *maxdata;
    signed char thesign;
public: // Complexity:
    integer(); // 1
    integer( int ); // 1
    integer( double ); // 1
    integer( const char * ); // < N^2 (see 3.4)
    integer( const string & ); // < N^2 (see 3.4)
    integer( const integer & ); // N
    virtual ~integer(); // 1
    const unsigned int size() const; // 1
    integer &operator=( const integer & ); // N
    integer &negate(); // 1
    integer &abs(); // 1
    integer &operator++(); // 1
    integer &operator\--(); // 1
    const integer operator++( int ); // N
    const integer operator\--( int ); // N
    integer &operator|=( const integer & ); // N
    integer &operator&=( const integer & ); // N
    integer &operator^=( const integer & ); // N
    integer &operator<<=( unsigned int ); // N
    integer &operator>>=( unsigned int ); // N
    integer &operator+=( const integer & ); // N
    integer &operator-=( const integer & ); // N
    integer &operator*=( const integer & ); // < N^2 (see 3.4)
    integer &operator/=( const integer & ); // < N^2 (see 3.4)
    integer &operator%=( const integer & ); // < N^2 (see 3.4)
    const integer operator-() const; // N
    const integer operator<<( unsigned int ) const; // N
    const integer operator>>( unsigned int ) const; // N
};

const bool operator==( const integer &, const integer & ); // N
const bool operator!=( const integer &, const integer & ); // N
const bool operator>( const integer &, const integer & ); // N
const bool operator>=( const integer &, const integer & ); // N
const bool operator<( const integer &, const integer & ); // N
const bool operator<=( const integer &, const integer & ); // N
const integer operator|( const integer &, const integer & ); // N
const integer operator&( const integer &, const integer & ); // N
const integer operator^( const integer &, const integer & ); // N
const integer operator+( const integer &, const integer & ); // N
const integer operator-( const integer &, const integer & ); // N
const integer operator*( const integer &, const integer & ); // < N^2 see 3.4)
const integer operator/( const integer &, const integer & ); // < N^2 see 3.4)
const integer operator%( const integer &, const integer & ); // < N^2 see 3.4)

const integer gcd( const integer &, const integer & ); // ?
const integer lcm( const integer &, const integer & ); // ?
ostream & operator<<( ostream &, const integer & ); // < N^2 (see 3.4)
istream & operator>>( istream &, integer & ); // < N^2 (see 3.4)
const int sign( const integer & ); // 1
const bool even( const integer & ); // 1
const bool odd( const integer & ); // 1
const bool getbit( const integer &, unsigned int ); // 1
void setbit( integer &, unsigned int ); // 1
void clearbit( integer &, unsigned int ); // 1
const unsigned int lowestbit( const integer & ); // 1
const unsigned int highestbit( const integer & ); // 1
const integer abs( const integer & ); // N
const integer sqr( const integer & ); // < N^2 (see 3.4)
const integer pow( const integer &, const integer & ); // ?
const integer factorial( const integer & ); // ?
    // floor of the square root, like int sqrt( int )
const integer sqrt( const integer & ); // ?
    // random integer >= first and < second argument
const integer random( const integer &, const integer & ); // ?
const int toint( const integer & ); // 1
const double todouble( const integer & ); // 1

为了错误控制,可能需要创建一个独立的exception类:

class integer_exception : public exception
{ public:
    enum type_of_error {
        error_unknown, error_overflow,
        error_divbyzero, error_memalloc, ...
    };
    integer_exception( type_of_error = error_unknown, ... );
    virtual const char * what () const;
private:
    type_of_error error_type;
    string error_description;
};

5           没有解决的问题

5.1     需要的gcd,lcm,sqrt,pow,factorial复杂度还不清楚。

5.2     全部的无限长精确整数性能应该与著名的商业数学软件进行比较。

5.3     在无限长精确整数上,真实的精度应该可以被定义。

 

6           引用

1. D.E. Knuth, The Art of Computer Programming, Volume 2 (1998).

2. P. Zimmermann, An implementation of Sch¨onhage’s multiplication algorithm (1992).

3. A. Sch¨onhage and V. Strassen, Computing 7 (1971) 281.

4. Free Software Foundation, Gnu MP manual ed. 4.1.2 (2002).

5. http://numbers.computation.free.fr/Constants/Algorithms/fft.html

6. http://www.swox.com/gmp

7. http://www.math.utah.edu/docs/info/libg++ 20.html

阅读全文....

和实现有关的相同字符串存储方式检验的思考及c-string字符串

/*Copyright (c) 2007,九天雁翎

* All rights reserved.

* 和实现有关的相同字符串存储方式检验的思考

* 完成日期:2007年7月7日*/

#include "stdafx.h"

#include <iostream>

using namespace std;

int main()
{
    char *p1 = "Hello the world!";
    char *p2 = "Hello the world!";
    if(p1 == p2)
    {
        //ok,We could know it is the same as Bjarne Stroustrup said in VC++2005
        cout << "The same."<<endl;          
    }
    else
    {
        cout << "Not the same."<<endl;
    }
    //But look at this
    cout << &p1 <<'/t' <<&p2 <<endl;
    //the adress output is not the same!
    //Do you want to output it use p1 and p2?
    //Let try;
    cout << p1 <<'/t' << p2 <<endl;
    //We can only get the string.
    //Let reaffirm it
    if(&p1 == &p2)
    {
        cout<<"The same." <<endl;
    }
    else
    {
        cout<<"Not the same." <<endl;
    }
    return 0;
}

&p其实得到的是指针的地址,而不是c-string “Hello word!”的地址,而cout又为char* 重载了一个不同与一般指针的输出方式,所以你要输出”Hello world!”,可以通过static_cast<void>方式,强制转换到void指针,这样cout就可以输出想要的结果,结果自然和Bjarne Stroustrup说的一样,微软也没有错。

如下:

/*Copyright (c) 2007,九天雁翎

* All rights reserved.

* 和实现有关的相同字符串存储方式检验的思考

* 完成日期:2007年7月7日*/

#include "stdafx.h"

#include "myself.h"

#include <iostream>

using namespace std;

int main()
{
    char *p1 = "Hello the world!";
    char *p2 = "Hello the world!";
    if(p1 == p2)
    {
        cout << "The same."<<endl;          
    }
    else
    {
        cout << "Not the same."<<endl;
    }
    //Yes,it is.
    cout << static_cast<void*>(p1) <<'/t' <<static_cast<void*>(p2) <<endl;
    return 0;
}

阅读全文....

几种交换方式与参数传递方式效率的比较,发现STL最慢

/*Copyright (c) 2007,九天雁翎

* All rights reserved.

* 几种交换方式与参数传递方式效率的比较

* 完成日期:2007年7月7日*/

#include "stdafx.h"
#include "myself.h"
#include <iostream>
#include <utility>

using namespace std;

const int RUNTIME = 1e5;

void swap1(int &v1, int &v2);
void swap2(int &v1, int &v2);
void swap3(int *p1, int *p2);

int main()
{
    int v1 = 10000;
    int v2 = 20000;
    double t1 = myself::getTime();
    for (int i = 0; i < RUNTIME; ++i)
    {
       swap1(v1,v2);
    }
    double t2 = myself::getTime() - t1;
    cout <<v1 <<'/t' <<v2 <<endl;
    cout <<"Reference pass parameter and bit^ way used time: "<<t2 <<endl;

    t1 = myself::getTime();
    for (int i = 0; i < RUNTIME; ++i)
    {
       swap2(v1,v2);
    }
    t2 = myself::getTime() - t1;
    cout <<v1 <<'/t' <<v2 <<endl;
    cout <<"Reference pass parameter way used time: "<<t2 <<endl;

    t1 = myself::getTime();
    for (int i = 0; i < RUNTIME; ++i)
    {
       swap3(&v1,&v2);
    }
    t2 = myself::getTime() - t1;
    cout <<v1 <<'/t' <<v2 <<endl;
    cout <<"Point pass parameter way used time:" <<t2 <<endl;

    for (int i = 0; i < RUNTIME; ++i)
    {
       swap(v1,v2);
    }
    t2 = myself::getTime() - t1;
    cout <<v1 <<'/t' <<v2 <<endl;
    cout <<"STL way used time: " <<t2 <<endl;

    return 0;
}

void swap1(int &v1, int &v2)
{
    v1 = v1^v2;
    v2 = v1^v2;
    v1 = v1^v2;
}

void swap2(int &v1, int &v2)
{
    int temp = v1;
    v1 = v2;
    v2 = temp;
}

void swap3(int *p1, int *p2)
{
    int temp = *p1;
    *p1 = *p2;
    *p2 = temp;
}

结果是,指针效率一般最快,STL最慢,按位或没有效率优势,你可以改变RUNTIME去试试不同结果

阅读全文....

郁闷的要死,因为用中文注释,带来了无穷的痛苦

本来用中文注释是没有任何关系的应该,别人看也 容易明白,我自己看也容易明白,但是却常常在VC中产生很多莫名的错误,最让人受不了的是经常因为忘了关输入法,然后导致用的标点VC识别不了,这类错误自己几乎难以用初始的检查发现,而且一旦出现错误,很可能就是一大片的报错,改都改不过来,虽然我英文也许不能太准确的表达我的意思,我决定以后再写程序的时候一律用英文注释,以避免这种让人痛苦的行为,不要说我抛弃母语啊,为什么电脑是用英语的国家发明的?郁闷! 连在CSDN上写文章的TAG用中文都会遇到这种情况。。。。郁闷!不要和我说把中文输入法的标点也弄成半角的就好了,我要是常常能记得那么多,那就好了……………。</p>

阅读全文....

我自己学习C++编写的常用函数,类,模版 最近修改日期:2008年3月26日

鉴于个人习惯,将所有的文件都放到了jtianling目录下,一般的函数放到jtianling.h和jtianling.cpp中,其他的分类按文件放置,这样要包含文件就需要用include “jtianling/jtianling.h”形式包含,而且要在设置里面将jtianling这个目录包含进去

jtianling.h

———————————————————————————————

//-------Created By 九天雁翎(jtianling) Email:jtianling@gmail.com
//-------最后修改时间:3.26
//这个小库为了方便,特化了map和multimap所以包含进了<map>
//因为需要string和算法,包含了<algorithm>,<string>
//做这个库的目的本来就是简化一些学习C++的过程
//所以至于效率和文件的大小都没有那么太在乎了,毕竟不是工业级的应用:)
//最近在其中加入了算法效率比较的函数和随机数组创建函数

#ifndef JTIANLING_H
#define JTIANLING_H

#include <iostream>
#include <algorithm>
#include <string>
#include <map>
#include <vector>

namespace jtianling
{
//容器输出部分

//以容器为输入的简化函数,第二参数为前置的string,默认为空
template <typename T>
void PrintCon(const T &orig, const std::string &str = "")
{
    std::cout << str;
    typename T::const_iterator it;
    for(it = orig.begin(); it != orig.end(); ++it)
    {
        std::cout << *it <<" ";
    }
    std::cout << std::endl;
}

//特化map,multimap的模板,因为map,multimap的迭代器调用与一般容器有所不同
template<typename T1, typename T2>
void PrintCon(const std::map<T1, T2> &origMap, 
              const std::string &str = "")
{
    std::cout << str;
    typename std::map<T1, T2>::const_iterator it;
    for(it = origMap.begin(); it != origMap.end(); ++it)
    {
        std::cout << it->first << ":" << it->second << ";  ";
    }
    std::cout << std::endl;
}

template<typename T1, typename T2>
void PrintCon(const std::multimap<T1, T2> &origMultimap, 
              const std::string str = "")
{
    std::cout << str;
    typename std::multimap<T1, T2>::const_iterator it;
    for(it = origMultimap.begin(); it != origMultimap.end(); ++it)
    {
        std::cout << it->first << ":" << it->second << ";  ";
    }
    std::cout << std::endl;
}

//以参数数量不同,重载容器输出函数,以迭代器为输入,方便输出容器的一部份甚至数组,
//第三参数为前置的string,默认为空,因为输出部分map,multimap个人较少用到,所以不特
//化了,有需要的可以自己仿照特化响应模板
template <typename Iter>
void PrintCon(Iter itBegin, Iter itEnd, const std::string &str = "")
{
    std::cout << str;
    for( NULL; itBegin != itEnd; ++itBegin)
    {
        std::cout << *itBegin << " ";
    }
    std::cout << std::endl;
}


//-------------------------------------------------------------------
//时间获取部分
double GetTime(); //使用高精度计时器

//因为这里主要是比较不同算法的实现,所以这里不区分算法及其实现,都称为算法
//比较算法的函数,其中算法函数的参数为数组(array)和整数(int)
//简写为AI,为了防止混乱,这里我没有使用函数的重载
//准备以后有新的不同参数的算法时再加入新的比较函数
//方便最后查看对比我没有使用常规的屏幕输出而是输出到文件
//输出文件名为CompareAlogorithmAIResult.txt
//而且此文件可以用Excel打开,比较清晰,方便保存
//比较函数的参数解释如下:
//第一参数:各算法的用vector<>表示的函数指针数组
//第二参数:vector<int>表示的一系列算法函数第二参数,用以做算法增长比较
//主要注意的是,这里的第二参数实际也是算法函数第一参数数组的大小
typedef void (*pfAI)(int [], int);
void CompareAlgorithmAI(const std::vector<pfAI> &pfAIVec,const std::vector<int>& vec);


//比较算法的函数,其中算法函数的参数为整数(int)
//简写为I,方便最后查看对比我没有使用常规的屏幕输出而是输出到文件
//输出文件名为CompareAlogrithmIResult.txt
//而且此文件可以用Excel打开,比较清晰,方便保存
//比较函数的参数解释如下:
//第一参数:各算法的用vector<>表示的函数指针数组,
//第二参数:vector<int>表示的一系列算法函数第二参数,用以做算法增长比较
typedef void (*pfI)(int);
void CompareAlgorithmI(const std::vector<pfI> &pfIVec,const std::vector<int>& vec);

}//end of jtianling namespace
#endif

jtianling.cpp

//-------Created By 九天雁翎(jtianling) Email:jtianling@gmail.com
//-------最后修改时间:.3.26

#include "jtianling.h"
#include <iomanip>
#include <fstream>
#include "windows.h"

namespace jtianling
{

    double GetTime() //使用高精度计时器
    {      
        static LARGE_INTEGER s_freq;
        LARGE_INTEGER performanceCount;
        double t;
        if (s_freq.QuadPart==0)
        {
            if ( !QueryPerformanceFrequency( &s_freq))
                return 0;
        }
    
        QueryPerformanceCounter( &performanceCount );
        t=(double)performanceCount.QuadPart / (double)s_freq.QuadPart;
        return t;
    }

    //因为这里主要是比较不同算法的实现,所以这里不区分算法及其实现,都称为算法
    //比较算法的函数,其中算法函数的参数为数组(array)和整数(int)
    //简写为AI,为了防止混乱,这里我没有使用函数的重载
    //准备以后有新的不同参数的算法时再加入新的比较函数
    //比较函数的参数解释如下:
    //第一参数:各算法的用vector<>表示的函数指针数组
    //第二参数:vector<int>表示的一系列算法函数第二参数,用以做算法增长比较
    //主要注意的是,这里的第二参数实际也是算法函数第一参数数组的大小
    void CompareAlgorithmAI(const std::vector<pfAI> &pfAIVec,const std::vector<int>& vec)
    {
        double timeInAll = GetTime();  //这里保存所有比较花费的时间
        std::ofstream out("CompareAlogrithmAIResult.txt");//将结果保存在这里
        
        //在文件中输入第一行
        out <<"parameter" <<'/t';
        for(int i=0; i<pfAIVec.size(); ++i)
        {
            out <<"Algorithm " <<i+1 <<'/t';
        }
        out <<std::endl <<std::setprecision(4) <<std::scientific;
        
        double timeTaken;  //每个算法一次消耗的时间

        //以算法参数的数组个数为循环条件,每个数值让各个算法分别调用
        for(int i=0; i<vec.size(); ++i)
        {
            out <<vec[i] <<'/t';
            int *arr = new int[ vec[i] ];  //每次动态生成一个数组
            for(int j=0; j<pfAIVec.size(); ++j)
            {
                
                timeTaken = GetTime();
                pfAIVec[j](arr, vec[i]); //实际调用不同算法
                timeTaken = GetTime() - timeTaken;
                out <<timeTaken <<'/t';
            }
            delete []arr;   //删除arr,因为只考察算法的效率,所以根本不关心结果
                            //至于各算法是否正确由调用者保证
            out<<std::endl;
        }
        timeInAll = GetTime() - timeInAll;
        out <<"Compared time in all: " <<timeInAll <<std::endl;
    }

    void CompareAlgorithmI(const std::vector<pfI> &pfIVec,const std::vector<int>& vec)
    {
        double timeInAll = GetTime();  //这里保存所有比较花费的时间
        std::ofstream out("CompareAlogrithmIResult.txt");//将结果保存在这里
        
        //在文件中输入第一行
        out <<"parameter" <<'/t';
        for(int i=0; i<pfIVec.size(); ++i)
        {
            out <<"Algorithm " <<i+1 <<'/t';
        }
        out <<std::endl <<std::setprecision(4) <<std::scientific;
        
        double timeTaken;  //每个算法一次消耗的时间

        //以算法参数的数组个数为循环条件,每个数值让各个算法分别调用
        for(int i=0; i<vec.size(); ++i)
        {
            out <<vec[i] <<'/t';
            for(int j=0; j<pfIVec.size(); ++j)
            {
                timeTaken = GetTime();
                pfIVec[j](vec[i]); //实际调用不同算法
                timeTaken = GetTime() - timeTaken;
                out <<timeTaken <<'/t';
            }
            out<<std::endl;
        }
        timeInAll = GetTime() - timeInAll;
        out <<"Compared time in all: " <<timeInAll <<std::endl;
    }
}

rand.h

//-------Created By 九天雁翎(jtianling) Email:jtianling@gmail.com
//-------最后修改时间:.3.26

#ifndef RAND_H
#define RAND_H

namespace jtianling
{
    //还是需要自己设定种子,此函数只保证范围
    //此算法生成从lowBorder到highBorder的随机数,而且包括边界lowBorder,highBorder.
    //即lowBorder=<n<=highBorder
    int RandIntInRange(int lowBorder, int highBorder);

    //算法描述如<<Data Structures and Algorithm Analysis in C++>>
    //By Mark Allen Weiss 题目.8算法所示,根据习惯,不对outArray[]的大小做任何检验,
    //调用此函数的人应该确保这一点,即outArray的大小大于等于numOfArray
    //此算法的用途是高效地产生一个不重复的随机序列
    //且此序列正好包括(1,numOfArray)中所有自然数
    void RandArrayNoRepeatInN(int outArray[], int numOfArray);

    //此算法产生随机的不重复的数组,数组大小为N
    //利用set容器的特性来保证没有重复,因为set容器的查找远快于一个一个查找
    //所以此方法比<<Data Structures and Algorithm Analysis in C++>>
    //By Mark Allen Weiss 题目.8算法所示算法快,而且增长慢很多
    void RandArrayNoRepeat(int outArray[], int numOfArray);

    //产生一个的随机序列且此序列的数值只在(1,numOfArray)中
    void RandArrayInN(int outArray[], int numOfArray);

    //产生一个随机的序列,且序列的值为完全随机,序列的大小由numOfArray指定
    void RandArray(int outArray[], int numOfArray);

}//end of namespace jtianling

rand.cpp

//-------Created By 九天雁翎(jtianling) Email:jtianling@gmail.com
//-------最后修改时间:.3.28
#include "rand.h"
#include <cstdlib>
#include <ctime>
#include <algorithm>
#include <set>

namespace jtianling
{
    //此算法生成从i到j的随机数,而且包括边界i,j.即i=<n<=j
    int RandIntInRange(int i, int j)
    {
        //确保i<j,不然就交换
        if(i > j)
        {
            int temp = i;
            i = j;
            j = temp;
        }
        //确保范围正确
        return rand()%(j-i+1) + i;
    }

    //算法描述如<<Data Structures and Algorithm Analysis in C++>>
    //By Mark Allen Weiss 题目.8算法所示,根据习惯,不对arr[]的大小做任何检验,
    //调用此函数的人应该确保这一点,即arr的大小大于等于n
    //此算法的用途是高效地产生一个不重复的随机序列
    //且此序列正好包括(1,n)中所有自然数
    void RandArrayNoRepeatInN(int arr[], int n)
    {
        srand(time(NULL) );
        for(int i=0; i<n; ++i)
        {
            arr[i] = i + 1;
        }
        for(int i=0; i<n; ++i)
        {
            std::swap(arr[i], arr[RandIntInRange(0, i)] );
        }
    }

    //此算法产生随机的不重复的数组,数组大小为N
    //利用set容器的特性来保证没有重复,因为set容器的查找远快于一个一个查找
    //所以此方法比<<Data Structures and Algorithm Analysis in C++>>
    //By Mark Allen Weiss 题目.8算法所示算法快,而且增长慢很多
    void RandArrayNoRepeat(int arr[], int n)
    {
        srand(time(NULL) );

        std::set<int> intSet;
        int temp;
        for(int i=0; i<n; ++i)
        {

            while(true)
            {
                temp = rand();
                if(!intSet.count(temp))
                {
                    arr[i] = temp;
                    intSet.insert(temp);
                    break;
                }
            }
        }
        PrintCon(arr, arr+n);

    }

    //产生一个的随机序列且此序列的数值只在(1,n)中,序列的大小由n指定
    void RandArrayInN(int arr[], int n)
    {
        srand(time(NULL) );
        for(int i=0; i<n; ++i)
        {
            arr[i] = RandIntInRange(0, n);
        }
    }

    //产生一个随机的序列,且序列的值为完全随机,序列的大小由n指定
    void RandArray(int arr[], int n)
    {
        srand(time(NULL) );
        for(int i=0; i<n; ++i)
        {
            arr[i] = rand();
        }
    }

}//end of namespace jtianling

阅读全文....

可恶的Cpp(c语言预处理器),windows.h,导致程序莫名错误

而且,我还持有这样的观点,

Cpp必须被摧毁

-–Bjarne Stroustrup

全世界有经验的程序员都教导我们,应该多用C++中的特性,不要再停留在C语言中某些特别容易导致错误的旧特性,其中,预处理就是特别典型的一个,D&E中Bjarne Stroustrup详细解释了他为预处理提供的各个替代措施,但是,偏偏就是有人这么无聊,就是还喜欢用!比如windows.h中,一个特别特别无稽的使用宏去定义max(),min(),用宏就算了,竟然全部用的都是小写!我简直想拆了微软!因为很明显这样做是非常愚蠢的!比如下面这样一个简单的利用例子,因为包含了windows.h而无法运行。

#include <iostream>

#include <windows.h>

template<typename T>
class Type
{
public:
    static void print()
    {
        std::cout<< "sizeof(" << typeid(T).name() << ") = "
                  << sizeof(T) <<" and its range is ("
                  << std::numeric_limits<T>::min() <<", "
                  << std::numeric_limits<T>::max() << ")"<< std::endl;
    }
};

不要问我为什么要包含windows.h,因为这个程序下面要用到啊,这么简单的程序,因为windows.h而不能使用,我是该怪Dennis M.Ritchie设计C预处理,还是怪Bjarne Stroustrup竟然在C++保留了这些,还是怪微软呢?这几乎是windows下编程(自然需要windows.h啊)与C++之间的矛盾啊!

另外,这时,我不得不

#ifdef max     //因为windows.h里面竟然定义了max,min的宏
#undef max     //导致我不得不这样先让他们失效
#endif

#ifdef min
#undef min
#endif

// 木及其郁闷阿

阅读全文....

关于容器输出的进一步优化

以前我讨论过了在自己学习过程中经常要用到的一个特性,就是容器的输出问题,总感觉不是太方便,在学习过程中用的又非常多,我曾经在《 学了模板再来看容器输出的简化》  中已经把他处理的很简单了,不过最近看了 TC++PL受了点启发,又将程序进一步改进,主要的好处是更符合标准库容器的使用习惯,以首尾两个迭代器为输入,而且对普通的数组也可以使用,这样最大的方便之处在于可以接受一个范围的输出了。不过比起以前那种直接传递容器的引用来说,普通的输出整个容器使用上还是复杂一点。

原程序如下:

template <typename T>
void printCon(T begin, T last)  //改进后
{
    for(; begin != last; ++begin)
        cout<<*begin<<" ";
    cout<<endl;
}

一个使用的例子:

using namespace std;

int main()
{
    char cstr[4] = {'a', 'b', 'c', 'd'};
    vector<char> cvec(5, 'a');
    //I put printCon in the namespace of myself
    myself::printCon(cstr, cstr+4);
    myself::printCon(cvec.begin(), cvec.end() );
    return 0;
}

阅读全文....

和实现有关的各类型大小简易输出模版

/*Copyright (c) 2007,九天雁翎
* All rights reserved.
* 和机器有关的类型大小简易输出模版
* 完成日期:年月日*/
#include "stdafx.h"

#include <iostream>
#include <typeinfo>
#include <limits>

template<typename T>
class Type
{
public:
    static void print()
    {
        std::cout<< "sizeof(" << typeid(T).name() << ") = "
                 << sizeof(T) <<" and its range is ("
                 << std::numeric_limits<T>::min() <<", "
                 << std::numeric_limits<T>::max() << ")"<< std::endl;
    }
};

int main()
{
    Type<long long>::print();  //检查long long类型的大小及其范围
    return 0;
}

//在学习的过程中,及换到不同编译系统,不同电脑的时候都有所需要,
//虽然编写上的确没有什么技术含量。

阅读全文....

看了TCPL后,对C,C++书籍的一些想法

的确好久没有来写些什么新东西了,的确经常和同学们疯狂喝酒,彻夜狂欢似的K歌,没有那份清醒来完成什么新的东西,但是大整数基本有了个思路,而且最近回头去看了看TCPL,K&D那本,感觉其实自己学C都有3年多了,虽然中间断断续续,个人感觉是同学们中间学的最好的,大一考试笔试只扣了2分,很早的就过了C二级,并且自己很喜欢用C解决一些实际的问题,比如物理实验的数据计算,本来N组数据的计算,同学们都辛苦的按着计算机,我通过C计算起来非常愉快,然后一批同学都最后借用我的电脑解决了问题:)很多个实验最后都这样。在学C++以前我一度还以为自己C基础算不错的了,虽然知道C基础并不一定对学习C++有太大用处。但是其实还是有很多盲点,因为我看得是我们学校自己编的C语言教材,说实话,那本教材我吃的很透,很多最小最小的练习,或者例题我都自己编写,调试过了。后来把谭浩强那本当作参考书,偶尔翻了翻,感觉也差不多。最近再翻阅TCPL,还是感叹自己的无知。。。。突然想起一个人说的,永远不要以为自己已经了解C++了,我现在觉得,以为自己了解C了这个问题也要想清楚。也知道了一本好书,真正是,要学习一样东西,最好就是看最好的书!不然,就算你把一本书看透了也没有达到应该的效果。举例子,都说C++ Primer通俗易懂,于是我在看过钱能的C++教材后(说实话,粗略的断断续续地看了看),开始看C++ Primer,我真的用了3个月,仔细地看,仔细地想,仔细地编写,调试。到最后,我发现,我了解得无非就是一堆C++语法的细节,(要承认,其中还是讲了一些编程的要点,可惜都是类似Effective C++的细节中的细节)而且仅仅是了解,比如最近很少用模版,感觉模版的一些细节又模糊了,什么特例普通函数的冲突啊 ·#!%¥#……¥·#—其实我感觉,C++ Primer对语言不厌其烦的深入细节,让他成为了无可厚非的经典,不过是用来查阅的参考书经典,用来当入门的教材,不是太适合。要看就看一些最经典的,比如TC++PL,目前我的计划,就是把这本书啃了。其实,回忆起来,当垃圾书看多了后,再看经典,真有一种扑面而来的愉快。我初看C++ Primer 就是这样,当我看到TC++PL更是这样。让我们牢记,拒绝一般,只看经典!不然就只能看到那么多人抱怨的C++难学,因为走得是一条弯路。

阅读全文....

忽视的复杂性,关于C++中大整数的思考

原以为一个以前在C中轻易实现的猜数字游戏即便我加了一些奇怪的规则,还应该是非常简单就能实现的,但是,我忽略了C/C++ 中大整数带来的复杂性,的确,当整数范围超过long所能表示的范围以后,简单的四则运算或逻辑比较都是需要很复杂的代码才能实现。我在问题(3)就开始要解决一个这样的问题,似乎已经违背了我当初设想的从易到难写C++程序的目标了,但是,因为我对C++的了解程度,自然也很难真的说(或者对不同的人也不一样的)从易到难。但是因为太多方案在头脑中,所以先把问题提出来吧,假如有人来看,各取所需吧,我的解答自然不可能一下子出来,目前,我的想法是,实现一个稍微实用一点的大整数类库,以我的水平,自然不能多么完善,但希望这个类库能伴随我以后解决自己为自己提出的各种刁钻问题,目标自然很明确,让大整数的使用像内置类型一样!哪怕是多么大的天文数字!呵呵,目标而已,目标而已。

阅读全文....