1. 程式人生 > >將一堆數儘量均勻分配成N組

將一堆數儘量均勻分配成N組

1、問題來源

該問題是博主最近在公司實現的一個JAVA演算法,原場景是要將M個合同按照金額儘量平均的分配給N個業務員,並且合同金額不能拆分,每個業務員都要分到合同。

2、最初思路(錯誤)

一開始拿到這個問題的時候我覺得是不簡單,但不會太難的問題,抽象出來也就是將M個數儘量均勻的分配成N組。
於是自信滿滿的在腦內進行各種演算後很快就有了思路:先從大到小排序,然後取出最大的N個數分到N組中,接著從剩下的數中按從大到小繼續分配,每次都把剩下的最大數分給N組中最小的那組。寫了一天各種演算法最後發現本質上都是這種方式,一次次被自己舉的反例打臉推翻。
這裡我就說下這種分配方式錯誤的根本原因。每次分配的時候只考慮到了眼前的分組情況和剩下的數中的其中一個,只要是這種籠統的分配方式,不管是我一開始的錯誤思路還是博友們所嘗試過的思路,都肯定不會是能夠尋求最優解的方案。

3、何為最接近

最接近這個概念肯定不是想當然比較出的,思前顧後得出的結論卻也算簡單。假設有10個數,總和為100,現在要將它們儘量均勻的分配成5組,那麼分完後每一組的均值就是100/5=20。要讓每一組之間儘量均勻也就是要讓每一組最大限度的接近20。
用數學公式來理解的話,就假設每組均值是a,5組總和分別為s1、s2、s3、s4、s5,所以|s1 - a| + |s2 - a| + |s3 - a| + |s4 - a| + |s5 - a|越小的方案越合理。

4、最終思路

首先可能有的數會遠遠大於其他的數,當然這只是籠統的說法,至於怎麼樣才算遠遠大於得要有一個實際的範疇。還是假設我們有10個數,10個數總和是100,要分成5組,分完後每一組的均值就是100/5=20,我們要讓每一組的總和儘量接近20。但也許這裡有的數本身就大於或等於分組均值20,這樣的數就肯定是單獨作為一組的,於是就要先將這些大於或等於分組均值的數單獨拎出來作為一組。可能這麼處理一次還不夠,這裡就進行遞迴,每一次遞迴計算最新分組均值,直到不存在大於或等於分組均值的數為止。
以下是JAVA演算法程式碼,其中在使用時引數beanAll是傳一個空集合進去,不習慣的話也可以不傳這個引數,改成返回這個集合。

public class AmountBean {

	//金額總和
	private Double count;
	
	//金額集合
	private List<Double> listAmount;

	public Double getCount() {
		return count;
	}

	public void setCount(Double count) {
		this.count = count;
	}

	public List<Double> getListAmount() {
		return listAmount;
	}

	public void setListAmount(List<Double> listAmount) {
		this.listAmount = listAmount;
	}
	
}
/**
 * 將所有肯定是單獨分組的金額找出並存入目標LIST中
 * @param listAmount:所有金額集合
 * @param num:總共需要分組的數量
 * @param beanAll:目標物件集合
 */
public static void greaterThanAverage(List<Double> listAmount,int num,List<AmountBean> beanAll){
	int listAmountSize = listAmount.size();
	
	Double allAmount = 0.0;
	for(Double amount : listAmount){
		allAmount += amount;
	}
	//計算分組平均值
	Double average = allAmount / num;
	
	//是否遞迴
	Boolean flag = true;
	
	//將大於分組平均值的金額單獨分為一組
	for(int i = listAmount.size() -1 ;i >= 0 ;i--){
		Double current = listAmount.get(i);
		if(current >= average){
			//如果當前合同金額大於或等於分組平均值,則單獨分為一組
			List<Double> listTemp = new ArrayList<>();
			listTemp.add(current);
			AmountBean beanTemp = new AmountBean();
			beanTemp.setListAmount(listTemp);
			beanTemp.setCount(current);
			beanAll.add(beanTemp);
		}else{
			if(i == listAmount.size() -1){
				//最大的金額都不滿足條件,說明已經不存在肯定需要單獨分為一組的金額
				flag = false;
			}
			break;
		}
	}
	
	if(beanAll.size() < num && flag){
		//剩下的金額數
		int leftNum = listAmountSize - beanAll.size();
		//剩下還需分組的組數
		int leftListNum = num - beanAll.size();
		//按金額從小到大取出剩下的金額存到新的LIST中
		List<Double> leftAmount = new ArrayList<>();
		for(int i = 0;i < leftNum;i++){
			leftAmount.add(listAmount.get(i));
		}
		//遞迴到直至不能繼續執行
		greaterThanAverage(leftAmount,leftListNum,beanAll);
	}
}

