和AnC熬到三点半,只是为了解决一个给猫出的VB入门考试题:
给出一个数,编程分解其质因数。
AnC说猫将在他的指导下完成这个题目,如果成功了就加她到Maze的开发组。我提议我和AnC都写个程序,熬夜大战拉开序幕。AnC最初的想法就是从2开始,对每个数进行试除;随即他又将算法改进成仅对2和所有奇数试除。我的想法是从2开始进行计算,循环排除所有合数(法2)。这样不必如AnC的方法测试很多无意义的合数。但是根据理论估计和实际程序测试结果,这种方法实际上要比第一种方法慢。之后我对第一种试除法进行了一些优化(法1),运行速度有较大提高。今天上午我又写出一种新的算法(法3)。以下列出这三种算法(按速度由快至慢排列)。附所有源代码和可执行文件。
1. 最大试除法
循环测试试除数从2到1/2待分解数的所有值。如果待分解数可被试除数整除,则通过对数方法估测待分解数最大可能最大是多少试除数的连乘积,然后从此最小数即1开始向上测试,直到找到最大可整除试除数连乘积(先找出最小非整除试除数连乘积,再减一即是最大值),然后执行除法,将待分解数设置为商值,则增加试除数值。
本方法与基本试除法的最大区别在于对最大连乘积的估测,这种方法比通过大循环试除数查找(即每个大循环仅能执行一次查找)的速度要快很多。
2. 排除试除法
循环测试试除数从2到1/2待分解数的所有值。首先测试如果试除数在数组中标记为合数则不再继续试除。如果待分解数可被试除数整除,则标记试除数的所有整倍数直到最大可能值(用户输入的原待分解数除以试除数),然后执行除法,将待分解数设置为商值,试除数不变继续在下一循环试除;如果待分解数不能被试除数整除,则增加试除数值。
本方法与基本试除法的最大区别在于对合数的穷举和排除,从而减少了试除工作量。本方法使用大循环试除数查找。记录使用动态数组。由于本方法需要穷举所有可能合数,因此所需数组大小与用户输入的原待分解数相当。如果该值过大会导致内存溢出。
3. 回验试除法
循环测试试除数从2到1/2待分解数的所有值。首先测试如果试除数可以被测试过的试除数整除且不相等则不再继续试除。如果待分解数可被试除数整除,则记录此试除数(已记录者不再记录)然后执行除法,将待分解数设置为商值,试除数不变继续在下一循环试除;如果待分解数不能被试除数整除,则增加试除数值。
本方法与基本试除法的最大区别在于对曾试除数的回验,从而达到排除合数的目的,减少了试除工作量。本方法使用大循环试除数查找。记录使用动态数组。虽然本方法使用数组远比法2小,但如果数过大同样可能导致内存溢出。
由于今天白天我又对程序进行了修改优化,所以之前的测试结果已失去意义。
根据上述算法流程分析和执行效率结果可以得出以下结论:
1. 在大运算量程序中应尽量使用CPU运算而少避免使用内存存储数据减少CPU运算量;
2. 动态数组,尤其是调整动态数组的大小,会在很大程度上降低程序运行速度。
目前所有方法都是以试除为基本思想的,尚未考虑出其他思路。以试除为基本思想时,根据结论1,应多试除和通过运算测试分析,而减少通过内存转存数据。因此法1即最大试除法的运行效率最高,而其分解范围也是最大的,对于231-1=2,147,483,647(Long型最大值)以下的数都是可以进行分解的(可以通过编写代码扩展范围,但是也会相应降低效率),它是目前我们找到的最优算法。
