求多個正整數的最大公約數和最小公倍數的三種算法

第一種:分解質因數法

import java.util.HashSet;
import java.util.HashMap;
import java.util.Map;
import java.util.Iterator;
import java.util.Scanner;

/*
 *	@topic:多個正整數的最大公約數和最小公倍數——分解質因數法
 */
/*
 *  756 = 2*2*3*3*3*7
 *	504 = 2*2*2*3*3*7
 *	630 = 2*3*3*5*7
 *	2226 = 2*3*7*53
 *	最大公約數爲4個數的公因子相乘:2*3*7 = 42
 *	最小公倍數爲4個數不重複因子相乘:2*2*2*3*3*3*5*7*53 = 400680
 */
public class GcdLcm {
	static int[] a;//存放正整數
	static int n;//正整數個數
	static int[] prime = new int[1000];//素數表
	static HashSet<Integer> qf = new HashSet<Integer>();//存放所有因子
	static HashMap<Integer, Integer> df = new HashMap<Integer, Integer>();//存放重複因子及其個數

	public static void main(String[] args) {
		// System.out.println("Hello Landor!");
		for (int i = 1, j = 0; j < 1000; i++) {
			if (isPrime(i))
				prime[j++] = i;
		}// 建立素數表
		Scanner sc = new Scanner(System.in);
		n = sc.nextInt();
		a = new int[n];
		for (int i = 0; i < n; i++)
			a[i] = sc.nextInt();
		calculate();
		System.out.println("最大公約數爲:" + getGcd());
		System.out.println("最小公倍數爲:" + getLcm());
	}

	public static void calculate() {//依次將每個正整數進行因數分解:756 = 2*2*3*3*3*7
		int i, j, count = 0;
		for (i = 0; i < n; i++) {
			int m = a[i];
			for (j = 0; j < 1000 && m > 1; j++) {
				Integer key = new Integer(prime[i]);
				while (m % prime[j] == 0) {
					m /= prime[j];
					qf.add(key);//將此因數放進HashSet中(會自動去重)
					count++;
				}
				if (count > 0) {
					if (df.get(key) == null) {//如果此因數沒有出現過,則直接將因數及其個數放入HashMap中
						df.put(key, new Integer(count));
					} else if (df.get(key).intValue() < count) {
						df.remove(key);//如果出現此因數的個數比hashmap中的個數大,則刪除後將新的個數添加
						df.put(key, new Integer(count));
					}
				}
				count = 0;
			}
		}
	}

	public static int getGcd() {
		int gcd = 1, i, j;
		Iterator<Integer> it = qf.iterator();
		while (it.hasNext()) {//遍歷每個qf中的因數,若爲公因數則與gcd相乘
			i = it.next().intValue();
			for (j = 0; j < n; j++) {
				if (a[j] % i != 0)//不是公因數
					break;
			}
			if (j == n)
				gcd *= i;
		}
		return gcd;
	}

	public static int getLcm() {
		int lcm = 1;
		//System.out.println(df);
		for (Map.Entry<Integer, Integer> e : df.entrySet())
			lcm *= (int) Math.pow((double) e.getKey().intValue(), (double) e
					.getValue().intValue());//遍歷HashMap中的Key和Value,將Key的Value次方與lcm相乘
		return lcm;
	}

	public static boolean isPrime(int n) {// 判斷素數
		int i, step = 4, s = (int) Math.sqrt((double) n + 0.01);
		if (n < 1)
			System.exit(1);
		if (n == 2 || n == 3)
			return true;
		if (n == 1 || n % 2 == 0 || n % 3 == 0)
			return false;
		for (i = 5; i <= s; i += step) {
			if (n % i == 0)
				break;
			step ^= 6;
		}
		return i > s;
	}
}
/* 測試實例:
4
756
504
630
2226
最大公約數爲:42
最小公倍數爲:400680
*/

 

第二種:輾轉相除遞推法

import java.util.Scanner;

/*
 *	@topic:多個正整數的最大公約數和最小公倍數——輾轉相除遞推法
 */
