您的位置:首页 > 其它

用递归程序求解随机产生一个正整数n(n>=100000),确定n是否是它所有因子之和

2017-09-26 11:31 1116 查看
用递归程序求解随机产生一个正整数n(n>=100000),确定n是否是它所有因子之和(完数)

了解什么是因子:因子就是所有可以整除这个数的数,不包括这个数自身,例如:6的因子为1,2,3。

完数:即某正整数的所有因子之和等于该数,即为完数。例:6的因子为1,2,3且6=1+2+3;即称6为完数。

进行分析,要求利用递归实现,递归是一种直接或间接引用自身的定义方法。一个合法的递归定义包括两部分:基础情况和递归部分。基础情况以直接形式明确列举新事物的若干简单对象,递归部分给出有简单(或较简单)对象定义新对象的条件和方法。

本题要求编写一个算法实现该功能,算法讲究性能,即时间复杂度和空间复杂度,一个好的算法不仅能够准确运行,而且他的时间复杂度很低。算法具有五个特性:a,有0个或者多个输入;b,至少有1个或者多个输出;c,算法具有确定性;d,算法的指令有限;e,算法具有可行性。

对于该问题,首先先求一个整数的所有因子,在程序中即对该数依次进行求模运算,结果为0则代表是因子。

/**
* 求一个正整数的所有因子之和等于该数
* @author zclong
*
*/
public class test {

public static void main(String[] args) {
//产生随机数
int value = (int) (Math.random()*1000000);
//计算因子之和
int sum = 0;
//定义一个list数组存储因子
List<Integer> list = new ArrayList();
//对随机数进行遍历
long startTime = System.currentTimeMillis();
for (int j = 1; j <= value-1; j++) {
if(value % j == 0) {
list.add(j);
sum = sum + j;
}
}
long endTime = System.currentTimeMillis();
if(sum == value) {
System.out.println("因子个数" + list.size() + ", 运行时间:" + (endTime-startTime) + "ms");
System.out.println(value + "是完数,其因子为" + list.toString());
}else {
System.out.println("因子个数" + list.size() + ", 运行时间:" + (endTime-startTime) + "ms");
System.out.println(value + "不是完数,其因子为" + list.toString());
}
}
}




上面是普通的计算方法,从程序和结果可以看出,若是超过十万的整数,它将会计算十多万次,花费相当大的内存消耗和时间。

我们再用递归进行计算。

public class GetFactor {
//产生随机数
final int value = (int) (Math.random()*10000);
//计算因子之和
int sum = 0;
//定义一个list数组存储因子
List<Integer> list = new ArrayList();
//执行次数
int count = 0;

@Test
public void test() {
GetFactor factor = new GetFactor();
long startTime = System.currentTimeMillis();
factor.getFactor(1); // 调用执行方法,从1开始检验是否是因子
long endTime = System.currentTimeMillis();
System.out.println("运行时间:" + (endTime-startTime) + "ms");

}

public void getFactor(int n) {
count++;
if(n > value-1) {
if(sum == value) {
System.out.println("因子个数" + list.size() + ", 执行次数" + count);
System.out.println(value + "是完数,其因子为" + list.toString());
}else {
System.out.println("因子个数" + list.size() + ", 执行次数" + count);
System.out.println(value + "不是完数,其因子为" + list.toString());
}
return;
} else {
if(value % n == 0) {
list.add(n);
sum = sum + n;
}
getFactor(n+1);
}
}

}






4000

上面的递归运算可以看出当进行运算的算超过10000。便出现了内存溢出的现象。

递归的缺点:

a.递归由于是函数调用自身,而函数调用是有时间和空间的消耗的:每一次函数调用,都需要在内存栈中分配空间以保存参数、返回地址以及临时变量,而往栈中压入数据和弹出数据都需要时间。->效率

b.递归中很多计算都是重复的,由于其本质是把一个问题分解成两个或者多个小问题,多个小问题存在相互重叠的部分,则存在重复计算,如fibonacci斐波那契数列的递归实现。->效率

c.调用栈可能会溢出,其实每一次函数调用会在内存栈中分配空间,而每个进程的栈的容量是有限的,当调用的层次太多时,就会超出栈的容量,从而导致栈溢出。->性能

为了解决递归出现的问题,我们可以通过代数变换,减少子问题的个数。或者通过预处理,以空间换时间的方式进行处理。

我们选择预处理, 先对要求的正整数进行开平方根处理。

我们利用对该正整数求平方根来大大减少递归的次数,因为假设存在一个正整数num,使得num=k*M;且k不在(1,sqrt(num))之间,且M为正整数,那么M必在(1,sqrt(num))之间,否则k*M> sqrt(num)* sqrt(num),与num=k*M矛盾。所以我们只要求(1,sqrt(num))之间的因子即可,对应的因子也就出来了。

拿到要求的正整数的平方根之后,对其进行判断,当进行运算的数大于平方根数即结束,计算所有因子之和,若等于该数,即为完数;否则不断进行递归运算;

public class Test1 {
List<Integer> list = new ArrayList();
int k = 0;
int key  =0;
//产生随机数
int value = (int) (Math.random()*100000000);

@Test
public void factors(){
list.add(1);
if(1 == value) return;
int count1 = 1;// 1 与 N 必是(不包括N)
final int sqrt_N = (int)Math.sqrt(value); // 进行求平方根
long startTime = System.currentTimeMillis();
recursive(sqrt_N, 2, 1);// 从2开始判断是否是因子,因为1是所有正整数的因子,因子之和的初始值sum=1
long endTime = System.currentTimeMillis();
System.out.println("所用时间为:" + (endTime - startTime) + "ms");
}

public void recursive(int sqrt_N, int m, int sum) {
k++;
if(m > sqrt_N) {

Collections.sort(list); // 对list数组进行排序

System.out.println("随机数为:" + value + ", 运行次数:" + k);
System.out.println("其因子为" + list.toString() + ",因子个数:" + list.size());

if(sum == value) {
System.out.println("其和为:" + sum + ", 是完数");
}else {
System.out.println("其和为:" + sum + ", 不是完数");
}
}else {
if(0 == value % m) { // 判断是否整除为因子
list.add(m);
key = value / m; // 求出另一个因子
list.add(key);
sum = sum + m + key; // 求因子之和
recursive(sqrt_N,m + 1, sum);  // 递归
}else {
recursive(sqrt_N,m + 1, sum); // 递归
}
}
}
}




7. 上述做法将一个1000000的数化为1000的数,这样做不仅不会使内存溢出,使得子问题的个数减少,而且节省了时间,大大降低了时间复杂度。

8. 通过递归划分子问题进行子问题的求解,使程序变得简单。但是若递归不当则会引起内存溢出,此时可以进行预处理或者减小子问题的个数,以空间换时间来实现,来解决递归内存溢出的问题。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