JAVA - 基礎練習 - 枚舉出彩票36選7的所有組合

JAVA基礎練習,枚舉出彩票36選7的所有組合,並把它們保存在文本文件中,要求,每個文件最多保存60萬組。


爲什麼有這樣的要求?因爲36選7有 50,086,080 個組合,如果把它保存在一個文件中,有1G多,打都打不開。


  注意:
  彩票36選7並不僅僅是組合,因爲36選7還有一個特別號碼。

 

  01 02 03 04 05 06 07

  01 02 03 04 05 07 06
  01 02 03 04 06 07 05
  01 02 03 05 06 07 04
  01 02 04 05 06 07 03
  01 03 04 05 06 07 02

  02 03 04 05 06 07 01


因此我們要先求出 1-36 的 8,347,680 種組合,再把每一個組合的元素互換6次得到彩票36選7的組合。也就是說分成了兩個步驟。


package my.lottery.groups;

import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;

/***************************
 * 
 * 這個類的代碼雖然有100多行,但其實真在實現組合的代碼就幾行.
 * 而且每次獲取的是一個組合,方便對組合數據的後續操作.
 * 
 * 組合指:集合A中有n個元素,從n個元素中取m個,無排序的組成一組。其中任意一組叫做從n個元素中取m個的一個組合。
 * 例如:像彩票36選7。但是36選7還有一個特別號.所以36選7的組合總數應該是:
 * C(36,7)*P(6,1) = 36*35*34*33*32*31*30/7/6/5/4/3/2/1*6 = 50086080
 * System.out.println((36L*35*34*33*32*31*30)/(7*6*5*4*3*2*1)*6);
 * 
 * 這裏使用字典排序生成組合,字典排序就是後一個組合比前一個組合大,以6選3爲例,可以產生20個組合:
 * {1,2,3} < {1,2,4} < {1,2,5} < {1,2,6} < {1,3,4} < {1,3,5} ... < {4,5,6}
 * 
 * 一個組合要有以下成員:
 * 		1.要進行組合的集合coll......如{1,2,3,4,5,6}	
 * 		2.集合A的元素個數n.........這裏n=6
 * 		3.取m個元素生成的組合group...如{1,2,3}
 * 		4.每個組合的長度m..........這裏m=3	
 * 		5.能產生的組合總數count.....公式C(n,m) = P(n,m)/m!  count = 6*5*4/3/2/1 = 20 
 * 		6.最小的組合start.........集合的前m位{1,2,3}...最小組合也是字典排序的開始位置	
 * 		7.最大的組合end...........集合的後m位{4,5,6}...最大組合也是字典排序的結束位置
 * 		8.程序如果要枚舉出所有的組合要多少時間runTime
 * 因爲有時候集合是無序的,所以組合所操作的應該是集合的角標,然後再根據角標得到相應的元素。
 * 如對集合{a,t,v,d,f}進行組合我們可以看成對{0,1,2,3,4}進行組合
 *
 *********************/
public class Groups<T> {

	private T[] coll;
	private int n;
	private int m;
	private int last; // 組合的最後一個角標
	private int[] start;
	private int[] end;
	
	private static final String SEPARATOR = System.getProperty("line.separator");

	private boolean isStart = true; // 開始標記,一旦開始isStart變成flase

	private boolean isEnd = true; // 結束標記,一旦結束isEnd變成flase

	public int govCount; // 公式計算的結果
	
	public int mycount = 0; // 程序生成的結果,if(count==rightCount)表示程序的算法正確
	
