• 奇技淫巧:NOIP的读入优化


    最近看到洛谷上面有一个读入优化的代码:

    inline char get_char(){//劲者快读
        static char buf[1000001],*p1=buf,*p2=buf;
        return p1==p2&&(p2=(p1=buf)+fread(buf,1,1000000,stdin),p1==p2)?EOF:*p1++;
    }
    inline short read(){
        short num=0;
        char c;
        while(isspace(c=get_char()));
        while(num=num*10+c-48,isdigit(c=get_char()));
        return num;
    }

    说实话第一个函数get_char的第二行,这么长一六三目运算符真心看不懂

    (下面的read函数里面那个isspace()和isdigit()就是判断这个字符是不是空格,是不是数字,是的就返回true,不是返回false。你看多没用的函数= =)

    然后我就把代码百度了一下,发现还真有类似的东西:

    inline char NC(void)
    {
      static char buf[100000], *p1 = buf, *p2 = buf;
      if (p1 == p2) {
        p2 = (p1 = buf) + fread(buf, 1, 100000, stdin);
        if (p1 == p2) return EOF;
      }
      return *p1++;
    }

    于是研究了一会,发现这是一个极其神奇的读入优化

    一般来说我们读入都用的scanf和cin,实在必要的时候可以用getchar读入优化。

    然后众所周知,cin比scanf慢,getchar最快。

    但是到底差距有多大很多人都不知道。

    于是上个星期我做了一个贼有意思的测试,把scanf、cin、getchar(分为宏定义函数和内联函数两个)分别读入1000000个数,然后输出运行时间。

    getchar的两个函数贴在这里:

    #define gi(a) do { 
      register char ch; 
      while((ch = getchar()) > '9' || ch < '0'); 
      for(a = ch-'0'; (ch = getchar()) >= '0' && ch <= '9'; a = a*10+ch-'0'); 
      }while(0)
    inline void gi2(int &a) {
        register char ch; 
        while((ch = getchar()) > '9' ||ch < '0'); 
        for(a = ch-'0'; (ch = getchar()) >= '0' && ch <= '9'; a = a*10+ch-'0'; 
    }
    //(仅限正整数)

    发现scanf大概比cin快2倍,getchar比cin快5倍。

    其中宏定义的getchar稍稍比内联的getchar快那么一点点点点。

    然后就是今天我看到的玄学优化了。

    https://www.byvoid.com/zhs/blog/fast-readfile

    这里有所有读入的速度评估(看来我还不是第一个干这种贼有意思的事情的wwww)

    经过实测,这种优化比scanf快了10倍!

    这个读入用的是fread,我百度了一下,这是一种直接把文件所有字符全部读入的一个函数。

    函数原型是这样的:

    size_t fread ( void *buffer, size_t size, size_t count, FILE *stream) ;

    可以看到有四个参数,分别是读入的数组,读入每个数据项的字节数,读入的个数,以及读入的文件stream。

    返回的是读到的数据项的个数。

    此外还有个static关键字,就把它看成是函数里面的全局变量就行了,也就是说再用一次这个函数,里面的值不会变的。

    此外static只有在第一次定义的时候才能赋值,之后调用的时候赋值是无效的,也就是说之后的调用函数这个赋值那一行是没用的。

    这里还有个点,看这一行:

    p2 = (p1 = buf) + fread(buf, 1, 100000, stdin);

    这里的=号的返回值就是赋值号右边的那个值,也就是buf的值。

    那么就等于写成这样:

    p1 = buf; 
    p2 = buf+ fread(buf, 1, 100000, stdin);

    在我们一开始调用函数的时候,p1 p2都被赋值为buf的数组开始的位置,之后进入if语句,p2变成数据最后的位置(开始的位置+数据的长度 = 最后的位置)。

    然后随着每次返回,p1都会往前面移动一个,直到p1也遍历到了最后的位置,p1 == p2

    这时候再次进入if语句,然后同样p2变成数据最后的位置,因为p1这时也是数据最后的位置了,所以p1照样==p2,于是数据遍历结束了,返回文件结束符EOF.

    就这样,完美模拟一次文件读写~

    然后把if语句简化就变成了那个很长一六的三目运算符:

    inline char get_char(){//劲者快读
        static char buf[1000001],*p1=buf,*p2=buf;
        return p1==p2&&(p2=(p1=buf)+fread(buf,1,1000000,stdin),p1==p2)?EOF:*p1++;
    }

    可以把这个模板背一下,然后就直接套自己的getchar读入优化模板了。

    当然也可以把这里面的static索性全部拿到外面去,变成全局变量,然后做一个宏定义:

    char buf[1000001],*p1=buf,*p2=buf;
    #define get_char() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1000000,stdin),p1==p2)?EOF:*p1++)

    虽说这样肯定比内联函数快,但是因为宏定义毕竟是机械式替换的……所以不保证不出玄学bug,慎用。(不过一般来说用我自己写的模板不会有问题)

    (其实我不会把只有一行语句的宏用do while(0)封装起来,所以只能打个括号完事)

    这个优化虽然快是快,但是因为奇技淫巧……不保证不出bug,所以还是谨慎使用。

    就当做普及一个玄学优化吧。

    这里有一个大神写的整合版,大致原理是一样的,不过代码更简单,而且读入比我的又要快一倍(用的指针+一次性读入所有的整数),代码如下

    const int MAXS = 60*1024*1024;
    const int MAXN = 10000000;
    char buf[MAXS];
    int numbers[MAXN];
    
    void analyse(char *buf,int len = MAXS)
    {
        int i;
        numbers[i=0]=0;
        for (char *p=buf;*p && p-buf<len;p++)
            if (*p == ' ')
                numbers[++i]=0;
            else
                numbers[i] = numbers[i] * 10 + *p - '0';
    }
    void fread_analyse()
    {
        freopen("data.txt","rb",stdin);
        int len = fread(buf,1,MAXS,stdin);
        buf[len] = '';
        analyse(buf,len);
    }

    最后贴上我把这个玄学优化写进我的玄学getchar里面弄出的超级巨型玄学贼有意思奇技淫巧读入优化

    char buf[1000001],*p1=buf,*p2=buf;
    #define get_char() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1000000,stdin),p1==p2)?EOF:*p1++)
    #define gi(a) do { 
      register char ch; 
      while((ch = get_char()) > '9' || ch < '0'); 
      for(a = ch-'0'; (ch = get_char()) >= '0' && ch <= '9'; a = a*10+ch-'0'); 
      }while(0)
  • 相关阅读:
    mybatis模糊查询语句
    Java中解压文件名有中文的rar包出现乱码问题的解决
    tomcat服务器开启gzip功能的方法
    asp.net 操作word
    asp.net webservice 返回json数据乱码解决方法
    阿里云服务器mysql修改编码问题
    阿里云服务器问题:访问
    EasyUI 在aspx页面显示高度不正常解决办法
    C# 或 JQuery导出Excel
    如何分离数据库 (SQL Server Management Studio)
  • 原文地址:https://www.cnblogs.com/euphoria-eden/p/7581121.html
Copyright © 2020-2023  润新知