/*
 *  先求gcd:
 *	(756,504):756%504 = 252 ,504%252 = 0 ,所以(756,504)=252
 *	(630,252):630%252 = 126 ,252%126 = 0, 所以(630,252)=126
 *	(2226,126):2226%126 = 84,126%84 = 42,84%42=0,所以(2226,126)=42
 *	所以4個數的最大公約數爲:(756,504,630,2226)=42
 *	再求lcm:
 *	[756,504]=756*504/252=1512
 *	[1512,630]=1512*630/126=7560   注意,此處的126和上面所求126沒有關係,這裏的126是需要求gcd(1512,630)得到的
 *	[7560,2226]=7560*2226/42=400680
 *	所以4個數的最小公倍數爲:[756,504,630,2226]=400680
 */

public class GcdLcm1 {
	static int[] a;// 存放正整數
	static int n;// 正整數個數

	public static void main(String[] args) {
		// System.out.println("Hello Landor!");
		Scanner sc = new Scanner(System.in);
		n = sc.nextInt();
		a = new int[n];
		for (int i = 0; i < n; i++)
			a[i] = sc.nextInt();
		System.out.println("最大公約數爲:" + getGcd());
		System.out.println("最小公倍數爲:" + getLcm());
	}

	public static int getGcd() {
		int p = a[0];
		for (int i = 1; i < n; i++)
			p = gcd(Math.max(p, a[i]), Math.min(p, a[i]));
		return p;
	}

	public static int getLcm() {
		int lcm = a[0];
		for (int i = 1; i < n; i++)
			lcm *= (a[i] / gcd(Math.max(lcm, a[i]), Math.min(lcm, a[i])));
		return lcm;
	}

	public static int gcd(int x, int y) {
		return (y == 0) ? x : gcd(y, x % y);
	}
}


第三種:逐次想減、相除法

逐次相減是用來求最大公約數的,是輾轉相除的一種推廣

算法描述:

     先將m個數由大到小排序,再逐次相減修改各元素的值。設排序後相鄰的兩個數爲A、B(A≥B)。再設n=1、2、3.....,各個A同時按下述方法重新賦值:

       如A>n*B ,則A=A-n*B>0,n是滿足條件的最大整數,相當於A=A%B;

       如A=B,A不變;

       如A=n*B,n≠1,則A=A-(n-1)*B,即取A=B,A不能小於1;

   例如:求最大公約數(756,504,630,2226)

   ①4個數排序,再從左到右逐次相減:

           (756,504,630,2226)=(2226,756,630,504)=(2226-2*756,756-630,630-504,504)=(714,126,126,504)

   ②再排序,逐次相減:

           (714,126,126,504)=(714,504,126,126)=(210,504-3*126,126,126)=(210,126,126,126)

   ③再排序,逐次相減:

            (210,126,126,126)=(84,126,126,126)=(126,126,126,84)=(126,126,42,84)=(126,126,84,42)=(126,42,42,42)

                                                            =(126-2*42,42,42,42)=(42,42,42,42)

    此時,四個數相同,完畢,最大公約數爲42

   

main()
{
	long a[4]={756,504,630,2226},i,j,t;
	printf("最大公約數:(%ld,%ld,%ld,%ld)\n",a[0],a[1],a[2],a[3]);
	h:for(i=0;i<3;i++)	{
		for(j=i+1;j<=3;j++)
			if(a[i]<a[j]){
				t=a[i];
				a[i]=a[j];
				a[j]=t;
			}
	}
	printf("=(%ld,%ld,%ld,%ld)\n",a[0],a[1],a[2],a[3]);
	if(a[0]==a[3]){
		printf("=%ld\n\n",a[0]);
		exit(0);
	}else{
		for(i=0;i<3;i++){
			if(a[i]%a[i+1]!=0)
				a[i]=a[i]%a[i+1];
			else
				a[i]=a[i+1];
		}
		printf("=(%ld,%ld,%ld,%ld)\n",a[0],a[1],a[2],a[3]);
		goto h;
	}
}

逐次相除:用來求最小公倍數

設有若干數,其中最大的爲a,用a的1倍、2倍、3倍、…,除以其餘的各數,若第n次恰好都除盡,則此時的n*a即爲它們的最小公倍數

這個算法比較SB,就不貼代碼了,有興趣自己寫一下。


 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章