	private Date statrTime; // 只是爲了練習Date類
	private Date endTime; 
	private String info;	
	/**
	 * 構造函數,初始化對象.
	 * @param coll 要組合的集合如:{1,2,3,4,5,6...36}
	 * @param m    要從集合中取幾個元素進行組合,36選7。m=7	
	 */
	public Groups(T[] coll, int m) {

		this.coll = coll;
		n = coll.length;
		this.m = m;
		last = m-1;
		if (m <= 0 || m > n) throw new RuntimeException("..你小子是想幹嘛..");			
	}	
	/**
	 * 初始化最小組合{0,1,2,3,4,5,6},這裏操作的是{1,2,3,4,5,6...36}的角標
	 */
	private void startIndex() {
		start = new int[m];
		for (int x = 0; x < m; x++) start[x] = x;
		
	}	
	/**
	 * 初始化最大組合{29,30,31,32,33,34,35},這裏操作的是{1,2,3,4,5,6...36}的角標
	 */
	private void endIndex() {
		
		end = new int[m];
		for (int x = 0, i = n - m; x < m; x++, i++) end[x] = i;		
	}	
	/**
	 * @return 如果沒有下一個組合返回false
	 */
	public boolean hasNext() {
		return isEnd;
	}	
	/*************************
	 *
	 * 組合字典排序的算法:
	 * 	以 coll = {1,2,3...36} 選 7 爲例.
	 * 	雖然1-36是有序的但是考慮到更多無序的情況我們使用角標進行組合
	 * 	也就是對{0,1,2...35}進行組合
	 * 	從最小組合  start = {0,1,2,3,4,5,6} 開始...
	 * 	當start的最後一個元素start[start.length-1] <= coll[coll.length-1]時...
	 * 	start[start.length-1]++
	 * 	注意{0,1,2,3,4,5,6}中的0,1,2,3,4,5不變只有6在++
	 * 	當start的最後一個元素start[start.length-1]++到了等於coll[coll.length-1]時...
	 * 	最後一個元素的前一位start[start.length-1-1]+1
	 * 	然後前一位start[start.length-1-1]後面的   每   一個元素  = start[start.length-1-1]++.
	 * 
	 *  和我們的十進制是一個道理的,從0開始到9,十進1.個位歸零。
	 *  只是不太一樣的是,十進1以後.個位不是歸零,而是它的前一位加1
	 *  
	 *  ...這樣一直累加到最大組合{29,30,31,32,33,34,35}就求出了36選7的所有組合
	 *  
	 *  以下是6選3的所有組合:
	 *  123 124 125 126			{1,2,6} -> {1,3,4} <- {1,2+1,2+1+1}
	 *  134 135 136
	 *  145 146 
	 *  156 					{1,5,6} -> {2,3,4} <- {1+1,1+1+1,1+1+1+1}
	 *  234 235 236
	 *  245 246
	 *  256						{2,5,6} -> {3,4,5} <- {2+1,2+1+1,2+1+1+1}
	 *  345 346 
	 *  356
	 *  456
	 *  
	 * 	獲取下一個組合的方法,每次獲取一個組合,從最小組合開始,組合完畢時hasNext爲 flase
	 * 	@return 下一個組合的list集合
	 * 
	 **********************************/	
	
	public ArrayList<T> nextGroup() {
		
		/*只執行一次,初始化,最小組合和最大組合*/		
		if (isStart) {
			statrTime = new Date(System.currentTimeMillis());
			startIndex();
			endIndex();
			isStart = false;
			mycount++;
			return index2value(start);			 
		}
		
		/*如果組合的最後一個角標不是集合的最後一個角標,++...*/
		if (start[last] < n - 1) {			
			
			start[last] = start[last] + 1;			
			mycount++;
			return index2value(start);
		}		
		
		/*當組合的最後一個角標移到了集合的最後一個角標...*/		
		/*從左邊開始遍歷start組合的每一個角標元素,也可以從右邊開始,這裏從左邊開始比較方便*/
		for (int i = 0; i < m; i++) {

			
			
			/*和最大組合比較,如果大於,把前一個元素+1,然後前一個元素後面的每個元素++*/
			if (start[i] >= end[i]) {				
				
				start[i - 1] = start[i - 1] + 1;

				for (int ii = i, j = 1; ii < m; ii++, j++) {
					start[ii] = start[i - 1] + j;
				}
				break;
			}
		}		
		/*結束循環,當組合第一個元素的角標移到它的最大值時,組合完畢。*/
		if (start[0] >= n - m) {
			isEnd = false;
			endTime = new Date(System.currentTimeMillis());
		}
		
		mycount++;
		return index2value(start);		
	}	
	/**
	 * 把start角標數組轉換成它對應的值存儲到list集合中
	 * @param start 	start[]角標數組
	 * @return     		 相應的值
	 */		
	public ArrayList<T> index2value(int[] start) {
		ArrayList<T> group = new ArrayList<T>();
		for (int i : start)	group.add(coll[i]);
		return group;
	}	
	/**
	 * @return 用公式公式計算出來的標準組合總數
	 */
	public int getGovCount() {
		return (int)(factorial(n, m)/factorial(m,m));
	}	
	/**
	 *	階乘的方法
	 */
	private long factorial(int n, int m) {		
		long val = n;
		for (int i = 1; i < m ; i++) val = val * (n - i);
		return val;
	}	
	/**
	 * 獲取運算所使用的時間,精確到秒
	 */
	public String getInfo() {
		
		SimpleDateFormat sd = new SimpleDateFormat("yyy-MM-dd  HH:mm:ss");
		
		long time = endTime.getTime() - statrTime.getTime();
		
		return info ="本次運算從:"+sd.format(statrTime)+"開始"+"到:"+sd.format(endTime)+"結束"+"總共使用了:"+(time)+"毫秒";		
		
	}
}



