1. 背景

  中午下楼去吃饭,电梯里看到有人在玩数独,之前也玩过,不过没有用程序去解过,萌生了一个想法,这两天就一直想怎么用程序去解一个数独。要去解开一个数独,首先要先了解数独的游戏规则,这样才能找到对应的算法去解开。以下是本人用Java语言对数独进行的解析,代码只是抛砖引玉,欢迎大神们给指点指点。

2. 数独知识

  数独是源自18世纪瑞士的一种数学游戏。是一种运用纸、笔进行演算的逻辑游戏。玩家需要根据9×9盘面上的已知数字,推理出所有剩余空格的数字,并满足每一行、每一列、每一个粗线宫(3*3)内的数字均含1-9,不重复。
  数独盘面是个九宫,每一宫又分为九个小格。在这八十一格中给出一定的已知数字和解题条件,利用逻辑和推理,在其他的空格上填入1-9的数字。使1-9每个数字在每一行、每一列和每一宫中都只出现一次,所以又称“九宫格”。
 
水平方向有九横行,垂直方向有九纵列的矩形,画分八十一个小正方形,称为九宫格(Grid),如图一所示,是数独(Sudoku)的作用范围。

三行与三列相交之处有九格,每一单元称为小九宫(Box、Block),简称宫,如图四所示

更多关于数独的知识可以查看百度百科

3. 生成随机数独

  在解开一个数独之前,首先要知道数独是怎么生成的,接下来先随机生成一个9*9的数独。

  生成思路:使用嵌套for循环,给每个格子填数,这个格子中的数必是1-9中的某一个数字,在填第n个格子时,要排除行、列、宫中已经存在的数字,在剩下的数字中随机选一个,如果排除掉行、列、宫中的数字后,已经没有可选数字了,说明这个数独生成错了,while循环重新开始生成,直到生成一个可用的数独。这个地方用到了Set集合及集合中的方法,以下是生成数独的代码。

package com.woasis.demo;

import java.util.*;

/**
* 数独
* 1 3 3 4 5 6 7 8 9
* 1. [1, 2, 3, 4, 5, 6, 7, 8, 9]
* 2. [1, 2, 3, 4, 5, 6, 7, 8, 9]
* 3. [1, 2, 3, 4, 5, 6, 7, 8, 9]
* 4. [1, 2, 3, 4, 5, 6, 7, 8, 9]
* 5. [1, 2, 3, 4, 5, 6, 7, 8, 9]
* 6. [1, 2, 3, 4, 5, 6, 7, 8, 9]
* 7. [1, 2, 3, 4, 5, 6, 7, 8, 9]
* 8. [1, 2, 3, 4, 5, 6, 7, 8, 9]
* 9. [1, 2, 3, 4, 5, 6, 7, 8, 9]
*
*/
public class Sudoku { public static void main(String[] args) {
boolean flag = true;
while (flag) {
try {
start();
flag = false;
} catch (ArithmeticException e) {
System.out.println(e);
}
}
} /**
* 开始生成数独
*/
private static void start(){
int[][] source = new int[9][9];
//第i行
for (int i=0; i<9; i++){
// 第i行中的第j个数字
for (int j=0; j<9; j++){
//第i行目前的数组
int[] row = Arrays.copyOf(source[i], j);
int[] column = new int[i];
for (int k=0; k<i; k++){
column[k] = source[k][j];
}
//所在宫
List<Integer> palaceList = new ArrayList<>();
//取整,获取宫所在数据
int palaceRow = i/3;
int palaceColumn = j/3;
for (int m=0; m<3; m++){
for (int n=0; n<3; n++){
palaceList.add(source[palaceRow*3+m][palaceColumn*3+n]);
}
}
source[i][j] = getNumber(row, column, palaceList.stream().mapToInt(Integer::intValue).toArray());;
}
} //打印随机生成的数独数组
for (int i=0; i<source.length; i++){
System.out.println(Arrays.toString(source[i]));
}
} /**
* 从即没有在行也没有在列中,选出一个随机数
* @param row
* @param column
* @return
*/
private static int getNumber(int[] row, int[] column, int[] palace ){
//数组合并,并去重,使用Set集合
Set<Integer> mergeSet = new HashSet<>();
for (int i=0; i<row.length; i++){
mergeSet.add(row[i]);
}
for (int j=0; j<column.length; j++){
mergeSet.add(column[j]);
} for (int k=0; k<palace.length; k++){
mergeSet.add(palace[k]);
}
Set<Integer> source = new HashSet<>();
for (int m=1; m<10; m++){
source.add(m);
}
//取差集
source.removeAll(mergeSet);
int[] merge = source.stream().mapToInt(Integer::intValue).toArray();
//随机返回一个下标
return merge[getRandomCursor(merge.length)];
} /**
* 获取一个随机下标
* @param length
* @return
*/
public static int getRandomCursor(int length) {
return Math.abs(new Random().nextInt())%length;
}
}