如此處理完以後用剩下的陣列成一個新的集合,比如說總共有10個數要分成5組,已經通過以上程式碼單獨取出2個數分別作為一組,接著我們還剩8個數要分成3組。
假設這8個數總和是90,剩下的三組均值是90/3=30。就直接舉個例子,剩下的8個數從大到小為:27,21,13,13,10,3,2,1。
實際上到了這一步,問題已經轉變為了:從一堆數中選出任意個(至少1個)數,使其總和最接近某個數。也就是說,我們要從剩下這8個數中用至少1個數組成最接近30的集合,確定之後再將這些數從8個數中剔除掉再分配下一組。考慮到了這裡可能會覺得問題已經差不多解決了,但實際上離最終結論還差很多需要考量的邏輯。
從大到小遍歷剩下的8個數,這裡記為listAmount,首先是當前數也就是最大的數27,記為current。因為每一組至少要有1個數,所以先直接將current新增到一個集合中,記為listNearest,並將current從listAmount剔除。於是30-27=3,這是27和分組均值的差值,記為lack,接著我們要從剩下的7個數中選出任意個(可以不選)數,使其總和最接近lack。因為是從大到小遍歷,所以找到第一個小於或等於lack的數,在這裡找到2。
實際演算法中,在這一步有多種情況要考慮。

  • 1、如果找到的這個數是遍歷到的第一個也就是最大的數:
    那麼之後也不會有單獨比其更接近lack的數了。但是,可能會有幾個相加後比這個數更接近lack的數。所以這裡進行遞迴,從之後的數中尋找最接近lack的組合,找到後再與2比較誰更接近lack並新增到listNearest中,接著還要記得將其從listAmount中剔除。完成這一步後假設新增進去的是2,那麼這一組距離分組均值30還差1,更新lack=1繼續下一步遍歷進行相同操作,而如果lack已經大於或等於30,則已完成一組的分配,跳出遍歷迴圈。

  • 2、如果找到的這個數是遍歷到的最後一個數:
    通過上一情況的討論,因為這已經是最後一個數,不會存在後面的某些數相加後比其更接近lack的情況,所以直接將其新增進listNearest,並更新和分組均值的差值lack判斷是否跳出迴圈。

  • 3、如果找到的這個數在中間某個位置:
    將這個數記為偏小數s,再找到該數前一個也就是比lack大的最小的數,記為偏大數g,比較s和g誰更接近lack。
    (1)s更接近,操作同1。
    (2)g更接近,這時還要繼續尋找後面有沒有一些數和s相加後比g更接近lack,具體操作是從s這個位置開始(包括s)遞迴尋找最接近lack的組合。找到後再判斷這個組合和g誰更接近lack並新增到listNearest中,再將其從listAmount中剔除。更新lack繼續遍歷進行相同操作。

  • 4、如果直到最後都沒有小於或等於lack的數:
    那麼判斷最後也就是最小的數s和current相加後是否比current本身更接近或同樣接近分組均值,實際上只需比較s是否小於或等於兩倍lack。是的話就將其新增進listNearest中,再將其從listAmount中剔除。更新lack繼續遍歷進行相同操作。

分析完以後還是把程式碼直接搬出來

/**
 * 兩個Double相加
 */
public static Double add(Double v1,Double v2){
	BigDecimal b1 = new BigDecimal(v1.toString());
	BigDecimal b2 = new BigDecimal(v2.toString());
	return b1.add(b2).doubleValue();
}

/**
 * 兩個Double相減
 * @param v1:減數
 * @param v2:被減數
 */
