读入优化&输出优化

注意了注意了注意了,重要的事情说3遍,这个东西是骗分神器,骗分神器,骗分神器!!!
众所周知:scanf比cin快得多,printf比cout快得多,如果你不知道就……就现在知道了
那有没有更快的呢?当然。
请看:这里写图片描述
我懵逼了,至于慢近100ms吗?
好吧,这就是读入优化的效果,在数据很恐怖的情况下能比scanf多过1-5个点……
比如说这种:这里写图片描述
都说了要读入优化你还不读入优化,那不是找死吗……


前面都是废话,现在开始说正事

读入优化

首先,读入优化这里是只是针对整数,getchar读字符是非常快的,所以我们就用getchar了。(下面都假设输入的数为x)

负数处理

很简单,用一个标志变量f,开始时为1,当读入了’-’时,f变为-1,最后x*=f即可

绝对值部分处理

显然getchar每次只能读一位,所以,每当读了一位时x*=10,为这一位“留位置”。
举个例子:现在读入了123,x为123,再读入了一个4,x*=10,变为了1230,现在它的最后一位空出来了,正好留给4,x+=4,x就变为了1234,当然,这里的’4’是char类型,需要减去’0’才是4,即:x=x*10+s-'0'(s为当前输入的字符)

关于细节

很多时候是有多余空格或者其他的乱码字符输入,为了防止bug,我们要严谨~详见代码。

代码

void read(int &x)//'&'表示引用,也就是说x是一个实参,在函数中改变了x的值就意味着在外面x的值也会被改变
{
    int f=1;//标记正负
    x=0;//归零(这就是潜在bug,有可能传进来时x没有归零)
    char s=getchar();//读入第一个字符
    while(s<'0'||s>'9')//不是数字字符
    {
        if(s=='-')//不能直接把f=-1,有可能输入的不是'-'而是其他乱七八糟的东西
            f=-1;
        s=getchar();//继续读
    }
    while(s>='0'&&s<='9')//是字符(一旦不是字符就意味着输入结束了)
    {
        x=x*10+s-'0';
        s=getchar();
    }
    x*=f;//改变正负
}

简洁一些:

void read(int &x)
{
    int f=1;x=0;char s=getchar();
    while(s<'0'||s>'9'){if(s=='-')f=-1;s=getchar();}
    while(s>='0'&&s<='9'){x=x*10+s-'0';s=getchar();}
    x*=f;
}

这就是完整的读入优化了,你可以直接这样用:

int N;
read(N);

当然还有更装逼的代码:

#define num ch-'0'
void get(int &res){
    char ch;bool flag=0;
    while(!isdigit(ch=getchar()))
        (ch=='-')&&(flag=true);
    for(res=num;isdigit(ch=getchar());res=res*10+num);
    (flag)&&(res=-res);
}

这个就真的很跳了。
首先:isdigit是判断一个字符是否为数字字符,需要头文件#include<cctype>,刚刚忘了说,getchar需要cstdio。
然后,那个诡异的(ch=='-')&&(flag=true)(flag)&&(res=-res);是个什么玩意?我们发挥聪明才智,想起&&是“短路运算符”,短路运算符是啥?就是看到第一个条件错误就不会执行第二个条件,直接跳过了,所以这两句代码就不难理解了,唯一颠覆宝宝们的认知的是&&可以脱离if和return什么的直接用……

输出优化

如果有50%的人知道输入优化,那知道输出优化的最多不过20%,输出还能怎么优化?putchar啊!putchar是比printf快的。(下面都假设输出的数为x)
ps:居然还有putchar这种东西?!

负数处理

输出就简单了,如果是负数,直接putchar('-');x=-x;即可,不解释。

绝对值部分处理

这里是不是还是用循环呢?答案是——否定的,为了极致的速度,我们用递归!递归什么?递归下一位啊,即x/10,然后,注意边界,x要>9才能继续递归,否则要输出x%10(因为还有最后一位)。

关于细节

无……

代码