如下图是代码执行后生成的随机数独,行、列、宫中都是1-9个数字,没有重复。

4. 数独的解析

  数独已经可以生成了,现在就对数独进行解析,首先声明一下,接下来的方法可能对一些数独是解不开的,解开数独不是唯一目的,而是在解析数独中对一些Java知识进行回顾和学习。采用的是隐形唯一候选数法,什么是唯一候选数法呢,就是某个数字在某一行列宫格的候选数中只出现一次,就是这个格子只有一个数可选了,那这个格子里就只能填这个数,这就是唯一候选数法,其实也是排除法。参照的这篇文章进行的一次数独解析,数独解题方法大全,可以参考学习一下。

  解题思路:

  1. 要解析的数独,与数独对应的隐形数组;
  2. 排除掉隐形数组中的数字,哪些数字需要排除呢,就是数独中已有的数字,要排除该数字所在的行、列、宫。例如,如下图R4C4格是2,则R4行、C4列以及2所在的宫除了R4C4格子之外,其余的候选数中都不能有2这个数字了。

  3. 排除一次完成后,看剩下的隐形数组中有没有剩下的单个数,如果有则剩下的这个候选数字就是该位置所要填的数字,有的话需要递归一次2步骤;查看行中有没有唯一的单数,如果有递归一次2步骤;查看列中有没有唯一的单数,如果有递归一次2步骤。

  4. 排除以部门隐形数字之后,有一些数字是不好排除的,就是一些对数,对数就是在一个宫两个格子,候选数字都是AB,要么这个格子是A要么另一个格子是B。到这个地方之后不好排除,只能用试探法,假如一个格子是A,那么另一个格子是B,这样去试探,如果试探一次后发现试探的对的,那么就确认这种试探是可行的,如果不对,则数值对换。

  5. 步骤4试探对之后,再从步骤2进行递归,直到获得最终解。

