JAVA - 基础练习 - 枚举出彩票36选7的所有组合

来源:互联网 发布:笔顺查询软件下载 编辑:程序博客网 时间:2024/05/29 09:06

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变成flaseprivate boolean isEnd = true; // 结束标记,一旦结束isEnd变成flasepublic 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;elseballs[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;}}




0 0