public static Double sub(Double v1,Double v2){
	BigDecimal b1 = new BigDecimal(v1.toString());
	BigDecimal b2 = new BigDecimal(v2.toString());
	return b1.subtract(b2).doubleValue();
}

/**
 * 從剩下的小於分組均值的陣列成的集合中,分配成num個最優組合
 * @param listAmount:剩下的小於分組均值的陣列成的集合,要求從小到大排序
 * @param num:需要分配成的組數
 * @param average:分組均值
 */
public static List<AmountBean> Grouping(List<Double> listAmount,int num,Double average){
	//目標返回結果
	List<AmountBean> resultBean = new ArrayList<>();
	//每次都用目前最大的數值進行最優組合
	while(listAmount.size() > 0){
		//呼叫遞迴方法得到與當前遍歷數值匹配成一組的最優方案
		AmountBean currentBean = lackNearest(listAmount,average);
		resultBean.add(currentBean);
	}
	return resultBean;
}

/**
 * 從N個數中找出任意個(至少1個)數,使其總和最接近某個數
 * @param listAmount:N個數集合
 * @param average:需要最接近的目標數
 */
public static AmountBean lackNearest(List<Double> listAmount,Double average){
	//目標返回結果
	AmountBean beanResult = new AmountBean();
	//從剩餘的數中從大到小遍歷尋找最接近lack的組合方式
	List<Double> listNearest = new ArrayList<>();
	
	int initialSize = listAmount.size();
	Double firstAmount = listAmount.get(initialSize - 1);
	//最大的數先歸入一組中並從原始LIST中剔除,繼續尋找是否還有與其相加後使其更接近目標值的組合
	listNearest.add(firstAmount);
	listAmount.remove(initialSize - 1);
	
	beanResult.setListAmount(listNearest);
	beanResult.setCount(firstAmount);
	
	if(listAmount.size() == 0){
		return beanResult;
	}
	
	//目標值與當前數值的差值
	Double lack = sub(average,firstAmount);
	
	//接近差值的偏大數
	Double g = 0.0;
	//接近差值的偏小數
	Double s = 0.0;
	
	//從大到小遍歷
	for(int j = listAmount.size() - 1; j >= 0; j --){
		if(beanResult.getCount() >= average){
			break;
		}
		Double tempJ = listAmount.get(j);
		if(tempJ <= lack){
			//找到最大的一個小於或等於差值的數值
			s = tempJ;
			if(j == listAmount.size() - 1){
				//遍歷到的第一個數就滿足條件,繼續尋找當前數前面是否存在相加後比當前數更接近目標值的組合
				AmountBean beanTemp = new AmountBean();
				if(j != 0){
					//存放當前位置前面所有的數值
					List<Double> listRecursion = new ArrayList<>();
					for(int k = 0; k < j; k ++){
						listRecursion.add(listAmount.get(k));
					}
					//遞迴
					beanTemp = lackNearest(listRecursion,lack);
				} else {
					List<Double> listTemp = new ArrayList<>();
					listTemp.add(s);
					beanTemp.setListAmount(listTemp);
					beanTemp.setCount(s);
				}
				if(Math.abs(sub(lack,beanTemp.getCount())) < sub(lack,s)){
					//當前數前面存在相加後比當前數更接近目標值的組合
					List<Double> listTemp = beanTemp.getListAmount();
					listNearest.addAll(listTemp);
					beanResult.setCount(add(beanResult.getCount(),beanTemp.getCount()));
					//確定新增到某一組後要將這些數值從引數LIST中剔除
					listAmount.removeAll(listTemp);
					//因為當前值沒被處理,所以要繼續留在集合中進行下一次遍歷,減完後要加1
					j = j - listTemp.size() + 1;
					lack = sub(lack,beanTemp.getCount());
				} else {
					listNearest.add(s);
					beanResult.setCount(add(beanResult.getCount(),s));
					//當前值確定新增到某一組後要將其剔除
					listAmount.remove(s);
					j --;
					lack = sub(lack,s);
				}
			} else {
				g = listAmount.get(j + 1);
				if(sub(lack,s) <= sub(g,lack)){
					//偏小數更接近或同樣接近,則需要繼續尋找是否存在相加後比偏小數更接近目標值的組合
					Double lack2 = sub(lack,s);
					if(j != 0){
						//存放當前位置前面所有的數值
						List<Double> listRecursion = new ArrayList<>();
						for(int k = 0; k < j; k ++){
							listRecursion.add(listAmount.get(k));
						}
						//遞迴
						AmountBean beanTemp = lackNearest(listRecursion,lack2);
						if(Math.abs(sub(lack,beanTemp.getCount())) < lack2){
							//存在相加後比偏小數更接近目標值的組合
							List<Double> listTemp = beanTemp.getListAmount();
							listNearest.addAll(listTemp);
							beanResult.setCount(sub(beanResult.getCount(),beanTemp.getCount()));
							//確定新增到某一組後要將這些數值從引數LIST中剔除
							listAmount.removeAll(listTemp);
							//因為當前值沒被處理,所以要繼續留在集合中進行下一次遍歷,減完後要加1
							j = j - listTemp.size() + 1;
							lack = sub(lack,beanTemp.getCount());
						} else {
							listNearest.add(s);
							beanResult.setCount(add(beanResult.getCount(),s));
							//確定新增到某一組後要將這些數值剔除
							listAmount.remove(s);
							j --;
							lack = sub(lack,s);
						}
					} else {
						listNearest.add(s);
						beanResult.setCount(add(beanResult.getCount(),s));
						//確定新增到某一組後要將這些數值剔除
						listAmount.remove(s);
						j --;
						lack = sub(lack,s);
					}
				} else {
					//偏大的數更接近,則需要繼續尋找是否存在和偏小數相加後比偏大數更接近的組合
					if(j != 0){
						//存放當前位置以及當前位置前面所有的數值
						List<Double> listRecursion = new ArrayList<>();
						for(int k = 0; k <= j; k ++){
							listRecursion.add(listAmount.get(k));
						}
						//遞迴
						AmountBean beanTemp = lackNearest(listRecursion,lack);
						if(Math.abs(sub(lack,beanTemp.getCount())) < sub(g,lack)){
							//存在和偏小數相加後比偏大數更接近的組合
							List<Double> listTemp = beanTemp.getListAmount();
							listNearest.addAll(listTemp);
							beanResult.setCount(add(beanResult.getCount(),beanTemp.getCount()));
							//確定新增到某一組後要將這些數值剔除
							listAmount.removeAll(listTemp);
							j = j - listTemp.size();
							lack = sub(lack,beanTemp.getCount());
						} else {
							listNearest.add(g);
							beanResult.setCount(add(beanResult.getCount(),g));
							//確定新增到某一組後要將這些數值剔除
							listAmount.remove(g);
							j --;
							lack = sub(lack,beanTemp.getCount());
						}
					}
				}
			}
			
		} else if (j == 0) {
			//遍歷到最後都沒找到小於或等於差值的,則判斷最小值是否小於或等於lack*2
			if(tempJ <= lack * 2){
				listNearest.add(tempJ);
				beanResult.setCount(add(beanResult.getCount(),tempJ));
				//確定新增到某一組後要將這些數值剔除
				listAmount.remove(tempJ);
				j --;
				lack = sub(lack,tempJ);
			}
		}
		
		//lack小於等於0後就跳出迴圈
		if(lack <= 0){
			break;
		}
	}
	return beanResult;
}

