数据结构求解素数环问题(JAVA版):将N个自然数(1~N),使得每相邻两数之和为素数,构成一个素数环!
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了数据结构求解素数环问题(JAVA版):将N个自然数(1~N),使得每相邻两数之和为素数,构成一个素数环!相关的知识,希望对你有一定的参考价值。
//【例4.3】 求解素数环问题。
public class PrimeRing
public PrimeRing(int n) //求1~n素数环
SeqList<Integer> ring = new SeqList<Integer>(n); //创建一个顺序表存储素数环
ring.append(new Integer(1)); //1添加到素数环中
SeqQueue<Integer> que = new SeqQueue<Integer>(n); //创建一个队列que// LinkedQueue<Integer> que = new LinkedQueue<Integer>(); //创建一个队列que
for (int i=2; i<=n; i++) //2~n全部入队
que.enqueue(new Integer(i));
// System.out.println(que.toString());
int i=0;
while (!que.isEmpty())
int k = que.dequeue().intValue(); //出队
// System.out.print("dequeue: "+k+"\t");
if (isPrime(ring.get(i)+k)) //判断是否为素数
i++;
ring.append(new Integer(k)); //k添加到素数环中
else
que.enqueue(new Integer(k)); //k再次入队
// System.out.println("队列: "+que.toString());
System.out.println("素数环: "+ring.toString());
public boolean isPrime(int k) //判断k是否为素数
if (k==2)
return true;
if (k<2 || k>2 && k%2==0)
return false;
int j=(int)Math.sqrt(k); //Math.sqrt(k)返回k的平方根值
if (j%2==0)
j--; //获得测试范围内的最大奇数
while (j>2 && k%j!=0)
j-=2;
return j<2;
public static void main(String args[])
new PrimeRing(10);
运行结果:素数环: (1, 2, 3, 4, 7, 10, 9, 8, 5, 6)
问题:上述的算法不完全,并且未求多个解,求完整(求多个解)算法!(N为10)
嗯。想一下。
这个是分别以每个自然数为起点,开始遍历,结果会有重复。
比如
(1, 2, 3, 4, 7, 10, 9, 8, 5, 6)
(6, 1, 2, 3, 4, 7, 10, 9, 8, 5)
import java.util.ArrayList;
import java.util.List;
public class PrimeRing
// 求1~n素数环
public PrimeRing(int n)
List<Integer> src = new ArrayList<Integer>();
List<Integer> dest = new ArrayList<Integer>();
for (int i = 1; i <= n; i++)
src.add(i);
loop(src, dest, n);
public void loop(List<Integer> src, List<Integer> dest, int n)
if (dest.size() == n)
Integer start = dest.get(0);
Integer end = dest.get(dest.size() - 1);
if (isPrime(start + end))
System.out.println(dest);
return;
for (int i = 0; i < src.size(); i++)
Integer element = src.remove(i);
if (dest.isEmpty())
dest.add(element);
else
Integer tmp = dest.get(dest.size() - 1);
if (isPrime(tmp + element))
dest.add(element);
else
src.add(i, element);
continue;
loop(src, dest, n);
src.add(i, element);
dest.remove(dest.size() - 1);
// 判断k是否为素数
public boolean isPrime(int k)
if (k == 2)
return true;
if (k < 2 || k > 2 && k % 2 == 0)
return false;
int j = (int) Math.sqrt(k); // Math.sqrt(k)返回k的平方根值
if (j % 2 == 0)
j--; // 获得测试范围内的最大奇数
while (j > 2 && k % j != 0)
j -= 2;
return j < 2;
public static void main(String args[])
new PrimeRing(10);
我运行了一下你的算法,能够得出所有解,但是我的题目是素数环,也就是首个数字和尾数字的和也应该要判断是否为素数。还有就是可以完善一下我上面那个用队列的算法么,麻烦啦,好的话可以给你分。
追答你上面那个是一个解的算法,要完善什么?
我得到结果有,有首和尾,相加不是素数的吗?
if (isPrime(start + end))
System.out.println(dest);
这个地方有判断啊,应该是没有错啊。
噢噢,看到了哈哈。你的算法挺好的,但是因为我这是java数据结构的课程设计,题目是完善我上面的算法,就是上面的算法的结果只有一个解,要完善的有:1.判断首尾两数之和为素数;2.给定初始序列,求多个解。 麻烦啦。
追答SeqList
SeqQueue
这两个类是什么?我就是找不到这两个类,才换了一下,
基本和你那个差不多,判断素数的方法也没有变。
这两个类 Seqlist 是顺序表类(线性表LList类的子类) SeqQueue顺序循环队列类(对列Queue的子类) 我可以发那几个类的声明给你的。
追答嗯,好,你把这两个类发给我,这个应该是你自己实现的,我找不到这两个类。
参考技术A 那就加上第一个元素和最后一个元素的判断就是!这还不全,把能写的都写出来了。这是什么书啊,求推荐啊! 参考技术B 纯算法,最讨厌算法的了。。。。素数求解及其优化
题目一:
请实现一个函数,对于给定的整型参数N,依次打印出小于N的素数。
解法一:试除法
由素数的定义我们很自然的会想到如下代码:
#include <stdio.h>
void print_prime(int n)
int i=0;
for(i=2;i<=n;i++)
int j=0;
for(j=2;j<i;j++)
if(0==i%j)
break;
if(j==i)
printf("%d\\t",i);
int main()
int num=0;
scanf("%d",&num);
print_prime(num);
return 0;
在上面的代码中,我们看到在判断素数时一直从2试除到n-1。这里从n/2之后的数到n-1的试除显然是多余的,比如某个数不能被3整除,必然不能被6整除。
优化1:
试除的范围优化到[2,n/2],这样一下子就将工作量减少了一半,代码如下:
void print_prime(int n)
int i=0;
for(i=2;i<=n;i++)
int j=0;
for(j=2;j<=i/2;j++) //修改部分
if(0==i%j)
break;
if(j==(i/2+1)) //修改部分
printf("%d\\t",i);
既然能将试除范围优化到[2,n/2],那么这个范围是不是还能优化呢?答案是可以的,在[2,n/2]这个范围里(√n,n/2]的试除也是多余的。因为因数是成对出现的,比如16可分解为:1和16 、2和8、4和4、8和2、16和1。这些因数里必然有一个小于等于4。所以只需试除小于等于√n的数就可以了。
优化2:
试除范围优化为[2,√n],代码如下:
#include <stdio.h>
#include <math.h>
void print_prime(int n)
int i = 0;
for (i = 2; i <= n; i++)
int j = 0;
for (j = 2; j <= sqrt(i); j++) //修改部分
if (0 == i%j)
break;
if (j >sqrt(i)) //修改部分
printf("%d\\t", i);
int main()
int num = 0;
scanf("%d", &num);
print_prime(num);
return 0;
上面所有的代码在找素数的时候是从2到n,在这个范围内除了2之外的偶数都不是素数,所以我们可以跳过这些偶数。
还有试除范围内除了2之外的偶数也是没有必要的,因为如果不能被2整除,必然不能被大于2的偶数整除。
优化3:
寻找素数时跳过偶数、试除范围跳过除2之外的偶数。代码如下:
void print_prime(int n)
int i = 0;
if (n >= 2) //修改部分
printf("%d\\t", 2);
for (i = 3; i <= n; i+=2) //修改部分
int j = 2;
if(0==i%j) //修改部分
break;
for (j = 3; j <= sqrt(i); j+=2) //修改部分
if (0 == i%j)
break;
if (j >sqrt(i))
printf("%d\\t", i);
其实在上面的代码中,试除范围内的一些数也是不必要的。比如判断101是否为素数时,要分别试除小于10的2和所有奇数,即2、3、5、7、9,其实对9的试除是不必要的。即对所有的非素数的试除是不必要的,因为非素数必然可分解为比它小的素数的乘积,既然它的质因数不能整除某个数,这个数必然也不能。故试除的范围可缩小到小于等于√n的所有素数。
优化4:
只试小于√n的素数
那么问题来了,要试除这些素数时必然要将前面求出的素数保存起来,开辟多大一块空间合适呢?因为n的大小未知,所以无法确定开辟多少空间。这里可以使用动态内存开辟空间,当空间不够时使用realloc()追加空间。
代码实现如下:
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#define LEN 10 //每次开辟空间的大小
void print_prime(int n)
int *p = (int*)calloc(sizeof(int),LEN);
if (p == NULL)
printf("out of memory !\\n");
exit(EXIT_FAILURE);
int i = 0, j = 1;
int count = 1;
if (n >= 2) //第一个素数,输出并保存起来
printf("%d\\t", 2);
p[0] = 2;
for (i = 3; i <= n; i += 2)
int k = 0;
while (p[k]>0 && p[k] <= sqrt(i)) //试除保存的素数
if (i%p[k] == 0)
break;
k++;
if (!p[k] || (p[k] > sqrt(i))) //将新求出的素数保存起来
printf("%d\\t", i);
if (j >= (LEN*count))
count++;
p = realloc(p, sizeof(int)*LEN*count);
if(p==NULL)
exit(EXIT_FAILURE);
p[j] = i;
j++;
free(p);
int main()
int num = 0;
scanf("%d", &num);
print_prime(num);
return 0;
解法二:筛法
这种方法求素数的思想就是,不断筛去最小的数的倍数。这个最小的数必然是素数。
比如最小的素数是2,去掉所有2的倍数;接下来最小的数是3,3就是素数,去掉所有的3的倍数;依次类推,直到最小的数小于等于√n为止。为什么是√n呢? 在上面的试除法中讲到只要试除小于等于√n的所有素数即可判断出小于等于n的所有素数,这里同样适用,只要去掉所有的小于等于√n的所有数的倍数,剩下的数就是小于等于n的所有素数。
代码如下:
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
void print_prime(int n)
int *arr=(int*)malloc(sizeof(int)*n);
if (arr == NULL)
printf("out of memory !\\n");
exit(EXIT_FAILURE);
int i = 0, j = 0;
for (i = 0; i < n - 1; i++) //初始化数组[2,n]
arr[i] = i + 2;
while (arr[j] <= sqrt(n)) //除数的范围
for (i = j + 1; i < n - 1; i++)
if (arr[i] % arr[j] == 0)//筛去arr[i]的倍数
arr[i] = 0;
j++;
while (!arr[j]) //确定最小数
j++;
for (i = 0; i < n - 1; i++) //打印素数
if (arr[i])
printf("%d\\t", arr[i]);
free(arr);
int main()
int num = 0;
scanf("%d", &num);
print_prime(num);
return 0;
上面的代码有一个很明显的缺陷就是开辟的空间过大,如何来解决这个问题呢?
上面代码所开辟的空间为int型,占用空间太多,我们可以构造一个bool型数组,以下标来存储数据,这样就节省了75%的空间。
优化1:
构造bool型数组,以下标来存储数据,每个数只占一个字节。
代码如下:
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <math.h>
void print_prime(int n)
bool *arr=(bool*)malloc(sizeof(bool)*n);
if (arr == NULL)
printf("out of memory !");
exit(EXIT_FAILURE);
int i = 0, j = 2;
for (i = 0; i <= n; i++)//初始化数组为true
arr[i] = true;
while (j <= sqrt(n)) //除数的范围
for (i = j + 1; i <= n; i++)
if (i%j == 0)
arr[i] = false; //筛除
j++;
while (!arr[j]) //寻找最小数
j++;
for (i = 2; i <= n; i++) //打印素数
if (arr[i])
printf("%d\\t", i);
free(arr);
int main()
int num = 0;
scanf("%d", &num);
print_prime(num);
return 0;
上面的代码使用bool类型占一字节来存储数据,想到这里,我们是不是可以用位来存储数据,一个字节有8位,每位节用bool值来表示这样空间利用率会大大的提高。
优化2:
构造定长的byte数组,用bit位存储数据
代码如下:
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
//将ch的第position位 置0
unsigned char set_bit_0(unsigned char ch, int position)
return ch & (~ ( (unsigned char)pow(2, position) ) );
//找arr指向的内存第position个位起,第一个不为0的比特位
int check_bit_1(int num,const unsigned char *arr, int pos)
while (pos<= num)
if (( (arr[pos / 8] >> (pos % 8) ) & 1) == 1)
return pos;
pos++;
return 0;
//求解素数
void print_prime(int n)
unsigned char *arr = (unsigned char*)malloc(sizeof(unsigned char)*n / 8+1);
if (arr == NULL)
printf("out of memory !\\n");
exit(EXIT_FAILURE);
int i = 0,j=2;
for (i = 0; i < n/8+1; i++) //将bit数组置为全1
arr[i] = 0xff;
while (j <= sqrt(n)) //筛除范围
for (i = j + 1; i <= n; i++)
if (i%j == 0) //将非素数对应的位 置0
arr[i / 8] = set_bit_0( arr[i / 8] , i % 8);
j++;
j = check_bit_1(n,arr, j);
if (j == 0)
break;
for (i = 2; i <= n; i++) //打印素数
i = check_bit_1(n,arr, i);
if (i == 0)
break;
printf("%d\\t", i);
int main()
int num = 0;
scanf("%d", &num);
print_prime(num);
return 0;
题目二:
从小到大依次打印N个素数
解法一:试除法
经过题目一的求解优化,这里直接给出试除法优化的终极版:试除保存起来的素数。
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
void print_N_prime(int num)
int count = 0;
int i = 3,j=0,flag=2;
int *arr = calloc(num,sizeof(int));
if(arr==NULL)
printf("out of memory !\\n");
exit(EXIT_FAILURE);
arr[0] = 2;
count = 1; //统计素数的个数
while (count<num)
for (j = 0; arr[j] <= sqrt(i) && arr[j] ; j++) //试除判断素数
if (i%arr[j] == 0)
break;
if (arr[j] > sqrt(i))
arr[count]=i;
count++;
i++;
for (i = 0; i < count; i++) //打印素数
printf("%d\\t", arr[i]);
int main()
int n = 0;
scanf("%d", &n);
print_N_prime(n);
return 0;
解法二:筛法
筛法需要容器,这个容器要多大呢?由素数定理可以近似求出素数的分布范围。如0~x中有x/lnx个素数,反推即可求出n个素数的分布范围,由于这只是近似,把容器再扩大30%,应该足够了。
求出范围后解法与题目一类似,只需在输出素数时控制输出个数即可。
注:本文借鉴了program_think在CSDN的博客,求解素数的N种境界(N>10)
http://blog.csdn.net/program_think/article/details/7032600
以上是关于数据结构求解素数环问题(JAVA版):将N个自然数(1~N),使得每相邻两数之和为素数,构成一个素数环!的主要内容,如果未能解决你的问题,请参考以下文章