素数环-谈代码优化

昨天在博问里面看到的一道算法题,原题如下:

给出一个N(0<N<20),在1~N的所有排列中,满足相邻两个数之和是素数的排列输出

比如当N = 4时,满足条件的素数环有如下几种

1 2 3 4
1 4 3 2
2 1 4 3
2 3 4 1
3 2 1 4
3 4 1 2
4 1 2 3
4 3 2 1

常规的做法是,找出这N个数的所有排列,然后依次检查每个排列,筛选出符合条件的排列即可。求排列可以用回溯法的排列树模型,筛选就按照题目要求即可,判断素数的算法也有很多,选择一个即可。注意不要忘记最后一个元素和第一个元素的检测。优化前的代码如下:

代码

题目不难,做完以后,我发现有很多可以优化的地方,可以大幅提高速度。

1. 首先,找出所有排列并逐个检查,这是很浪费时间的,更高效的方法是,一边排列一边检查,这样可以提早发现不满足条件的候选解,提早剪枝,避免不必要的搜索,例如当N=10时,排列到1234的时候,满足条件,下一次选择5,序列变为12345,由于4 + 5 = 9,非素数,所以后面不用再排列了,也就是从当前位置开始,以5为根的子树可以不用再搜索了,直接跳到6,序列变为12346,由于4 + 6 = 10,非素数,同样舍弃6为根的子树。下一次搜索变成12347,这回满足条件,继续排列下一个元素,如此直到10个元素全部排列完成。代码如下:a是储存排列的数组,n是元素个数,t用来控制递归过程。

复制代码
 1  void  PrimeCircle( int  a[],  int  n,  int  t)
 2  {
 3       if (t  ==  n)
 4      {
 5          Output(a, n) ;  //  找到一个解
 6      }
 7       else
 8      {
 9           for ( int  i  =   1 ; i  <=  n; i ++ )
10          {
11              a[t]  =  i ;
12               if (IsOk(a))  //  检查当前值,满足条件才继续
13                  PrimeCircle(a, n, t  +   1 ) ;
14          }
15      }
16  }
复制代码

2. 再看题目的输入范围,1 < N < 20,由于输入规模比较小,所以考虑使用查表法来判定素数,查表法是典型的以空间换时间的方法。20以内两个数之和最大是18 + 19 = 37,而37以内的素数分别是2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37,我们可以定义一个38个元素的数组,以i为数组下标。当i为素数时,令a[i] = 1,否则a[i] = 0。这样,要判断一个数是否为素数时,直接判断a[i]是否为1即可。对应的数组如下:

复制代码
 1  int  prime[ 38 =  
 2  {
 3       0 0 1 1 0 1 0
 4       1 0 0 0 1 0 1
 5       0 0 0 1 0 1 0
 6       0 0 1 0 0 0 0
 7       0 1 0 1 0 0 0 ,
 8       0 0 1 ,
 9  } ;
复制代码

判断i是否为素数的代码也很简单

1  if  (prime[i]  ==   1 )   // 素数
2  {
3       //  do something
4  }

3. 再考虑输入的特点,如果输入N是奇数的话,由于起点从1开始,那么1-N之间一共有N / 2个偶数,N / 2 + 1个奇数,也就是奇数的个数比偶数多一个,那么把这N个数排成一个环,根据鸽巢原理,必然有两个奇数是相邻的,而两个奇数之和是偶数,偶数不是素数,所以我们得出结论,如果输入N是奇数的话,没有满足条件的排列。这样当N是奇数的时候,直接返回即可。如果1-N之间每个数输入的几率相同,这个判断可以减少一半的计算量。

1  if (n  &   1 //  奇数无解,直接返回
2       return  ;
3 

4. 扩展一下第三点,可以发现,任何一个满足条件的排列都有一个共同点:相邻的两个数奇偶性必然不同,原因是:两个奇数之和或者两个偶数之和都是偶数,而偶数一定不是素数,所以在选取当前元素的时候,比较一下它和前一个元素的奇偶性。再做决定,可以减少一部分计算量。

由 于奇数 + 偶数 = 奇数, 而奇数的二进制表示中,最低位是1, 所以有下面的代码, 其中curValue是当前值, a[lastIndex]是前一个值.

1  if ( ! ((curValue  +  a[lastIndex])  &   1 ))  //  相邻的数奇偶性必然不同
2       return   false  ;
3 

经过上面的优化,代码如下,应该会比原来快很多。还有什么地方可以优化么?欢迎讨论!

代码

 

本文转自zdd博客园博客,原文链接:http://www.cnblogs.com/graphics/archive/2010/03/27/1698403.html,如需转载请自行联系原作者

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
根据引用\[1\]和引用\[2\]的代码,这是一个解决"素数"问题的算法。该问题要求找到一个由1到n这些数组成的,使得中相邻两个数之和都是素数。算法使用了深度优先搜索的思想,先选定一个数,然后递归地选择下一个数,直到找到一个满足条件的。在递归过程中,使用了一个数组来记录已经选择的数,以及一个布尔数组来标记是否已经访问过某个数。同时,还使用了一个函数来判断一个数是否是素数。 根据引用\[3\]的输出示例,当n=4时,可能的素数有8个,分别是: 1: 1 2 3 4 2: 1 4 3 2 3: 2 1 4 3 4: 2 3 4 1 5: 3 2 1 4 6: 3 4 1 2 7: 4 1 2 3 8: 4 3 2 1 因此,对于问题"1358 - 素数",当n=4时,总共有8个满足条件的素数。 #### 引用[.reference_title] - *1* [【DFS练习】素数](https://blog.csdn.net/z135733/article/details/122742948)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insert_down28v1,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* *3* [1358 - 【提高】素数](https://blog.csdn.net/PanDaoxi2020/article/details/126597717)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insert_down28v1,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值