一条道简单的算法引发的思考

996 阅读3分钟

我报名参加金石计划1期挑战——瓜分10万奖池,这是我的第2篇文章,点击查看活动详情

前言

前两天有个小兄弟问我一道数据结构简单的算法题怎么做,题目如下:

d611cedcd46b217f142e99a1d7573ed.jpg

一看到题目,心里想:这不就是简单的求素数(除了1和它本身,其他数均无法整除的数)题目吗,于是我很快就给他写了出来,后面发现才发现没那么简单。。。

穷举解法

看到题目后,这不就是直接暴力破解就好了吗,于是我很快就写了出来了~

# include<stdio.h>
# include<math.h>

// 求素数
int primeNumber(long int num) {
    int result = 0;
    int isInsert = 1;
    
    if (num > 10000000) {
        return 0;
    }
    
    for(long int i = 2; i <= num; i++) {
        isInsert = 1;
        // 穷举整除
        for (long int j = 2; j < i;j++) {
            if (i % j == 0) {
                isInsert = 0;
                break;
            }
        }
        if (isInsert == 1) {
            result++;
        }
    }
    printf("%d\n", result);
    return result;
}

int main() {
    long int inNum;
    
    if (scanf("%ld", &inNum)) {
        primeNumber(inNum);
    }
    
    return 0;
}

然后我很快就写好给那位小兄弟参考了,没过一会那个小兄弟又来找我了。他说:不行,执行超时了,而且这题还要用两种不同的解法出来。
原来我忽略了时间限制,以为作业而已,只要解答出来就行了。这题目还有一个时间和内存的限制。

image.png

image.png

去平方根

上面的穷举法在统计10000以内的素数还可以,但是遇到十万,百万,千万的统计时,穷举的次数就会呈指数级的增长,于是我就上网冲浪去找数学里求素数的优解法,找到了一个开平方根的解法:原理

#include <stdio.h>
#include <math.h>
#include <time.h>

// 求素数
int primeNumber(long int num)
{
  int result = 0;
  int isInsert = 1;

  if (num > 10000000)
  {
    return 0;
  }

  for (long int i = 2; i <= num; i++)
  {
    isInsert = 1;
    // 穷举整除
    for (long int j = 2; j <= (int)sqrt(i); j++)
    {
      if (i % j == 0)
      {
        isInsert = 0;
        break;
      }
    }
    if (isInsert == 1)
    {
      result++;
    }
  }
  printf("%d\n", result);
  return result;
}

int main()
{
  long int inNum;
  int startTime;
  int endTime;

  if (scanf("%ld", &inNum))
  {
    startTime = clock();
    primeNumber(inNum);
    endTime = clock();
    printf("time: %dms\n", (endTime - startTime) / 1000);
  }

  return 0;
}

image.png

看了一下结果,计算一千万以内的数时,依然需要15秒左右的时间,一百万则需要600ms。
依然是没有达到要求。。。

image.png

继续优化

当继续然后我发现规律,素数除了2以外,其他数都是奇数,因为偶数必然能被2整除~
那么继续来优化吧~

#include <stdio.h>
#include <math.h>
#include <time.h>

// 求素数
int primeNumber(long int num)
{
  int result = 0;
  int isInsert = 1;
    
  if (num > 10000000)
  {
    return 0;
  }
  if (num >= 2) {
      result++;
  }
  for (long int i = 3; i <= num; i+=2)
  {
    isInsert = 1;
    // 穷举整除
    for (long int j = 2; j * j <= i; j++)
    {
      if (i % j == 0)
      {
        isInsert = 0;
        break;
      }
    }
    if (isInsert == 1)
    {
      result++;
    }
  }
  printf("%d\n", result);
  return result;
}

int main()
{
  long int inNum;
  int startTime;
  int endTime;

  if (scanf("%ld", &inNum))
  {
    startTime = clock();
    primeNumber(inNum);
    endTime = clock();
    printf("time: %dms\n", (endTime - startTime) / 1000);
  }

  return 0;
}

image.png

得出的结果与上面的几乎没有什么差别,比较在大数面前/2的操作也只是杯水车薪,时间复杂度摆在那里。所以必须要再换一种方法去去解题。

枚举法

原理

#include <stdio.h>
#include <math.h>
#include <time.h>
#include <stdbool.h>

// 求素数
bool isPrime(int x)
{
  for (int i = 2; i * i <= x; ++i)
  {
    if (x % i == 0)
    {
      return false;
    }
  }
  return true;
}

int primeNumber(int n)
{
  int ans = 0;
  for (int i = 2; i < n; ++i)
  {
    ans += isPrime(i);
  }
  printf("%d\n", ans);
  return ans;
}

int main()
{
  long int inNum;
  int startTime;
  int endTime;

  if (scanf("%ld", &inNum))
  {
    startTime = clock();
    primeNumber(inNum);
    endTime = clock();
    printf("time: %dms\n", (endTime - startTime) / 1000);
  }

  return 0;
}

image.png

在尝试了一晚上之后,还是没有解决出来... 在200毫秒,64MB的内存,写出一个能统计1千万以内的素数的算法(估计需要时间复杂度<= O(n)且空间复杂度 < O(n))

思考

最后,还是没解出来,这位小兄弟就交作业了~
虽然没写出来,但是还是引发了一些思考:

  • 当我们需要遇到难题的时候,我们常常会说要迎难而上,但是实际上又有多少人会认真研究。很多人都是只要做到了行了,没有必要优化。等到出问题时再来想着优化,那时候事情可能已经变得不可控了。最终可能会演变成给后面接盘的小伙伴一个大坑。

  • 同样,在我工作中遇到的很多领导和开发,遇到问题时大部分都会直接说:看看网上有没有现成的库直接拿来用。又没有考虑到以后的维护性与扩展性,为了赶进度而不管业务需求去使用现成的库,可能会无形中埋下一些严重的问题。

  • 我经常在想:工作了也有好几年了,当初刚刚学习编程的热情现在还有多少分,刚刚接触编程的时候,没遇到一个新的特性或功能时,都会非常地兴奋。每次自己写出一些有意思的程序时都会非常高兴,觉得很有意思。到了工作时候,工作经历多了,觉得大部分时间都在拧螺丝,都是一些流水线的工作一样。在遇到这个问题是不去选择躺平,而是在业余时间继续自己去研究自己喜欢的方向。

  • 或许这才是我们程序猿应该做的事情吧,保持自己当初的热情,去发掘和研究,发现有意思的东西。尽管这可能是与现实背道而驰,但是不忘初心,坚持自己的爱好。或许能让自己走的更远。