用递归程序求解随机产生一个正整数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则代表是因子。
上面是普通的计算方法,从程序和结果可以看出,若是超过十万的整数,它将会计算十多万次,花费相当大的内存消耗和时间。
我们再用递归进行计算。
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))之间的因子即可,对应的因子也就出来了。
拿到要求的正整数的平方根之后,对其进行判断,当进行运算的数大于平方根数即结束,计算所有因子之和,若等于该数,即为完数;否则不断进行递归运算;
7. 上述做法将一个1000000的数化为1000的数,这样做不仅不会使内存溢出,使得子问题的个数减少,而且节省了时间,大大降低了时间复杂度。
8. 通过递归划分子问题进行子问题的求解,使程序变得简单。但是若递归不当则会引起内存溢出,此时可以进行预处理或者减小子问题的个数,以空间换时间来实现,来解决递归内存溢出的问题。
了解什么是因子:因子就是所有可以整除这个数的数,不包括这个数自身,例如: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. 通过递归划分子问题进行子问题的求解,使程序变得简单。但是若递归不当则会引起内存溢出,此时可以进行预处理或者减小子问题的个数,以空间换时间来实现,来解决递归内存溢出的问题。
相关文章推荐
- 一个正整数有可能可以被表示为 m(m>=2) 个连续正整数之和,编写一个程序,输入一个正整数,然后找出符合这种要求的所有 连续正整数序列,若不存在这种序列,则打印None。
- python程序2(递归查找某一个文件夹下所有的文件是否含有某个特定的字符串,并打印该文件名)
- 判断正整数m是否为完全数(如果一个正整数m的所有小于m的因子(包括1)加起来正好等于m本身,那么这个数就称为完全数)
- 一个正整数有可能可以表示为n(n>=2)个连续的正整数之和,如:15=1+2+3+4+5,15=4+5+6,15=7+8 请编写程序,根据输入的任何一个正整数,找出符合这种要求的所有连续正整数序列。
- zyc从小就比较喜欢玩一些小游戏,其中就包括画一笔画,他想请你帮他写一个程序,判断一个图是否能够用一笔画下来。 规定,所有的边都只能画一次,不能重复画。 输入 第一行只有一个正整数N(N<=
- 今天开始学Java 输入一个正整数,按照从小到大的顺序输出它的所有质数的因子
- 一个正整数有可能可以被表示为n(n>=2)个连续正整数之和--算法求解
- 写一个程序,对于一个正整数,输出它所有可能的连续自然数(两个以上)之和的算式
- 12.输入一个正整数,输出它所有的因子
- 随机产生一些数传递给一个函数,写程序找出并维护这些数的中位数。
- 输入一个正整数,按照从小到大的顺序输出它的所有质数的因子
- pyhton 查找一个数的所有因子 以及 判断一个数是否是质数 两个小脚本
- 【笔试】57、确定一个字符串的所有字符是否都不同
- P53.37(设计一个程序,从键盘输入一个正整数M,判断该正整数是否左右对称,若对称,则输出yes,否则输出no。)
- 编写一个程序,用于接收用户输入的数,然后显示从0到该数为止表示的所有字符,询问用户是否愿意再次继续同样的过程
- <C语言>编写一个程序,该程序读取输入直到遇到#字符,然后报告读取的空格数目、读取的换行数目以及读取的所有其他字符数目。
- 给定一个正整数n,则在n所有的分解式中,求因子乘积最大的一个分解及此乘积。
- java笔试题---程序产生一个int数组,长度为100,并向其中随机插入1-100,并且不能重复。
- 一个c程序产生6位数的随机密码
- 设计程序,单击【随机数】按钮,使用Math对象的random函数产生一个0-100之间(含0-100)的随机整数,并在对话框中显示,如下图。单击【计算】按钮,计算该随机数的平方、平方根和自然对数,保留两位小数,并在对话框中显示,如下图。