k个数中求任意k-1个数的最小公倍数(LCM)的最小值

原题: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()函数。

程序:

import java.lang.reflect.Array;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Map;
/**
 * 
 * @author ljs
 * 2011-06-23
 *
 */
public class K_LCM {
	private static final boolean debug = false;
	//factors is returned as factor-count pair,e.g.
	//n=2*2*2*2*3*5: returns [2,4,3,1,5,1] (factors are in asc. order)
	private static LinkedList<Integer> findFactors(int n){
		LinkedList<Integer> factorList = new LinkedList<Integer>();
		int p = n;
		for(int i=2;i<=p;i++){
			int count = 0;
			while(n % i == 0){		
				count++;				
				n /= i;		
			}
			if(count>0){
				factorList.add(i);
				factorList.add(count);
			}
		}		
		return factorList;
	}
	private static Map<Integer,LinkedList<Integer>> factorAllNumbers(int[] nums){
		Map<Integer,LinkedList<Integer>> factoredNumsMap =new HashMap<Integer,LinkedList<Integer>>();
		for(int i=0;i<nums.length;i++){			
			//no need to calculate the factors for duplicate numbers
			if(factoredNumsMap.get(nums[i]) == null){
				LinkedList<Integer> currFactors = K_LCM.findFactors(nums[i]);
				factoredNumsMap.put(nums[i],currFactors);
			}			
		}
		return factoredNumsMap;
	}
	
	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

posted @ 2011-06-28 00:05  ljsspace  阅读(501)  评论(1编辑  收藏  举报