【问题标题】:Why does this code go into infinite loop为什么这段代码进入无限循环
【发布时间】:2013-04-22 07:06:56
【问题描述】:

下面的这个函数检查一个整数是否是素数。

我正在运行一个从 3 到 2147483647 的 for 循环(+ve 的 long int 限制)。

但是这段代码挂了,不知道为什么?

#include<time.h>
#include<stdio.h>
int isPrime1(long t)
{
    long i;
    if(t==1) return 0;
    if(t%2==0) return 0;
    for(i=3;i<t/2;i+=2)
    {
        if(t%i==0) return 0;
    }
    return 1;
}
int main()
{
    long i=0;
    time_t s,e;

    s = time(NULL);
    for(i=3; i<2147483647; i++)
    {
        isPrime1(i);
    }
    e = time(NULL);
    printf("\n\t Time : %ld secs", e - s );
    return 0;
}

【问题讨论】:

  • 你确定它真的挂了,不是说它只需要很长时间才能执行吗?
  • 你确定它实际上是卡在一个循环中,而不是真的很长的执行时间?
  • 我不认为这是一个无限循环。您只需拥有超过 2147483647 个循环,仅此而已。太重了
  • 也许这对您的计算机来说太多了,您是否尝试过使用较小的数字,例如 for(i=3; i&lt;100; i++)
  • 这东西的复杂性在我看来是 n^2,所以 2147483647 可能正在推动它。

标签: c long-integer


【解决方案1】:

它最终会终止,但需要一段时间,如果您在内联 isPrime1 函数时查看循环,您会看到类似:

for(i=3; i<2147483647; i++)
   for(j=3;j<i/2;j+=2)

大约是 n*n/4 = O(n^2)。您的循环行程次数过多。

【讨论】:

    【解决方案2】:

    这取决于系统和编译器。在 Linux 上,使用 GCC 4.7.2 并使用 gcc -O2 vishaid.c -o vishaid 编译,程序立即返回,编译器通过删除它们来优化对 isPrime1 的所有调用(我使用 gcc -O2 -S -fverbose-asm 检查生成的汇编代码,然后使用 main甚至不打电话给isPrime1)。而 GCC 是对的:因为 isPrime1 没有副作用并且没有使用它的结果,所以可以删除它的调用。那么for循环体是空的,所以也可以优化。

    要吸取的教训是,在对优化的二进制文件进行基准测试时,最好在代码中产生一些真正的副作用。

    此外,算术告诉我们,如果某个i 没有小于其平方根的除数,则它是素数。所以更好的代码:

    int isPrime1(long t) {
      long i;
      double r = sqrt((double)t);
      long m = (long)r;
      if(t==1) return 0;
      if(t%2==0) return 0;
      for(i=3;i <= m;i +=2)
        if(t%i==0) return 0;
      return 1; 
    }
    

    在我的系统上(x86-64/Debian/Sid 和 i7 3770K Intel 处理器,运行该程序的核心是 3.5GHz)long-s 是 64 位。所以我编码了

    int main ()
    {
      long i = 0;
      long cnt = 0;
      time_t s, e;
    
      s = time (NULL);
      for (i = 3; i < 2147483647; i++)
        {
          if (isPrime1 (i) && (++cnt % 4096) == 0) {
            printf ("#%ld: %ld\n", cnt, i);
            fflush (NULL);
          }
        }
      e = time (NULL);
      printf ("\n\t Time : %ld secs\n", e - s);
      return 0;
    }   
    

    大约 4 分钟后,它仍在打印很多行,包括

    #6819840: 119566439
    #6823936: 119642749
    #6828032: 119719177
    #6832128: 119795597
    

    我猜这需要几个小时才能完成。 30 分钟后它仍在吐痰(缓慢)

    #25698304: 486778811
    #25702400: 486862511
    #25706496: 486944147
    #25710592: 487026971
    

    实际上,该程序需要 4 小时 16 分钟才能完成。最后的输出是

    #105086976: 2147139749
    #105091072: 2147227463
    #105095168: 2147315671
    #105099264: 2147402489
    
       Time : 15387 secs
    

    顺便说一句,这个程序仍然非常低效:bsdgames 包中的 primes 程序 /usr/games/primes 的响应速度要快得多

    % time /usr/games/primes 1 2147483647 | tail
    2147483423
    2147483477
    2147483489
    2147483497
    2147483543
    2147483549
    2147483563
    2147483579
    2147483587
    2147483629
    /usr/games/primes 1 2147483647 
         10.96s user 0.26s system 99% cpu 11.257 total
    

    它仍然打印了 105097564 行(大部分被 tail 跳过)

    如果您对质数生成感兴趣,请阅读几本数学书籍(如果您对效率感兴趣,这仍然是一个研究主题;您仍然可以获得该主题的博士学位。)。从 Wikipedia 上的 sieve of erasthothenesprimality test 页面开始。

    最重要的是,首先使用调试信息和所有警告编译您的程序(即 Linux 上的gcc -Wall -g)并学习使用您的调试器(即gdb on Linux)。然后你可以在大约一两分钟后中断你调试的程序(在gdb下使用Ctrl-C,然后让它继续使用cont命令到gdb),然后观察main中的i计数器是缓慢增加。也许还要求提供分析信息(将-pg 选项设置为gcc,然后使用gprof)。在编写复杂的算术代码时,阅读关于它们的优秀数学书籍是非常值得的(素性测试是一个非常复杂的主题,是大多数密码算法的核心)。

    【讨论】:

    • 这只是因为在这种情况下他没有对返回值做任何事情,因为这是一个最小的例子。对于一个真正的程序,他可能对返回值感兴趣;)
    • 我知道,但我想吸引原始发帖人了解一些关于优化和基准测试的有趣观点。
    【解决方案3】:

    这是测试素数的一种非常低效的方法,这就是它似乎挂起的原因。 在网上搜索更高效的算法,例如埃拉托色尼筛法

    【讨论】:

      【解决方案4】:

      这里试试这个,看看是不是真的死循环了

      int main()
      {
          long i=0;
          time_t s,e;
      
          s = time(NULL);
          for(i=3; i<2147483647; i++)
          {
              isPrime1(i);
      
              //calculate the time execution for each loop
              e = time(NULL);
              printf("\n\t Time for loop %d: %ld secs", i, e - s );
          }
      
          return 0;
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2014-02-13
        • 2021-09-18
        • 2022-08-19
        • 2012-04-19
        • 1970-01-01
        • 2013-08-10
        • 2013-07-25
        • 1970-01-01
        相关资源
        最近更新 更多