【问题标题】:Optimizing I/O(Output) in C code + a loop在 C 代码 + 循环中优化 I/O(输出)
【发布时间】:2012-03-30 14:57:12
【问题描述】:

我有一个代码从标准输入读取大约 (10^5) 个整数,然后在执行 ## 之后将它们输出到标准输出。我已经通过使用“setvbuf”和使用“fgets_unlocked()”读取行来处理 INPUT 部分,然后解析它们以获得所需的 int(s)。 我有 2 个问题无法解决:

1.) 当我在标准输出上打印 int(s) 500 万时,它花费了很多时间:有没有办法减少这个(我尝试使用 fwrite() 但由于原因,o/p 打印了不可打印的字符using fread to read into int buffer)

2.) 在解析 int(s) say 'x' 的输入后,我实际上通过在循环中对 no 执行 %(mod) 来找到除数的 no。(参见下面的代码):也许这个也是我的代码超时的一个原因: 对此有任何改进建议。 非常感谢 这其实是来自http://www.codechef.com/problems/PD13的问题

# include <stdio.h>
# define SIZE 32*1024
char buf[SIZE];

main(void)
{
int i=0,chk =0;
unsigned int j =0 ,div =0;
int a =0,num =0;
char ch;

setvbuf(stdin,(char*)NULL,_IOFBF,0);

scanf("%d",&chk);
while(getchar_unlocked() != '\n');
while((a = fread_unlocked(buf,1,SIZE,stdin)) >0)
{
    for(i=0;i<a;i++)
    {
        if(buf[i] != '\n')
        {
            num = (buf[i] - '0')+(10*num);
        }
        else
        if(buf[i] == '\n')
        {   
            div = 1;
            for(j=2;j<=(num/2);j++)
            {
                if((num%j) == 0)    // Prob 2
                {
                    div +=j;
                }
            }
            num = 0;
            printf("%d\n",div); // problem 1
        }       
    }
}
return 0;
 }

【问题讨论】:

  • 我几乎不相信 stdio 有限制。一般。像这样的程序是严格(磁盘)I/O 绑定的,CPU 利用率将
  • 顺便说一句:您的内部循环似乎消耗了所有 CPU。您是否考虑过将数字分解为素数,并生成所有素因数组合的总和?
  • :D @wildplasser 我的答案在下面坐了几分钟......
  • 伟大的思想是相似的,所以看起来 ;-} 重新组合主要因素也可能代价高昂。但可能不像尝试所有可能的除数那样昂贵。顺便说一句:恕我直言,这个问题似乎是错误的;对于某些示例,数据 1 被视为除数;但 input=2 的总和被列为 2,而不是 3。

标签: c optimization io


【解决方案1】:

您的打印速度比 printf 快得多。

查看itoa(),或编写您自己的简单函数,将整数快速转换为ascii。

这是 itoa 的 quick-n-dirty 版本,应该可以快速满足您的目的:

char* custom_itoa(int i)
{
    static char output[24];  // 64-bit MAX_INT is 20 digits
    char* p = &output[23];

    for(*p--=0;i/=10;*p--=i%10+0x30);
    return ++p;    
}

请注意,此函数有一些严重的内置限制,包括:

  • 它不处理负数
  • 目前不处理大于 23 个字符的十进制形式的数字。
  • 它本质上是线程危险的。不要尝试在多线程环境中。
  • 一旦再次调用该函数,返回值就会被破坏。

我写这个纯粹是为了速度,而不是为了安全或方便。

【讨论】:

  • c 中没有称为 itoa 的函数:P
  • @EdHeal:它是一个常见的扩展,但它不是标准库的一部分,也没有得到普遍支持。
  • 你说得对,nixing printf 会提高性能,但程度与在 pinto 上使用涡轮增压器加速从 LA 到 NY 的行驶速度大致相同。 OP 的示例代码存在更大的性能问题。
  • 另外,我不会称其为“非线程安全”,我会称其为“线程敌对”。非线程安全意味着您可以通过在所有调用站点周围打一个互斥锁以线程安全的方式使用它。要在线程环境中安全地使用它,您必须做更多的事情。
【解决方案2】:

版本 2 基于 @UmNyobe 和 @wildplasser 的建议(见上文 cmets) 在线判断代码执行耗时 0.12 秒,占用内存 3.2 MB。 我自己在 1 到 5*10^5 的范围内检查了 2*10^5 int(input) 并执行了:

真正的 0m0.443s

用户0m0.408s

系统 0m0.024s

**请查看是否可以进行更多优化。

enter code here
/** Solution for the sum of the proper divisor problem from codechef **/
/** @ author dZONE **/
# include <stdio.h>
# include <math.h>
# include <stdlib.h>
# include <error.h>
# define SIZE 200000

inline int readnum(void);
void count(int num);