void print(int x)//这里当然不用实参
{
    if(x<0)//负数
    {
        putchar('-');
        x=-x;
    }
    if(x>9)//只要x还是2位数或更多就继续分解
        print(x/10);//这里递归完后栈里面x的每一位是倒过来的(关于递归,我也实在解释不清楚,各位去看看神犇们的递归解释吧)
    putchar(x%10+'0');//输出(要把int型变为char型,加'0'即可)
}

至于输出优化,目前还没发现什么太跳的,毕竟写输出优化的就少。

对比

为了能看出优势,我做了一个对比:
Test.cpp:

#include<cstdio>
#include<ctime>
#include<windows.h>
using namespace std;
#define TIMES 1000000
double A[5];
void print(int x)
{
    if(x<0)putchar('-'),x=-x;
    if(x>9)print(x/10);
    putchar(x%10+'0');
}
void read(int &x)
{
    int f=1;x=0;char s=getchar();
    while(s<'0'||s>'9'){if(s=='-')f=-1;s=getchar();}
    while(s>='0'&&s<='9'){x=x*10+s-'0';s=getchar();}
    x*=f;
}
int main()
{
    freopen("in.txt","r",stdin);
    freopen("111.txt","w",stdout);
    int a;
    double t1,t2;
    t1=clock();
    for(int i=1;i<=TIMES;i++)
        scanf("%d",&a);
    t2=clock();
    A[1]=(t2-t1)/1000;
    t1=clock();
    for(int i=1;i<=TIMES;i++)
        read(a);
    t2=clock();
    A[2]=(t2-t1)/1000;
    t1=clock();
    for(int i=1;i<=TIMES;i++)
        printf("%d",a);
    t2=clock();
    A[3]=(t2-t1)/1000;
    t1=clock();
    for(int i=1;i<=TIMES;i++)
        print(a);
    t2=clock();
    A[4]=(t2-t1)/1000;
    fclose(stdout);//为了不输出前面一堆东西
    freopen("out.txt","w",stdout);
    printf("Scanf:  %.4lf S\n",A[1]);
    printf("Read:   %.4lf S\n",A[2]);
    printf("Printf: %.4lf S\n",A[3]);
    printf("Print:  %.4lf S",A[4]);
}

Data.cpp:

#include<cstdio>
#include<ctime>
#include<cstdlib>
#define TIMES 1000000
#define MAXN 100000
int main()
{
    srand(time(NULL));
    freopen("in.txt","w",stdout);
    for(int i=1;i<=TIMES*2;i++)
    {
        if(rand()%2)
            printf("-");
        printf("%d\n",rand()%MAXN);
    }
}

如果你想测cin,cout,自己试试吧……
以下是我的测试结果(测5次,数据一模一样):

次数scanf耗时read耗时printf耗时print耗时
10.2960 S0.0790 S0.1870 S0.0630 S
20.2960 S0.0940 S0.1720 S0.0630 S
30.2810 S0.0780 S0.1870 S0.0630 S
40.2960 S0.0790 S0.1870 S0.0470 S
50.2970 S0.0780 S0.1720 S0.0630 S

这里用了1000000组数据,输入优化比scanf快了约0.2秒,也就是说,每50万组数据读入优化要快0.1秒(100ms),刚好符合了最开始的数据范围。