以下是完整代码:

  其中demo中解析的数独就是数独解题方法大全中隐形唯一候选数法中的一个例子。

 package com.woasis.demo;

 import java.util.*;

 public class SudokuCrack {
public static void main(String[] args) {
//生成候选数字表,9行9列,每个格子有9个数字
int[][][] candi = new int[9][9][9];
//初始化候选数字表
for (int i=0; i<9; i++){
for (int j=0; j<9; j++){
candi[i][j] = new int[]{1,2,3,4,5,6,7,8,9};;
}
}
int[][] sudo = {
{0,0,9,6,0,0,0,3,0},
{0,0,1,7,0,0,0,4,0},
{7,0,0,0,9,0,0,8,0},
{0,7,0,0,8,0,5,0,0},
{1,0,0,0,4,0,0,2,0},
{0,2,0,0,1,0,9,0,0},
{5,0,0,0,0,9,0,0,0},
{6,0,0,0,0,3,0,0,2},
{4,0,0,0,0,0,0,0,1}
}; if (isOkSudo(candi, sudo)){
System.out.println("校验是不是一个合法数独:是");
}else {
System.out.println("校验是不是一个合法数独:不是");
return;
} crack(candi, sudo); //获取隐形数组中两个相等的数
List<CandiInfo> equalCandi = getEqualCandi(candi,sudo); //获取其中一个进行试探。
for (CandiInfo info : equalCandi){ //获取坐标
String[] location = info.location.split("\\|");
String[] ALocation = location[0].split("-");
int aRow = Integer.parseInt(ALocation[0]);
int aColumn = Integer.parseInt(ALocation[1]);
String[] BLocation = location[1].split("-");
int bRow = Integer.parseInt(BLocation[0]);
int bColumn = Integer.parseInt(BLocation[1]);
//获取数据
int[] data = info.nums.stream().mapToInt(Integer::intValue).toArray(); System.out.println("开始进行试探:data="+data[0]+", "+data[1]+" 位置:"+aRow+"-"+aColumn+", "+bRow+"-"+bColumn); if(isRight(candi, sudo,aRow, aColumn, bRow, bColumn, data[0], data[1])){
modifySudoAndCandi(candi, sudo, aRow, aColumn, data[0]);
modifySudoAndCandi(candi, sudo, bRow, bColumn, data[1]);
}else{
modifySudoAndCandi(candi, sudo, aRow, aColumn, data[1]);
modifySudoAndCandi(candi, sudo, bRow, bColumn, data[0]);
}
crack(candi, sudo);
} System.out.println("解析完成:");
for (int i=0; i<9; i++){
System.out.println(Arrays.toString(sudo[i]));
}
} /**
* 试探这样的组合是否正确
* @param candi
* @param sudo
* @param aRow
* @param aColumn
* @param bRow
* @param bColumn
* @param data0
* @param data1
* @return
*/
private static boolean isRight(int[][][] candi, int[][] sudo, int aRow, int aColumn, int bRow, int bColumn, int data0, int data1){
int[][][] deepCandiCopy = new int[9][9][9];
for (int i=0; i<9; i++){
deepCandiCopy[i] = candi[i].clone();
}
int[][] deepSudoCopy = new int[9][9];
for (int i=0; i<9; i++){
deepSudoCopy[i]= sudo[i].clone();
}
modifySudoAndCandi(deepCandiCopy, deepSudoCopy, aRow, aColumn, data0);
modifySudoAndCandi(deepCandiCopy, deepSudoCopy, bRow, bColumn, data1); crack(deepCandiCopy, deepSudoCopy); return isOkSudo(deepCandiCopy,deepSudoCopy);
} /**
* 隐藏数法解析数独
* @param candi 隐藏数数组
* @param sudo 要解的数独
*/
private static void crack(int[][][] candi, int[][] sudo){ eliminateCandidateNumbers(candi, sudo); //一轮结束后,查看隐形数组里有没有单个的,如果有继续递归一次
boolean flag = false;
for (int k=0; k<9; k++){
for (int q=0; q<9; q++){
int f = sudo[k][q];
if (f == 0){
int[] tmp = candi[k][q];
Set<Integer> s = new HashSet<>();
for (int t=0; t<tmp.length; t++){
if (tmp[t]>0){
s.add(tmp[t]);
}
}
//说明有单一成数据可以用的
if (s.size() == 1){
flag = true;
modifySudoAndCandi(candi, sudo, k, q, s.stream().mapToInt(Integer::intValue).toArray()[0]);
}
}
}
}
//如果有确定的单个数,进行递归一次
if (flag){
crack(candi, sudo);
}
//查看行有没有唯一数字,有就递归一次
flag = checkRow(candi, sudo);
if (flag){
crack(candi, sudo);
}
//查看列有没有唯一数字,有就递归一次
flag = checkColumn(candi, sudo);
if (flag){
crack(candi, sudo);
}
} /**
* 剔除数组中的候选数字,剔除行、列、宫
* @param candi
* @param sudo
*/
private static void eliminateCandidateNumbers(int[][][] candi, int[][] sudo){
for (int i=0; i<9; i++){
for (int j=0; j<9; j++){
int num = sudo[i][j];
//剔除备选区数字
if (num>0){
candi[i][j] = new int[]{0,0,0,0,0,0,0,0,0};
for (int m=0; m<9; m++){
int[] r = candi[i][m];
r[num-1] = 0;
int[] c = candi[m][j];
c[num-1] = 0;
}
//摒除宫里的唯一性
//取整,获取宫所在数据
int palaceRow = i/3;
int palaceColumn = j/3;
for (int m=0; m<3; m++){
for (int n=0; n<3; n++){
int[] p = candi[palaceRow*3+m][palaceColumn*3+n];
p[num-1] = 0;
}
}
}
}
}
} /**
* 修改数独的值并剔除隐形数字
* @param candi
* @param sudo
* @param row
* @param column
* @param v
*/
private static void modifySudoAndCandi(int[][][] candi, int[][] sudo, int row, int column, int v){
//修改数独的值
sudo[row][column] = v; //剔除备选区数字
candi[row][column] = new int[]{0,0,0,0,0,0,0,0,0};
for (int m=0; m<9; m++){
int[] r = candi[row][m];
r[v-1] = 0;
int[] c = candi[m][column];
c[v-1] = 0;
}
//摒除宫里的唯一性
//取整,获取宫所在数据
int palaceRow = row/3;
int palaceColumn = column/3;
for (int m=0; m<3; m++){
for (int n=0; n<3; n++){
int[] p = candi[palaceRow*3+m][palaceColumn*3+n];
p[v-1] = 0;
}
}
} /**
* 查看行中的隐形数组有没有唯一存在的候选值
* @param candi
* @param sudo
* @return
*/
private static boolean checkRow(int[][][] candi, int[][] sudo){
boolean flag = false;
for (int i=0; i<9; i++){
Map<String ,Set<Integer>> candiMap = new HashMap<>();
int[] row = sudo[i];
for (int j=0; j<9; j++){
if (row[j]==0){
int[] tmp = candi[i][j];
Set<Integer> set = new HashSet<>();
for (int k=0; k<tmp.length; k++){
if (tmp[k]>0) {
set.add(tmp[k]);
}
}
candiMap.put(String.valueOf(i)+"-"+String.valueOf(j), set);
}
}
if (candiMap.size()>0) {
Set<String> keys = candiMap.keySet();
Iterator iterator = keys.iterator();
while (iterator.hasNext()){
String tKey = (String) iterator.next();
//要查看的集合
Set<Integer> set = deepCopySet(candiMap.get(tKey));
//深复制
Set<String> tmpKeys = candiMap.keySet();
Iterator tmpKeyIterator =tmpKeys.iterator();
while (tmpKeyIterator.hasNext()){
String tmpKey = (String) tmpKeyIterator.next();
//取交集
if (!tKey.equals(tmpKey)) {
set.removeAll(candiMap.get(tmpKey));
}
}
//交集取完,集合空了,看下一个结合有没有
if (set.size() == 0){
continue;
}else {
//还剩一个唯一值
if (set.size() == 1){
String[] ks = tKey.split("-");
flag = true;
modifySudoAndCandi(candi, sudo, Integer.parseInt(ks[0]),Integer.parseInt(ks[1]), set.stream().mapToInt(Integer::intValue).toArray()[0] );
}
}
}
}
}
return flag;
} /**
* 查看列中的隐形数组有没有唯一存在的候选值
* @param candi
* @param sudo
* @return
*/
private static boolean checkColumn(int[][][] candi, int[][] sudo){
boolean flag = false;
for (int i=0; i<9; i++){
Map<String ,Set<Integer>> candiMap = new HashMap<>();
for (int j=0; j<9; j++){
if (sudo[j][i]==0){
int[] tmp = candi[j][i];
Set<Integer> set = new HashSet<>();
for (int k=0; k<tmp.length; k++){
if (tmp[k]>0) {
set.add(tmp[k]);
}
}
candiMap.put(String.valueOf(i)+"-"+String.valueOf(j), set);
}
}
if (candiMap.size()>0) {
Set<String> keys = candiMap.keySet();
Iterator iterator = keys.iterator();
while (iterator.hasNext()){
String tKey = (String) iterator.next();
//要查看的集合
Set<Integer> set = deepCopySet(candiMap.get(tKey));
//深复制
Set<String> tmpKeys = candiMap.keySet();
Iterator tmpKeyIterator =tmpKeys.iterator();
while (tmpKeyIterator.hasNext()){
String tmpKey = (String) tmpKeyIterator.next();
//取交集
if (!tKey.equals(tmpKey)) {
set.removeAll(candiMap.get(tmpKey));
}
}
//交集取完,集合空了,看下一个结合有没有
if (set.size() == 0){
continue;
}else {
//还剩一个唯一值
if (set.size() == 1){
String[] ks = tKey.split("-");
flag = true;
modifySudoAndCandi(candi,sudo, Integer.parseInt(ks[1]),Integer.parseInt(ks[0]),set.stream().mapToInt(Integer::intValue).toArray()[0]);
}
}
}
}
}
return flag;
} /**
* 获取隐形数字中宫中两个相等的数字
* @return
*/
private static List<CandiInfo> getEqualCandi(int[][][] candi, int[][] sudo){
//找到两个相等数字
//遍历宫
List<CandiInfo> maps = new ArrayList<>();
for (int m=0; m<3; m++){
for (int n=0; n<3; n++){
Map<String, Set<Integer>> palaceMap = new HashMap<>();
for (int i=0; i<3; i++){
for (int j=0; j<3; j++){
int sudoRow = m*3 + i;
int sudoColumn = n*3 +j;
if (sudo[sudoRow][sudoColumn] == 0) {
int[] tmpX = candi[sudoRow][sudoColumn];
Set<Integer> set = new HashSet<>();
for (int k=0; k<tmpX.length; k++){
if (tmpX[k]>0) {
set.add(tmpX[k]);
}
}
if (set.size() == 2) {
palaceMap.put(String.valueOf(sudoRow) + "-" + String.valueOf(sudoColumn), set);
}
}
}
} Set<String> pSet = palaceMap.keySet();
Iterator pIterator = pSet.iterator();
while (pIterator.hasNext()){
String key = (String) pIterator.next();
Iterator tmpIterator = pSet.iterator();
while (tmpIterator.hasNext()){
String tmpKey = (String) tmpIterator.next();
if (!key.equals(tmpKey)){
Set<Integer> tmpIntSet = palaceMap.get(tmpKey);
Set<Integer> palaceIntSet = deepCopySet(palaceMap.get(key));
palaceIntSet.removeAll(tmpIntSet);
//说明两个集合相等
if (palaceIntSet.size() == 0){
CandiInfo candiInfo = new CandiInfo();
candiInfo.location = key+"|"+tmpKey;
candiInfo.nums = palaceMap.get(key);
maps.add(candiInfo);
}
}
}
}
}
}
List<CandiInfo> infos = new ArrayList<>();
CandiInfo candiInfo = null;
for (CandiInfo info : maps){
if (candiInfo == null){
candiInfo = info;
}else {
if (candiInfo.nums.equals(info.nums)) {
infos.add(info);
}
candiInfo = info;
}
}
return infos;
} /**
* 校验这个数独是不是还满足数独的特点
* 思路:
* 1. 校验行和列有没有重复的数字
* 2. 校验数独是0的格子,对应的隐形数组还有没有值,如果没有候选值,肯定是某一个地方填错了
* @param candi 隐形数组
* @param sudo 数独二维数组
* @return
*/
private static boolean isOkSudo(int[][][] candi, int[][] sudo){
boolean flag = true;
for (int i=0; i<9; i++){
//校验行
Set<Integer> rowSet = new HashSet<>();
//校验列
Set<Integer> clumnSet = new HashSet<>();
for (int j=0; j<9; j++){
int rowV = sudo[i][j];
int cloumV = sudo[j][i];
if (rowV>0){
if (!rowSet.add(rowV)) {
flag = false;
break;
}
}
if (cloumV>0){
if (!clumnSet.add(cloumV)) {
flag = false;
break;
}
} }
if (!flag){
break;
}
}
//校验隐形数字是否为空
for (int m=0; m<9; m++){
for (int n=0; n<9; n++){
if (sudo[m][n] == 0){
int[] s = candi[m][n];
Set<Integer> set = new HashSet<>();
for (int p=0; p<s.length; p++){
if (s[p]>0){
set.add(s[p]);
}
}
if (set.size() == 0){
flag = false;
break;
}
}
}
}
return flag;
} /**
* 深度复制set集合
* @param source
* @return
*/
private static Set<Integer> deepCopySet(Set<Integer> source){
Set<Integer> deepCopy = new HashSet<>();
Iterator iterator = source.iterator();
while (iterator.hasNext()){
deepCopy.add((Integer) iterator.next());
}
return deepCopy;
} public static class CandiInfo{
String location;
Set<Integer> nums;
}
}