int pft[]={2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73,79,83,89,97,101,103,107,109,113,127,131,137,139,149,151,157,163,167,173,179,181,191,193,197,199,211,223,227,229,233,239,241,251,257,263,269,271,277,281,283,293,307,311,313,317,331,337,347,349,353,359,367,373,379,383,389,397,401,409,419,421,431,433,439,443,449,457,461,463,467,479,487,491,499,503,509,521,523,541,547,557,563,569,571,577,587,593,599,601,607,613,617,619,631,641,643,647,653,659,661,673,677,683,691,701,709};

unsigned long long int sum[SIZE];

int k = 0;

inline int readnum(void)
{
int num = 0;
char ch;
while((ch = getchar_unlocked()) != '\n')
{
    if(ch >=48 && ch <=57)
    {
        num = ch -'0' + 10*num;
    }
}
if(num ==0)
{
    return -1;
}
return num;
    }

    void count(int num)
    {
    unsigned int i = 0;
    unsigned long long tmp =0,pfac =1;
    int flag = 0;
    tmp = num;
    sum[k] = 1;
    for(i=0;i<127;i++)
    {   
    if((tmp % pft[i]) == 0)
    {
        flag =1;                // For Prime numbers not in pft table
        pfac =1;
        while(tmp % pft[i] == 0)
        {
            tmp =tmp /pft[i];
            pfac *= pft[i];
        }
        pfac *= pft[i];
        sum[k] *= (pfac-1)/(pft[i]-1);  
    }
}
if(flag ==0)
{
    sum[k] = 1;
    ++k;
    return;
}
if(tmp != 1)                        // For numbers with some prime factors in the pft table+some prime > 705
{
    sum[k] *=((tmp*tmp) -1)/(tmp -1);
}
sum[k] -=num;
++k;
return;
    }

    int main(void)
    {
    int i=0,terms =0,num = 0;

    setvbuf(stdin,(char*)NULL,_IOFBF,0);
    scanf("%d",&terms);
    while(getchar_unlocked() != '\n');
    while(terms--)
    {
    num = readnum();
    if(num ==1)
    {
        continue;   
    }
    if(num == -1)
    {
        perror("\n ERROR\n");
        return 0;
    }

    count(num);
        }
       i =0;
       while(i<k)
       {
    printf("%lld\n",sum[i]);
    ++i;
        }   
        return 0;
         }

【讨论】:

  • @UmNyobe 旧代码非常糟糕 :P 在在线法官上已经超时...但现在感谢您的建议,我成功了... :)
【解决方案3】:

//Prob 2 您现在的问题是不是更大……您只想找到除数?

我的第一个建议是在某种程度上缓存您的结果...但这可能需要您一开始的存储量的两倍:/。

您可以做的是事先生成一个素数列表 (using the sieve algorithm)。最好知道列表中的最大数 N 并生成所有素数直到他的平方根。现在对于您列表中的每个数字,您希望找到他作为因子乘积的表示,即

n = a1^p1 * a1^p2 *... *an^pn

那么sum of divisors就是。

((a1^(p1+1) - 1)/(a1 - 1))*((a2^(p2+1) - 1)/(a2-1))*...*((an^(pn+1) - 1)/(an-1))

要了解您有(对于 n = 8) 1+ 2 + 4 + 8 = 15 = (16 - 1)/(2 - 1)

这将大大提高速度,但整数分解(你真正在做什么)真的很昂贵......

编辑:

在您的链接中,最大值为 5000000,因此您最多有 700 个素数

简单的分解算法

void primedecomp(int number, const int* primetable, int* primecount,
      int pos,int tablelen){
    while(pos < tablelen && number % primetable[pos] !=0 )
       pos++;

    if(pos == tablelen)
      return

     while(number % primetable[pos] ==0 ){
        number = number / primetable[pos];
        primecount[pos]++;
     }
     //number has been modified
     //too lazy to write a loop, so recursive call
     primedecomp(number,primetable,primecount, pos+1,tablelen);
    
}

编辑:使用primepow = a; primepow = a*primepow;计算a^(n+1)而不是计数

在你有 hashmap 的 C++ 或 java 中会更干净。在最后 primecount 包含我上面提到的 pi 值。

即使它看起来很吓人,您也只能创建一次primetable。现在这个算法 在O(tablelen) 最坏的情况下运行O(square root(Nmax))。你的初始 循环在O(Nmax) 中运行。

【讨论】:

  • 实际上问题要求找到除数的总和。感谢有关主要因素的建议,我将使用它并尝试提出新版本的答案。
猜你喜欢
  • 2011-03-12
  • 1970-01-01
  • 1970-01-01
  • 2023-04-03
  • 2011-10-12
  • 1970-01-01
  • 1970-01-01
  • 2017-09-11
  • 1970-01-01
相关资源
最近更新 更多