原题:http://topic.csdn.net/u/20110623/10/0324c75e-f72a-4894-83e9-f5a00fc88f62.html?85618
描述:有k个正整数 任取其中k-1个数 找到一个他们的最小公倍数数N 求N的最小值
分析:
可以采用以下三种方法:
******************************
1) 方法一:(缺点:需对k个数做质因素分解)
基本想法是先对每个数做质因素分解,然后对k-1个数两两相互统计,找出它们对应质因子的最大个数,最后当所有k-1个数统计结束时,结果就是该质因子个数的最大值,然后将这些质因子相乘(个数>=1,需要重复相乘),找出lcm最小值即可。举个例子:例如有三个数:
x = 12 = 2*2*3;
y = 45 = 3*3*5;
z = 36 = 2*2*3*3
这里k=3,如果取(x,y)这一组共2个数,因为x中的质因子2在y中没有,将它加入lcm的质因子,即lcm=(2*2); x中的质因子3在y中有两个,这时需要在lcm中记录两个3而不是一个3,即lcm=(2*2*3*3); 最后y中的质因子5在x中没有,将它加入lcm的质因子,即lcm=(2*2*3*3*5) = 180。其他两组k-1个数同样处理,最后取最小的lcm就可以得出结果。
对这个例子程序运行结果为:
Input numbers: 12 45 36
(w/o 12), LCM Factors: 2 2 3 2 5 1 lcm: 180
(w/o 45), LCM Factors: 2 2 3 2 lcm: 36
(w/o 36), LCM Factors: 2 2 3 2 5 1 lcm: 180
N: 36
该解法参见下面程序中的solve()函数。
******************************
2)方法二:(优点:无需对k个数做质因素分解;缺点:需k-1个数 --- 共有k组 --- 单独计算)
由于lcm(a,b,c) = lcm(lcm(a,b),c);而且lcm(a,b)=a*b/gcd(a,b)
其中lcm表示最小公倍数,gcd表示最大公约数。
利用欧几里得算法,可以求得gcd(a,b)。
该解法参见下面程序中的solve2()函数。
******************************
3)方法三:(第二种方法的变体,原理是利用被剔除的元素x做文章)
由于要求k各元素中的k-1个数,考虑能否减少一些计算,首先计算全部元素的最小公倍数lcm(0,1,....k),然后考虑如何计算下式中的lcm(0,1,...k-1):
lcm(lcm(0,1,...k-1),x) = lcm(0,1,....k)
这样,问题转化为在lcm(0,1,....k)和x(这里x为要剔除的某个元素,共有k个这样的元素)已知的情况下,如何求上式中的lcm(0,1,...k-1)?
考虑下图一个实际例子, 假设lcm(0,1,...k)的值为变量lcm,即第一个式子,显然lcm一定能被第二式的x整除(因为lcm是k个数的最小公倍数)。这样x总可以表示成(2):这里(6)式表示lcm中因子2和5的最大指数来自x;lcm中另外两个因子3和7的最大指数来一定来自lcm(0,1,...k-1),换句话说,lcm(0,1,...k-1)里面一定有一个因子为(4)式(记作变量contriFactors),最后lcm(0,1,...k-1)可以写成(7)式的形式,即lcm(0,1,...k-1)中因子2的最高指数不超过3,5的最高指数不超过2。
这样问题分解为两部分:1)如何在已知lcm和x的条件下,得出contriFactors的值;2)如何找出lcm(0,1,...k-1)中2和5的最高指数。
第一个部分可以这样计算:由于有条件式(3),将lcm/x得出第(5)式temp的值,这里a和b都大于0,且a=u-u'和b=v-v'。然后不断通过 gcd(temp,x)可以消除x中的因子3和7,即可得到x的伴随变量y。将lcm/y就是第(7)式中左边部分contriFactors的值。
第二部分等价于k-1个数中剔除因子3和7后(得到的数中只含因子2和5),求它们的最小公倍数(记作变量lcmSub)就可得到。由于剔除因子3和7后,得到的k-1个新数比原来的数要小一些,计算它们的最小公倍数会相对容易一些。
最后将contriFactors乘上第二部分中的最小公倍数lcmSub就是lcm(0,1,...k-1)的值。
值得补充说明的是,如果y=1(即x中没有对lcm的贡献因子),那么(7)式(=lcm)一定是最大值,所以将y=1的情况剔除。也可以将这个方法只用来判断y的值是否为1,如果不为1,仍然按照第二种方法计算k-1个数的最小公倍数。下面solve3()中就是只利用y做判断。(计算第二部分中的最小公倍数lcmSub的代码已被注释掉)
这个解法的的效率高低取决于如何剔除公共因子(即下面的函数removeCommonFactors())。比较遗憾的是:测试证明,其效率比第二种方法要稍微慢一点,原因可能是y=1的数据不多(即对lcm做出贡献因子的数据总体上占的比例较多,对这些数据而言计算y的值等于是多余的操作)。不过比第一种分解质因子的方法要快很多。
该解法参见下面程序中的solve3()函数。
程序:
private static LinkedList<Integer> findTwoNumbersLCM(LinkedList<Integer> prev,LinkedList<Integer> next){
int j=next.size()-1; //next list's position
LinkedList<Integer> curr = new LinkedList<Integer>(prev);
if(curr.size()==0){ //e.g. 1's factors list is empty
curr.addAll(next);
}
for(int i=curr.size()-1;i>=0;i-=2){
int count = curr.get(i);
int factor = curr.get(i-1);
while(j>=0){
int jcount = next.get(j);
int jfactor = next.get(j-1);
if(jfactor == factor){
if(jcount>count){
//update the factor's count
curr.set(i, jcount);
}
j-=2;
break;
}else {
if(jfactor > factor){
//insert
int insertAt = i + 1;
if(insertAt<curr.size()){
curr.add(insertAt,jcount);
curr.add(insertAt,jfactor);
}else{
curr.addLast(jfactor);
curr.addLast(jcount);
}
j-=2;
//don't move i;
}else {
//add as a first element
if(i-1==0){
curr.addFirst(jcount);
curr.addFirst(jfactor);
j-=2;
//i has moved to head position
}else{
//don't move j
break;
}
}
}
}
}
//insert the least factors at the front
while(j>=0){
int jcount = next.get(j);
int jfactor = next.get(j-1);
curr.addFirst(jcount);
curr.addFirst(jfactor);
j-=2;
}
return curr;
}
private static long findLCM(LinkedList<Integer>[] factorsList){
LinkedList<Integer> currFactors = factorsList[0];
for(int i=1;i<factorsList.length;i++){
LinkedList<Integer> nextFactors = factorsList[i];
currFactors = K_LCM.findTwoNumbersLCM(currFactors, nextFactors);
}
if(debug){
System.out.format("LCM Factors: ");
for(int factor:currFactors)
System.out.format(" %d",factor);
}
//caculate the lcm value
long lcm = 1;
for(int i=0;i<currFactors.size();i+=2){
int factor = currFactors.get(i);
int count = currFactors.get(i+1);
lcm *= (int)Math.pow(factor, count);
}
if(debug){
System.out.format(" lcm: %d",lcm);
System.out.println();
}
return lcm;
}
//find the minimum lcm among k-1 numbers (k=nums.length)
@SuppressWarnings("unchecked")
public static long solve(int[] nums){
if(debug){
System.out.format("Using method 1 - Input numbers: ");
for(int num:nums)
System.out.format("%d,",num);
System.out.println();
}
Map<Integer,LinkedList<Integer>> factoredNumsMap =K_LCM.factorAllNumbers(nums);
int k =nums.length;
LinkedList<Integer>[] factorsArr =
(LinkedList<Integer>[])Array.newInstance(LinkedList.class,k-1);
long lcmMin = Long.MAX_VALUE;
for(int i=0;i<k;i++){
for(int j=0,m=0;j<k;j++){
if(j!=i){
factorsArr[m++] = factoredNumsMap.get(nums[j]);
}
}
if(debug){
System.out.format("(w/o %d), ",nums[i]);
}
long lcm = K_LCM.findLCM(factorsArr);
if(lcm<lcmMin) lcmMin = lcm;
}
if(debug){
System.out.format("%nN=%d%n",lcmMin);
}
return lcmMin;
}
public static long gcd(long m,long n){
m = (m<0)?-m:m;
n = (n<0)?-n:n;
while(n!=0){
long remainder = m%n;
m = n;
n = remainder;
}
return m;
}
public static long solve2(int[] nums){
if(debug){
System.out.format("Using method 2 - Input numbers: ");
for(int num:nums)
System.out.format("%d,",num);
System.out.println();
}
int k = nums.length;
long lcmMin = Long.MAX_VALUE;
for(int i=0;i<k;i++){
long lcm = 1;
for(int j=0;j<k;j++){
if(j!=i){
lcm = lcm/gcd(lcm,nums[j])*nums[j];
}
}
if(debug) System.out.format("(w/o %d), LCM=%d%n",nums[i],lcm);
if(lcm<lcmMin) lcmMin = lcm;
}
if(debug){
System.out.format("%nN=%d%n",lcmMin);
}
return lcmMin;
}
private static long lcm(int[] nums){
//calculate the lcm of the n numbers
long lcm = 1;
for(int j=0;j<nums.length;j++){
lcm = lcm/gcd(lcm,nums[j])*nums[j];
}
return lcm;
}
//e.g. a=2^u*3^v*5^w; b=3^r*5^s*7^t, then return:
//b=7^t
private static long removeCommonFactors(long a,long b){
long temp = a;
long gcd = gcd(temp,b);
while(gcd != 1){
b/=gcd;
temp = gcd;
gcd = gcd(gcd,b);
}
return b;
}
//Another version: based on solve2(); no factoring
public static long solve3(int[] nums){
if(debug){
System.out.format("Using method 3 - Input numbers: ");
for(int num:nums)
System.out.format("%d,",num);
System.out.println();
}
int k = nums.length;
long lcmMin = Long.MAX_VALUE;
long lcm = lcm(nums);
for(int i=0;i<k;i++){
int x = nums[i];
long temp = lcm / x;
long y = K_LCM.removeCommonFactors(temp,x);
if(y==1)
continue;
/*
long contriFactors = lcm/y;
int[] newNums = new int[k-1];
for(int j=0,m=0;j<k;j++){
if(j!=i){
newNums[m++] = (int)K_LCM.removeCommonFactors(temp,nums[j]);
}
}
long lcmSub = lcm(newNums);
long m = lcmSub * contriFactors;
if(debug) System.out.format("(w/o %d), LCM=%d%n",x,m);
if(m<lcmMin) lcmMin = m;
*/
long lcmK_1 = 1;
for(int j=0;j<k;j++){
if(j!=i){
lcmK_1 = lcmK_1/gcd(lcmK_1,nums[j])*nums[j];
}
}
if(debug) System.out.format("(w/o %d), LCM=%d%n",x,lcmK_1);
if(lcmK_1<lcmMin) lcmMin = lcmK_1;
}
if(debug){
System.out.format("%nN=%d%n",lcmMin);
}
return lcmMin;
}
public static int[] generateRandIntegers(){
int totalKeys = 10;
int KEYRANGE = 100;
int i=0;
int[] nums = new int[totalKeys];
while(i<totalKeys){
nums[i++] = (int)(Math.random()*KEYRANGE+1);
}
return nums;
}
public static void main(String[] args) {
int[] inputNums = new int[]{2*2*2,2*3*3*3,2*3*5,3*3};
long N= K_LCM.solve3(inputNums);
long N0= K_LCM.solve2(inputNums);
if(N != N0){
System.out.println("Test Failed!");
}
System.out.println("***************************************");
inputNums = new int[]{9,10,15};
N= K_LCM.solve3(inputNums);
N0= K_LCM.solve2(inputNums);
if(N != N0){
System.out.println("Test Failed!");
}
System.out.println("***************************************");
inputNums = new int[]{80,40,79,7,13,39,32,80,74,4};
N= K_LCM.solve3(inputNums);
N0= K_LCM.solve2(inputNums);
if(N != N0){
System.out.println("Test Failed!");
}
System.out.println("***************************************");
int testRandCases = 10000;
int[][] cases = new int[testRandCases][];
for(int i=0;i<testRandCases;i++){
int[] nums = generateRandIntegers();
cases[i] = nums;
}
for(int i=0;i<testRandCases;i++){
int[] nums = cases[i];
long N1= K_LCM.solve(nums);
long N2= K_LCM.solve2(nums);
long N3= K_LCM.solve3(nums);
if(!(N1 == N2 && N2 == N3)){
System.out.format("Input numbers: ");
for(int num:nums)
System.out.format("%d,",num);
System.out.println();
System.out.format(" Test Failed!");
System.out.println();
System.out.println();
}
}
System.out.println("***************************************");
long start = System.currentTimeMillis();
for(int i=0;i<testRandCases;i++){
int[] nums = cases[i];
N= K_LCM.solve(nums);
}
long end = System.currentTimeMillis();
System.out.format("Using method 1 - time cost: %d%n", end-start);
System.out.println("***************************************");
start = System.currentTimeMillis();
for(int i=0;i<testRandCases;i++){
int[] nums = cases[i];
N= K_LCM.solve2(nums);
}
end = System.currentTimeMillis();
System.out.format("Using method 2 - time cost: %d%n", end-start);
System.out.println("***************************************");
start = System.currentTimeMillis();
for(int i=0;i<testRandCases;i++){
int[] nums = cases[i];
N= K_LCM.solve3(nums);
}
end = System.currentTimeMillis();
System.out.format("Using method 3 - time cost: %d%n", end-start);
System.out.println("----END----");
}
}
测试输出:
***************************************
Using method 1 - time cost: 1913
***************************************
Using method 2 - time cost: 350
***************************************
Using method 3 - time cost: 421
----END----
以下是使用这三种方法的一个实例测试结果(输入数据为9,10和15):
Using method 1 - Input numbers: 9,10,15,
(w/o 9), LCM Factors: 2 1 3 1 5 1 lcm: 30
(w/o 10), LCM Factors: 3 2 5 1 lcm: 45
(w/o 15), LCM Factors: 2 1 3 2 5 1 lcm: 90
N=30
Using method 2 - Input numbers: 9,10,15,
(w/o 9), LCM=30
(w/o 10), LCM=45
(w/o 15), LCM=90
N=30
Using method 3 - Input numbers: 9,10,15,
(w/o 9), LCM=30
(w/o 10), LCM=45
(w/o 15), LCM=90
N=30