以下是解析出的结果:

 5. 经验总结

  从有解析数独这个想法,到代码实现,大约经历了3天左右,在这个过程中会想一下怎么去构造解析,以及代码的逻辑,和解题的思路。对其中的收获就是Set集合的用法,数组的深浅复制,值传递引用传递等,以及怎么去构建一个数据结构来表示想要表达的东西。有些东西确实是了解,但是真正用的时候可能觉得自己知道的还不够,知识需要去积累学习,希望通过一个数独的解题思路,来温故一些基础知识。感谢阅读!

一次Java解析数独的经历的更多相关文章

  1. Java解析OFFICE(word,excel,powerpoint)以及PDF的实现方案及开发中的点滴分享

    Java解析OFFICE(word,excel,powerpoint)以及PDF的实现方案及开发中的点滴分享 在此,先分享下写此文前的经历与感受,我所有的感觉浓缩到一个字,那就是:"坑&qu ...

  2. java解析xml的三种方法

    java解析XML的三种方法 1.SAX事件解析 package com.wzh.sax; import org.xml.sax.Attributes; import org.xml.sax.SAXE ...

  3. atitit.java解析sql语言解析器解释器的实现

    atitit.java解析sql语言解析器解释器的实现 1. 解析sql的本质:实现一个4gl dsl编程语言的编译器 1 2. 解析sql的主要的流程,词法分析,而后进行语法分析,语义分析,构建sq ...

  4. java 解析XML文档

    Java 解析XML文档 一.解析XML文档方式: 1.DOM方式:将整个XML文档读取到内存中,按照XML文件的树状结构图进行解析. 2.SAX方式:基于事件的解析,只需要加载XML中的部分数据,优 ...

  5. Java 解析 XML

    Java 解析 XML 标签: Java基础 XML解析技术有两种 DOM SAX DOM方式 根据XML的层级结构在内存中分配一个树形结构,把XML的标签,属性和文本等元素都封装成树的节点对象 优点 ...

  6. JAVA解析XML的四种方式

    java解析xml文件四种方式 1.介绍 1)DOM(JAXP Crimson解析器) DOM是用与平台和语言无关的方式表示XML文档的官方W3C标准.DOM是以层次结构组织的节点或信息片断的集合.这 ...

  7. JSON 之JAVA 解析

    一.   JSON (JavaScript Object Notation)一种简单的数据格式,比xml更轻巧. Json建构于两种结构:     1.“名称/值”对的集合(A collection ...

  8. Java解析XML汇总(DOM/SAX/JDOM/DOM4j/XPath)

    [目录] 一.[基础知识——扫盲] 二.[DOM.SAX.JDOM.DOM4j简单使用介绍] 三.[性能测试] 四.[对比] 五.[小插曲XPath] 六.[补充] 关键字:Java解析xml.解析x ...

  9. Java 解析epub格式电子书,helloWorld程序,附带源程序和相关jar包

    秀才坤坤出品 一.epub格式电子书 相关材料和源码均在链接中可以下载:http://pan.baidu.com/s/1bnm8YXT 包括 1.JAVA项目工程test_epub,里面包括了jar包 ...

随机推荐

  1. 5-具体学习git--分支冲突,merge合并

    修改1.py: 然后提交修改: git commit -am "change 4 in master" 之后移到dev分支上: 哎呀,这个乱了. 人家意思是都基于c1分出来两个枝, ...

  2. php 微信登录 公众号 获取用户信息 微信网页授权

    php 微信登录 公众号 获取用户信息 微信网页授权 先自己建立两个文件: index.php  和  getUserInfo.php index.php <?php //scope=snsap ...

  3. javase jdk 环境变量 涵义

    jdk环境变量配置:path:jdk安装所在目录下的bin路径-->因为环境变量path下放置的是操作系统执行的.exe文件,jdk中bin中放的是可执行的.exe文件,所以要把这个路径放置到p ...

  4. Effective C++ 随笔(3)

    条款12: 以对象管理资源 两种只能指针: std:auto_ptr<> 当使用copy操作室,原先的智能指针指向为null std:tr1:share_ptr<int> sp ...

  5. 【ORA错误大全】 ORA-19527

    在做主备切换的时候,需要将备库的联机日志文件清除(clear online redo logfile),为了加快switchover的速度,Oracle10g在将备库置于manged standby状 ...

  6. 将电脑信息上传到中国移动ONENET平台

    用两个小时做的 可以用在服务器远程运维等环境非常方便 需要源码的可以联系NBDX123

  7. hdu 5059 简单字符串处理

    http://acm.hdu.edu.cn/showproblem.php?pid=5059 确定输入的数是否在(a,b)内 简单字符串处理 #include <cstdio> #incl ...

  8. 分形之希尔伯特-皮亚诺(Hilbert-Peano)曲线

    1890年,意大利数学家皮亚诺(Peano G)发明能填满一个正方形的曲线,叫做皮亚诺曲线.后来,由希尔伯特作出了这条曲线,又名希尔伯特曲线.Hilbert-Peano曲线是一种分形图形,它可以画得无 ...

  9. 在mac下配置Andriod环境 包括eclipse和andriod studio

    1 前提 已经配置好了java的环境,课上要使用andriod开发. 2 步骤 2.1 eclipse 2.1.1先安装adt,adt是一个在eclipse中开发andriod的插件.由于墙,我是从其 ...

  10. 关于2011年meng-meng组产品《豆酱》的Review

    这个组是一个做手机应用的组,比较有特色. 经过我们的一致讨论,得出我们组对前辈的有关选题.团队.产品等几个方面的看法,以及我们的感想. 选题的特点: 这个选题对于一个短期项目来说是很合适的,经过较为详 ...