package my.lottery.groups;
/*************
 *	枚舉出兩種彩票,一種36選7,一種排列三.
 *	都有一個方法,可以獲取彩票的所有球.
 */
public enum Lottery{
	
	L_36(36) {
		public String[] getBalls() {
			
			String[] balls = new String[36];
			for (int i = 1; i <= count; i++) {
				
				if (i < 10)
					balls[i - 1] = "0" + i;
				else
					balls[i - 1] = i + "";
			}
			return balls;			
		}		
	},
	L_3(10) {
		public String[] getBalls() {
			
			String[] balls = new String[10];
			for (int i = 0; i < count; i++) {

				balls[i] = "0" + i;
			}
			return balls;
		}
	};	
	
	protected int count;
	
	Lottery(int count) {
		this.count = count;
	}
	
	public abstract String[] getBalls();
} 




package my.lottery.groups;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Properties;
/**
 * 彩票36選7,計算出36選7的所有組合並把他們保存在文本文件中,每個文件保存60萬組.
 */
public class Lottery_1_36 {
	
	private static final String SEPARATOR = System.getProperty("line.separator");
	
	public static void main(String[] args) throws IOException {		
		
		// 獲取36選7的所有球。
		Lottery l_36 = Lottery.L_36;
		String[] balls = l_36.getBalls();			
		
		//用於把組合儲存到文件的流
		File dir = new File("f:\\彩票");
		if(!dir.exists()) dir.mkdir();		
		BufferedWriter bufw = null;					
		
		// 使用組合類獲取36選7的所有組合,但是因爲36選7的特殊情況,還要把獲取到的每一個組合進行轉換
		Groups<String> groups = new Groups<String>(balls, 7);			
		
		// 控制每個文件保存的組合個數的一些變量
		int start = 0;
		int size = 100000;
		int fileCount = 1;
		
		over:
		while(true){ // 把組合儲存到文件中每個文件保存60萬組.			
			
			File file = new File(dir,"36選7的所有組合"+fileCount+".txt");
			bufw = new BufferedWriter(new FileWriter(file));
			
			fileCount++;
			
			int end = start + size;
			boolean b = true;
			
			while(b){
				
				if(!groups.hasNext()) break over;				
				
				//獲取1-36的下一個組
				ArrayList<String> group = groups.nextGroup();
				
				//把1-36的組轉成36選7的組
				StringBuilder group_1_36 = GroupTo36(group," ");			
				
				//寫入文件
				bufw.write(group_1_36.toString());
				bufw.flush();
					
				bufw.newLine();				
							
				start++;
				if(start>end) b=false;				
			}	
		}		
		
		// 用於把計算信息儲存到文件的流
		File file2 = new File(dir,"計算信息.txt");
		FileWriter bufw2 = new FileWriter(file2,true);
		
		int mycount =  groups.mycount*6;
		int govCount =  groups.getGovCount()*6;
		
		Properties pro = new Properties();
		
		pro.setProperty("GOVCOUNT-----", govCount+"");
		pro.setProperty("MYCOUNT------", mycount+"");
		pro.setProperty("FILECOUNT----", (fileCount-1)+"");
		pro.setProperty("INFO---------", groups.getInfo());
		pro.store(bufw2, "---- ---- -- ---- --- --- ----- ---- ---- ---- ---");		
		
		bufw2.close();
		bufw.close();
	}
	/**
	 * 把從組合類返回的組合重新排列,並把每一個組合轉換成字符串,保存到list集合中
	 * 爲什麼要重新排列?
	 * 因爲36選7的特殊情況{1,2,3,4,5,6,7}還有六種組合
	 * {1,2,3,4,5,7,6}
	 * {1,2,3,4,6,7,5}
	 * {1,2,3,5,6,7,4}
	 * ...
	 * {2,3,4,5,6,7,1}
	 * 也就是元素互換....
	 * @param group
	 * @param sign 分隔符
	 * @return	返回7組彩票
	 */
	public static StringBuilder GroupTo36(ArrayList<String> group,String sign) {		
		
		StringBuilder sb = new StringBuilder();
		
		//把組合{1,2,3,4,5,6,7}按指定格式添加到list集合中
		for (String element : group) {
			sb.append(element + sign);
		}
		sb.append(SEPARATOR);

		int lastIndex = group.size() - 1;
		
		//元素互換得到其他的六種組合並添加到list集合中
		for (int i = 1; i <= lastIndex; i++) {
			
			String lastelement = group.get(lastIndex);
			group.set(lastIndex, group.get(lastIndex-i));
			group.set(lastIndex-i, lastelement);
			
			for (String ii : group) {
				sb.append(ii + sign);
			}			
			sb.append(SEPARATOR);
		}
		return sb;
	}
}




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