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 0601 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;
}
}