您的位置:首页 > 其它

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

2011-06-25 15:21 232 查看
原题: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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