5、總結

實現該思路的前提是理解“最接近”這一概念,接著就是保證每一步都能考慮到全域性後走當前最優解,不能只考慮眼前。
實際上我無法證明我的這種方式得出的方案一定會是最優解,但肯定是能夠適用於絕大部分資料的方案,同時也希望博友們要是能舉出反例後可以提供給我。然後就是某些地方的判斷遇到同樣接近的情況該取哪個,這點我還不太清楚是否會影響最終結果,所以儘量都取所用數字更少的組合。
感謝閱讀!

6、JAVA程式碼彙總

實體類:

public class AmountBean {
	
	//金額總和
	private Double count;
	
	//金額集合
	private List<Double> listAmount;

	public Double getCount() {
		return count;
	}

	public void setCount(Double count) {
		this.count = count;
	}

	public List<Double> getListAmount() {
		return listAmount;
	}

	public void setListAmount(List<Double> listAmount) {
		this.listAmount = listAmount;
	}
	
}

演算法類:

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class DistributionUtil {

	public static void main(String[] args) {
		List<Double> listAmount = new ArrayList<>();
		listAmount.add(22.5);
		listAmount.add(32.1);
		listAmount.add(54.2);
		listAmount.add(215.1);
		listAmount.add(22000.1);
		listAmount.add(22.5);
		listAmount.add(73.9);
		listAmount.add(52.1);
		listAmount.add(215.3);
		listAmount.add(12.2);
		
		List<AmountBean> resultBean = amountShare(listAmount,5);
		for(AmountBean bean : resultBean){
			System.out.println("總金額:" + bean.getCount());
			List<Double> listTemp = bean.getListAmount();
			System.out.print("各個金額:");
			for(Double amount : listTemp){
				System.out.print(amount + ";");
			}
			System.out.println("\n=======================");
		}
		
	}
	
	/**
	 * 將一堆數儘量均勻的分配成num個組
	 * @param listAmount:一堆數的集合
	 * @param num:需要分配成的組數
	 * @return List<AmountBean>:有兩個屬性,List<Double>以及該LIST<Double>中數的總和
	 */
	public static List<AmountBean> amountShare(List<Double> listAmount,int num){
		//將合同金額從小到大排序
		Collections.sort(listAmount);
		//將所有合同分攤為數個List(最終返回目標)
		List<AmountBean> listAll = new ArrayList<>();
		//先通過遞迴將肯定需要單獨分組的合同找出
		greaterThanAverage(listAmount,num,listAll);
		int listAmountSize = listAmount.size();
		//剩下的合同數
		int leftNum = listAmountSize - listAll.size();
		//剩下還需分組的組數
		int leftListNum = num - listAll.size();
		//按金額從小到大取出剩下的合同金額存到新的LIST中
		List<Double> leftAmount = new ArrayList<>();
		Double leftCount = 0.0;
		for(int i = 0;i < leftNum; i ++){
			Double amount = listAmount.get(i);
			leftAmount.add(amount);
			leftCount = add(leftCount,amount);
		}
		//計算平均值
		Double average = leftCount / leftListNum;
		//剩下的金額進行遞迴分組
		List<AmountBean> resultBean = Grouping(leftAmount,leftListNum,average);
		listAll.addAll(resultBean);
		return listAll;
	}
	
	/**
	 * 從剩下的小於分組均值的陣列成的集合中,分配成num個最優組合
	 * @param listAmount:剩下的小於分組均值的陣列成的集合,要求從小到大排序
	 * @param num:需要分配成的組數
	 * @param average:分組均值
	 */
	public static List<AmountBean> Grouping(List<Double> listAmount,int num,Double average){
		//目標返回結果
		List<AmountBean> resultBean = new ArrayList<>();
		//每次都用目前最大的數值進行最優組合
		while(listAmount.size() > 0){
			//呼叫遞迴方法得到與當前遍歷數值匹配成一組的最優方案
			AmountBean currentBean = lackNearest(listAmount,average);
			resultBean.add(currentBean);
		}
		return resultBean;
	}
	
	/**
	 * 從N個數中找出任意個(至少1個)數,使其總和最接近某個數
	 * @param listAmount:N個數集合
	 * @param average:需要最接近的目標數
	 */
	public static AmountBean lackNearest(List<Double> listAmount,Double average){
		//目標返回結果
		AmountBean beanResult = new AmountBean();
		//從剩餘的數中從大到小遍歷尋找最接近lack的組合方式
		List<Double> listNearest = new ArrayList<>();
		
		int initialSize = listAmount.size();
		Double firstAmount = listAmount.get(initialSize - 1);
		//最大的數先歸入一組中並從原始LIST中剔除,繼續尋找是否還有與其相加後使其更接近目標值的組合
		listNearest.add(firstAmount);
		listAmount.remove(initialSize - 1);
		
		beanResult.setListAmount(listNearest);
		beanResult.setCount(firstAmount);
		
		if(listAmount.size() == 0){
			return beanResult;
		}
		
		//目標值與當前數值的差值
		Double lack = sub(average,firstAmount);
		
		//接近差值的偏大數
		Double g = 0.0;
		//接近差值的偏小數
		Double s = 0.0;
		
		//從大到小遍歷
		for(int j = listAmount.size() - 1; j >= 0; j --){
			if(beanResult.getCount() >= average){
				break;
			}
			Double tempJ = listAmount.get(j);
			if(tempJ <= lack){
				//找到最大的一個小於或等於差值的數值
				s = tempJ;
				if(j == listAmount.size() - 1){
					//遍歷到的第一個數就滿足條件,繼續尋找當前數前面是否存在相加後比當前數更接近目標值的組合
					AmountBean beanTemp = new AmountBean();
					if(j != 0){
						//存放當前位置前面所有的數值
						List<Double> listRecursion = new ArrayList<>();
						for(int k = 0; k < j; k ++){
							listRecursion.add(listAmount.get(k));
						}
						//遞迴
						beanTemp = lackNearest(listRecursion,lack);
					} else {
						List<Double> listTemp = new ArrayList<>();
						listTemp.add(s);
						beanTemp.setListAmount(listTemp);
						beanTemp.setCount(s);
					}
					if(Math.abs(sub(lack,beanTemp.getCount())) < sub(lack,s)){
						//當前數前面存在相加後比當前數更接近目標值的組合
						List<Double> listTemp = beanTemp.getListAmount();
						listNearest.addAll(listTemp);
						beanResult.setCount(add(beanResult.getCount(),beanTemp.getCount()));
						//確定新增到某一組後要將這些數值從引數LIST中剔除
						listAmount.removeAll(listTemp);
						//因為當前值沒被處理,所以要繼續留在集合中進行下一次遍歷,減完後要加1
						j = j - listTemp.size() + 1;
						lack = sub(lack,beanTemp.getCount());
					} else {
						listNearest.add(s);
						beanResult.setCount(add(beanResult.getCount(),s));
						//當前值確定新增到某一組後要將其剔除
						listAmount.remove(s);
						j --;
						lack = sub(lack,s);
					}
				} else {
					g = listAmount.get(j + 1);
					if(sub(lack,s) <= sub(g,lack)){
						//偏小數更接近或同樣接近,則需要繼續尋找是否存在相加後比偏小數更接近目標值的組合
						Double lack2 = sub(lack,s);
						if(j != 0){
							//存放當前位置前面所有的數值
							List<Double> listRecursion = new ArrayList<>();
							for(int k = 0; k < j; k ++){
								listRecursion.add(listAmount.get(k));
							}
							//遞迴
							AmountBean beanTemp = lackNearest(listRecursion,lack2);
							if(Math.abs(sub(lack,beanTemp.getCount())) < lack2){
								//存在相加後比偏小數更接近目標值的組合
								List<Double> listTemp = beanTemp.getListAmount();
								listNearest.addAll(listTemp);
								beanResult.setCount(sub(beanResult.getCount(),beanTemp.getCount()));
								//確定新增到某一組後要將這些數值從引數LIST中剔除
								listAmount.removeAll(listTemp);
								//因為當前值沒被處理,所以要繼續留在集合中進行下一次遍歷,減完後要加1
								j = j - listTemp.size() + 1;
								lack = sub(lack,beanTemp.getCount());
							} else {
								listNearest.add(s);
								beanResult.setCount(add(beanResult.getCount(),s));
								//確定新增到某一組後要將這些數值剔除
								listAmount.remove(s);
								j --;
								lack = sub(lack,s);
							}
						} else {
							listNearest.add(s);
							beanResult.setCount(add(beanResult.getCount(),s));
							//確定新增到某一組後要將這些數值剔除
							listAmount.remove(s);
							j --;
							lack = sub(lack,s);
						}
					} else {
						//偏大的數更接近,則需要繼續尋找是否存在和偏小數相加後比偏大數更接近的組合
						if(j != 0){
							//存放當前位置以及當前位置前面所有的數值
							List<Double> listRecursion = new ArrayList<>();
							for(int k = 0; k <= j; k ++){
								listRecursion.add(listAmount.get(k));
							}
							//遞迴
							AmountBean beanTemp = lackNearest(listRecursion,lack);
							if(Math.abs(sub(lack,beanTemp.getCount())) < sub(g,lack)){
								//存在和偏小數相加後比偏大數更接近的組合
								List<Double> listTemp = beanTemp.getListAmount();
								listNearest.addAll(listTemp);
								beanResult.setCount(add(beanResult.getCount(),beanTemp.getCount()));
								//確定新增到某一組後要將這些數值剔除
								listAmount.removeAll(listTemp);
								j = j - listTemp.size();
								lack = sub(lack,beanTemp.getCount());
							} else {
								listNearest.add(g);
								beanResult.setCount(add(beanResult.getCount(),g));
								//確定新增到某一組後要將這些數值剔除
								listAmount.remove(g);
								j --;
								lack = sub(lack,beanTemp.getCount());
							}
						}
					}
				}
				
			} else if (j == 0) {
				//遍歷到最後都沒找到小於或等於差值的,則判斷最小值是否小於或等於lack*2
				if(tempJ <= lack * 2){
					listNearest.add(tempJ);
					beanResult.setCount(add(beanResult.getCount(),tempJ));
					//確定新增到某一組後要將這些數值剔除
					listAmount.remove(tempJ);
					j --;
					lack = sub(lack,tempJ);
				}
			}
			
			//lack小於等於0後就跳出迴圈
			if(lack <= 0){
				break;
			}
		}
		return beanResult;
	}
	
	/**
	 * 將所有肯定是單獨分組的金額找出並存入目標LIST中
	 * @param listAmount:所有金額集合
	 * @param num:總共需要分組的數量
	 * @param beanAll:目標物件集合
	 */
	public static void greaterThanAverage(List<Double> listAmount,int num,List<AmountBean> beanAll){
		int listAmountSize = listAmount.size();
		
		Double allAmount = 0.0;
		for(Double amount : listAmount){
			allAmount += amount;
		}
		//計算分組平均值
		Double average = allAmount / num;
		
		//是否遞迴
		Boolean flag = true;
		
		//將大於分組平均值的金額單獨分為一組
		for(int i = listAmount.size() -1 ;i >= 0 ;i--){
			Double current = listAmount.get(i);
			if(current >= average){
				//如果當前合同金額大於或等於分組平均值,則單獨分為一組
				List<Double> listTemp = new ArrayList<>();
				listTemp.add(current);
				AmountBean beanTemp = new AmountBean();
				beanTemp.setListAmount(listTemp);
				beanTemp.setCount(current);
				beanAll.add(beanTemp);
			}else{
				if(i == listAmount.size() -1){
					//最大的金額都不滿足條件,說明已經不存在肯定需要單獨分為一組的金額
					flag = false;
				}
				break;
			}
		}
		
		if(beanAll.size() < num && flag){
			//剩下的金額數
			int leftNum = listAmountSize - beanAll.size();
			//剩下還需分組的組數
			int leftListNum = num - beanAll.size();
			//按金額從小到大取出剩下的金額存到新的LIST中
			List<Double> leftAmount = new ArrayList<>();
			for(int i = 0;i < leftNum;i++){
				leftAmount.add(listAmount.get(i));
			}
			//遞迴到直至不能繼續執行
			greaterThanAverage(leftAmount,leftListNum,beanAll);
		}
	}
	
	/**
	 * 兩個Double相加
	 */
	public static Double add(Double v1,Double v2){
		BigDecimal b1 = new BigDecimal(v1.toString());
		BigDecimal b2 = new BigDecimal(v2.toString());
		return b1.add(b2).doubleValue();
	}
	
	/**
	 * 兩個Double相減
	 * @param v1:減數
	 * @param v2:被減數
	 */
	public static Double sub(Double v1,Double v2){
		BigDecimal b1 = new BigDecimal(v1.toString());
		BigDecimal b2 = new BigDecimal(v2.toString());
		return b1.subtract(b2).doubleValue();
	}

}