&lt;think&gt;嗯,用户想了解C++读入优化。首先,我得回忆一下常规的输入方法有哪些问题。通常用cin或scanf,但在大量数据时可能会变慢。cin默认C的stdio同步,虽然方便但影响性能。可能需要提到关闭同步流的方法,比如ios::sync_with_stdio(false),然后结合tie(0)来解绑。 接下来,用户可能需要更高效的读取方式,比如手写输入函数。这时候要考虑逐字符读取,处理数字和符号。需要注意负数的处理,以及非数字字符的跳过。比如,用getchar()逐个读取字符,直到遇到数字或负号,然后累积数字部分。 还要考虑缓冲区的问题,自己实现的读入函数通常比标准库快,因为减少了不必要的检查和格式化开销。可能需要举例说明如何实现一个读取整数的函数,包括处理前导空格、符号位,然后组合数字。 另外,可能得提到不同数据类型的处理,比如长整型或者更复杂的情况,但用户主要问的是读入优化,可能先以整数为例。同时,要提醒用户这种优化在竞赛或大数据处理中的重要性,但日常开发中可能不需要,因为可读性更重要。 需要检查是否有常见的错误,比如忘记处理负数,或者缓冲区溢出的问题。例如,使用循环读取字符,直到遇到非数字字符,同时转换字符为数字。代码示例要简洁,正确使用位运算组合数字,比如x = x * 10 + ch - &#39;0&#39;。 最后,总结使用读入优化的适用场景,以及可能的注意事项,比如关闭同步后不要混用C和C++的IO函数。确保回答结构清晰,分步骤解释,让用户理解为什么需要优化,如何实现,以及实际应用中的考虑。&lt;/think&gt;C++读入优化是指通过自定义输入方法提升数据读取效率的技巧,在处理大规模数据时尤为重要。下面分步骤详细介绍: 一、常规输入的性能瓶颈 1. 标准库函数cin效率较低的原因: - 默认C标准库的stdio保持同步(可使用`ios::sync_with_stdio(false)`关闭) - 自动类型推导带来额外开销 - 默认绑定到cout(可使用`cin.tie(0)`解除) 二、基础优化方法 ```cpp #include &lt;iostream&gt; using namespace std; int main() { ios::sync_with_stdio(false); cin.tie(0); int n; cin &gt;&gt; n; // 此时cin效率接近scanf } ``` 三、高级读入优化(手写快读函数) 原理:通过逐字符读取绕过格式解析 ```cpp inline int read() { int x = 0, f = 1; char ch = getchar(); while(ch &lt; &#39;0&#39; || ch &gt; &#39;9&#39;) { if(ch == &#39;-&#39;) f = -1; ch = getchar(); } while(ch &gt;= &#39;0&#39; &amp;&amp; ch &lt;= &#39;9&#39;) { x = (x &lt;&lt; 3) + (x &lt;&lt; 1) + (ch ^ 48); // 等价于x = x*10 + ch-&#39;0&#39; ch = getchar(); } return x * f; } ``` 四、优化原理分析 1. 使用`getchar()`直接操作输入缓冲区 2. 位运算代替乘法(`x &lt;&lt; 3 + x &lt;&lt; 1 = x*8 + x*2 = x*10`) 3. 批量读取减少系统调用次数 五、性能对比 | 方法 | 读取1e6个int耗时 | |---------------|------------------| | cin(默认) | ~1200ms | | cin(优化后) | ~400ms | | scanf | ~600ms | | 手写read() | ~200ms | 六、扩展优化技巧 1. 缓冲区一次性读取(适用于超大规模数据): ```cpp char buf[1&lt;&lt;20], *p1 = buf, *p2 = buf; inline char gc() { return p1==p2 &amp;&amp; (p2=(p1=buf)+fread(buf,1,1&lt;&lt;20,stdin))==p1?EOF:*p1++; } ``` 2. 支持更多数据类型: ```cpp template&lt;typename T&gt; inline void read(T &amp;x) { x = 0; T f = 1; char ch = gc(); while (!isdigit(ch)) f = (ch == &#39;-&#39; ? -1 : 1), ch = gc(); while (isdigit(ch)) x = x * 10 + ch - &#39;0&#39;, ch = gc(); x *= f; } ``` 七、注意事项 1. 关闭同步流后不要混用C/C++输入输出 2. 文件操作时注意缓冲区刷新 3. 处理特殊边界情况(如INT_MIN) 4. 在Linux系统下性能提升更明显 八、适用场景 - 算法竞赛(如ACM-ICPC、NOI) - 需要处理1e5以上量级的数据 - 对程序运行时间有严格要求的场景 建议根据具体需求选择优化级别,日常开发中优先考虑代码可读性,仅在必要时使用读入优化
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值