1.背景
作为一个在厦门工作的程序员,特别喜欢闽南的中秋博饼文化,但是年年博饼都与状元擦肩而过,特写此篇,计算博饼各种情况出现的概率,期待下一次可以抽到状元。
2.规则
科举 | 常用名 | 表达式 | 备注 |
---|---|---|---|
状元 | 六杯红 | 4 4 4 4 4 4 | |
状元 | 遍地锦 | 1 1 1 1 1 1 | |
状元 | 六杯黑 | A A A A A A | A∈{2,3,5,6} |
状元 | 五红 | 4 4 4 4 4 _ | |
状元 | 五子登科 | A A A A A _ | A∈{1,2,3,5,6} |
状元 | 状元插金花 | 4 4 4 4 1 1 | |
状元 | 四红 | 4 4 4 4 _ _ | |
榜眼 | 对堂 | 1 2 3 4 5 6 | |
探花 | 三红 | 4 4 4 _ _ _ | |
进士 | 四进 | A A A A _ _ | A∈{1,2,3,5,6} |
举人 | 二举 | 4 4 _ _ _ _ | |
秀才 | 一秀 | 4 _ _ _ _ _ | |
落榜 | 落榜 | _ _ _ _ _ _ |
3.代码
1.科举枚举
package bobing;
public enum EnumKeJu {
六杯红("状元", "六杯红", "444444"),
遍地锦("状元", "遍地锦", "111111"),
六杯黑("状元", "六杯黑", "AAAAAA"),
五红("状元", "五红", "44444_"),
五子登科("状元", "五子登科", "AAAAA_"),
状元插金花("状元", "状元插金花", "444411"),
四红("状元", "四红", "4444__"),
对堂("榜眼", "对堂", "123456"),
三红("探花", "三红", "444___"),
四进("进士", "四进", "AAAA__"),
二举("举人", "二举", "44____"),
一秀("秀才", "一秀", "4_____"),
落榜("落榜", "落榜", "______"),
;
private final String parentName;
private final String childName;
private final String expression;
EnumKeJu(String parentName, String childName, String expression) {
this.parentName = parentName;
this.childName = childName;
this.expression = expression;
}
public String getParentName() {
return parentName;
}
public String getChildName() {
return childName;
}
public String getExpression() {
return expression;
}
@Override
public String toString() {
return parentName + "-" + childName;
}
}
2.骰子投掷类
package bobing;
import java.util.Random;
public class Roll {
private final int[] times = new int[7]; // 下标代表点数值,代表出现次数,index=0不用
private String sameIndex = ""; // 重复的点数
private Integer sameTimes = 0; // 重复的次数
private EnumKeJu enumKeJu; // 计算得到的科举
public Roll() {
// 随机骰子
this.setTimes();
this.setSameIndexAndTimes();
this.setEnumKeJu();
}
public Roll(int[] dots) {
// 传入骰子
for (int dot : dots) {
this.times[dot]++;
}
this.setSameIndexAndTimes();
this.setEnumKeJu();
}
private void setTimes() {
Random random = new Random();
for (int i = 0; i < 6; i++) {
int dot = random.nextInt(6) + 1;
this.times[dot]++;
}
}
private void setSameIndexAndTimes() {
for (int i = 1; i < this.times.length; i++) {
int time = this.times[i];
if (time == 0 || time < sameTimes) {
continue;
}
if (time == sameTimes) {
if (sameIndex.equals("4")) {
continue;
}
} else {
sameTimes = time;
}
sameIndex = String.valueOf(i);
if (sameTimes >= 4) {
return;
}
}
}
private void setEnumKeJu() {
switch (sameTimes) {
case 6:
this.enumKeJu = getEnumKeJu6();
break;
case 5:
this.enumKeJu = getEnumKeJu5();
break;
case 4:
this.enumKeJu = getEnumKeJu4();
break;
case 3:
case 2:
this.enumKeJu = getEnumKeJu3AndEnumKeJu2();
break;
default:
this.enumKeJu = getEnumKeJu1();
}
}
private EnumKeJu getEnumKeJu1() {
return EnumKeJu.对堂;
}
private EnumKeJu getEnumKeJu3AndEnumKeJu2() {
if (times[4] == 3) {
return EnumKeJu.三红;
}
if (times[4] == 2) {
return EnumKeJu.二举;
}
if (times[4] == 1) {
return EnumKeJu.一秀;
}
return EnumKeJu.落榜;
}
private EnumKeJu getEnumKeJu4() {
if (sameIndex.equals("4")) {
if (times[1] == 2) {
return EnumKeJu.状元插金花;
}
return EnumKeJu.四红;
}
return EnumKeJu.四进;
}
private EnumKeJu getEnumKeJu5() {
if (sameIndex.equals("4")) {
return EnumKeJu.五红;
}
return EnumKeJu.五子登科;
}
private EnumKeJu getEnumKeJu6() {
switch (sameIndex) {
case "4":
return EnumKeJu.六杯红;
case "1":
return EnumKeJu.遍地锦;
default:
return EnumKeJu.六杯黑;
}
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
for (int i = 1; i< this.times.length; i++) {
int time = this.times[i];
for (int j = 0; j < time; j++) {
sb.append(i);
}
}
sb.append("\t奖励:");
sb.append(enumKeJu);
sb.append("\t相同点数:");
sb.append(sameIndex);
sb.append("\t相同次数:");
sb.append(sameTimes);
return sb.toString();
}
public EnumKeJu getEnumKeJu() {
return enumKeJu;
}
}
3.主方法
package bobing;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class Main {
private static final int TRY_TIMES = (int) Math.pow(6, 6);
public static void main(String[] args) {
// Map<EnumKeJu, List<Roll>> randomMap = getRandomMap();
// printResult(randomMap);
Map<EnumKeJu, List<Roll>> sequenceMap = getSequenceMap();
printResult(sequenceMap);
}
// 随机投掷骰子
public static Map<EnumKeJu, List<Roll>> getRandomMap() {
Map<EnumKeJu, List<Roll>> map = new HashMap<>();
for (int i = 0; i < TRY_TIMES; i++) {
Roll roll = new Roll();
List<Roll> rolls = map.computeIfAbsent(roll.getEnumKeJu(), k -> new ArrayList<>());
rolls.add(roll);
}
return map;
}
// 列举所有骰子情况
public static Map<EnumKeJu, List<Roll>> getSequenceMap() {
Map<EnumKeJu, List<Roll>> map = new HashMap<>();
int[] dots = new int[]{1, 1, 1, 1, 1, 1};
while (dots[0] != -1) {
Roll roll = new Roll(dots);
List<Roll> rolls = map.computeIfAbsent(roll.getEnumKeJu(), k -> new ArrayList<>());
rolls.add(roll);
getNextDots(dots, 5);
}
return map;
}
public static void getNextDots(int[] dots, int index) {
if (dots[index] != 6) {
dots[index] += 1;
return;
}
if (index != 0) {
dots[index] = 1;
getNextDots(dots, index - 1);
return;
}
dots[index] = -1;
}
public static void printResult(Map<EnumKeJu, List<Roll>> map) {
System.out.printf("%s\t%8s\t%8s\t%8s\t%8s\n", "科举", "常用名", "次数", "频率", "表达式");
int zhuangYuanCount = 0;
for (EnumKeJu enumKeJu : EnumKeJu.values()) {
List<Roll> rolls = map.get(enumKeJu);
int count = rolls != null ? rolls.size() : 0;
double rate = count / (double) TRY_TIMES;
System.out.printf("%s\t%8s\t%8s\t%8s\t%8s\n",
enumKeJu.getParentName(),
enumKeJu.getChildName(),
count,
String.format("%.5f", rate),
enumKeJu.getExpression());
if ("状元".equals(enumKeJu.getParentName())) {
zhuangYuanCount += count;
}
}
System.out.printf("%-8s\t\t%8s\t%8s\n", "状元汇总",
zhuangYuanCount, String.format("%.5f", zhuangYuanCount / (double) TRY_TIMES));
}
}
4.计算结果
科举 常用名 次数 频率 表达式
状元 六杯红 1 0.00002 444444
状元 遍地锦 1 0.00002 111111
状元 六杯黑 4 0.00009 AAAAAA
状元 五红 30 0.00064 44444_
状元 五子登科 150 0.00322 AAAAA_
状元 状元插金花 15 0.00032 444411
状元 四红 360 0.00772 4444__
榜眼 对堂 720 0.01543 123456
探花 三红 2500 0.05358 444___
进士 四进 1875 0.04019 AAAA__
举人 二举 9300 0.19933 44____
秀才 一秀 17400 0.37294 4_____
落榜 落榜 14300 0.30650 ______
状元汇总 561 0.01202