题目:
引用
人和计算机做猜数游戏.人默想一个四位数,由计算机来猜.计算机将所猜的数据显示到屏幕上,并问两个问题:一,有几个数字猜对了;二,猜对的数字中有几个位置也对了.人通过键盘来回答这两个问题。计算机一次又一次地猜,直到猜对位置。
为了简化输入输出,计算机每次输出一个四位数,然后人输入两个用空格分开的数,分别表示有几个数字猜对,有几个数字位置也对。下面有某次猜数过程:人默认的数是3422,奇数行是计算机猜的数,偶数行是人输入的信息。在这个例子中,计算机第五次猜中了数,在人输入4 4后程序结束退出.
请你写一个这样的猜数游戏,看看你所想的数能在几次后被程序猜中。如果在猜数过程中,计算机发现人故意欺骗,输入了不正确的信息,那么程序将输出0然后直接退出.
例子:
6897
0 0
5555
0 0
4444
1 1
4333
2 0
3422
4 4
算法描叙:
每次从剩下的可能值中选择能使下一次筛选后可能值最少(平均意义下)的数去猜。
算法效果:
不甚理想,平均需要5.8步才能猜对。下面是统计数据:
Step[k] n x 表示使用k步猜对的数字有n个,x表示在总游戏数中占得比例:
Step[ 1] 1 0.0001
Step[ 2] 13 0.0013
Step[ 3] 110 0.0110
Step[ 4] 713 0.0713
Step[ 5] 2792 0.2792
Step[ 6] 4440 0.4440
Step[ 7] 1553 0.1553
Step[ 8] 277 0.0277
Step[ 9] 82 0.0082
Step[10] 19 0.0019
Total: 57824 steps 10000 games
Use time: 420266 ms
代码:
import java.util.*;
/**
猜数字游戏
@author Eastsun
@version 2.0 2007/10/15
*/
public class NumberGuess{
public static void main(String[] args){
Scanner s =new Scanner(System.in);
AI solver =new AI();
try{
System.out.println(solver.firstGuess());
int b =s.nextInt();
int a =s.nextInt();
while(!(a==Item.SIZE&&b==Item.SIZE)){
System.out.println(solver.nextGuess(a,b));
b =s.nextInt();
a =s.nextInt();
}
}catch(IllegalArgumentException ex){
System.out.println("0");
}
}
}
class AI{
private static final Item[] ITEMS_BUFFER ={
new Item(56),
new Item(156),
new Item(256),
new Item(145),
new Item(135),
new Item(235),
new Item(123),
new Item(1322),
new Item(1124),
new Item(234),
new Item(2341),
new Item(1342),
new Item(1243),
null,
new Item(1234)
};
//可能的答案
private List<Item> candidates;
//最近一次的猜测
private Item lastGuess;
//已经猜过的次数
private int step;
/**
第一次猜测
*/
public Item firstGuess(){
step =1;
//初始化candidates为所有可能的答案
candidates =new LinkedList<Item>(Item.ALL_POSSIBLE_ITEMS);
//选择"1234"作为第一次的猜测.事实上任意一个四个数字各不相同的都可以,在统计意义下效果一样
lastGuess = new Item(1234); //
return lastGuess;
}
/**
根据上次的猜测结果得到下一次猜测
*/
public Item nextGuess(int a,int b){
step ++;
//根据上次结果对候选集进行过滤
filter(candidates,lastGuess,a,b);
//如果候选集为空,说明输入的数据有误
if(candidates.isEmpty()) throw new IllegalArgumentException();
if(step ==2){
lastGuess = ITEMS_BUFFER[b*(b+1)/2+a];
return lastGuess;
}
//从剩下的候选集中取出权重最小那个作为下一次的猜测
int min =Integer.MAX_VALUE;
for(Item guess : candidates){
int weight =weight(candidates, guess);
if(weight <min){
min =weight;
lastGuess =guess;
}
}
return lastGuess;
}
/**
将list中所有用guess猜答案不是(b,a)的过滤掉
*/
public static void filter(List<Item> list,Item guess,int a,int b){
for(Iterator<Item> itr =list.iterator();itr.hasNext();){
if(!Item.isMatch(itr.next(),guess,a,b)) itr.remove();
}
}
/**
用guess去猜结果集list时的权重,这个值越小越好
*/
public static int weight(List<Item> list,Item guess){
int[] count =new int[(Item.SIZE+1)*(Item.SIZE+2)/2];
for(Item word :list){
int a =Item.getA(word,guess);
int b =Item.getB(word,guess);
int index =(b+1)*b/2 +a; //hash for (b,a)
count[index] ++;
}
int value =0;
for(int c : count)
value += c*c;
return value;
}
}
class Item {
/**
数字位数
*/
public static final int SIZE =4;
//一个包含了所有可能解的不可修改的list
public static final List<Item> ALL_POSSIBLE_ITEMS;
static{
List<Item> list =new ArrayList<Item>(10000);
for(int num =0;num <10000;num ++) //magic number: 10000 is result of 10^Item.SIZE
list.add(new Item(num));
ALL_POSSIBLE_ITEMS =Collections.unmodifiableList(list);
}
//数字的各位,从高位到低位排列
int[] bits;
public Item(int num){
bits =new int[SIZE];
for(int index=SIZE-1;num>0;num/=10)
bits[index--] = num%10;
}
@Override
public String toString(){
StringBuilder sb =new StringBuilder(SIZE);
for(int index =0;index< SIZE;index ++)
sb.append(bits[index]);
return sb.toString();
}
/**
用guess去猜word,结果是否返回(b,a)
*/
public static boolean isMatch(Item word,Item guess,int a,int b){
return (getA(word,guess)==a&&getB(word,guess)==b);
}
/**
word 为谜底,guess去猜.
@return guess与word中位置相同数字也相同的个数
*/
public static int getA(Item word,Item guess){
int count =0;
for(int index =0;index<SIZE; index++)
if(word.bits[index]==guess.bits[index]) count ++;
return count;
}
/**
word 为谜底,guess去猜.
@return 猜对的数字个数
*/
public static int getB(Item word,Item guess){
int count =0;
boolean[] flags =new boolean[SIZE];
for(int i =0;i <SIZE;i ++)
for(int j =0;j <SIZE;j ++)
if((!flags[j])&&word.bits[i]==guess.bits[j]){
count ++;
flags[j] =true;
break;
}
return count;
}
}
分享到:
- 2007-10-15 23:46
- 浏览 2718
- 评论(3)
- 论坛回复 / 浏览 (3 / 3621)
- 查看更多
相关推荐
第9期的智慧擂台题目文件,无疑是为程序员们提供了一个提升技能、检验自身技术水平的平台。这次的主题聚焦在了技术文档的编写、程序员的基本素养以及通过实际题目来锻炼和展示程序员的解决问题能力。 技术文档是...
9期的算法擂台栏目聚焦于“骑士聚会”问题,这是一个涉及算法设计与实现的经典挑战。这个挑战源自于数学游戏,通常与图论或组合优化相关,旨在模拟中世纪骑士在棋盘上的排列方式。 "骑士聚会"问题来源于国际象棋,...
java程序员算法锻炼.
Java程序员面试资料及简历模版 Java程序员面试资料及简历模版 Java程序员面试资料及简历模版 Java程序员面试资料及简历模版 Java程序员面试资料及简历模版 Java程序员面试资料及简历模版 Java程序员面试资料及简历...
《程序员》2004 第10期 PDF
根据提供的文件信息,我们可以推断出这是一本关于Java程序员面试和笔试准备的书籍,作者为何昊。本书可能包含了大量关于Java编程语言的基础知识、高级特性以及与面试相关的技巧和策略等内容。下面将对可能涉及的重要...
Java算法集题大全Java算法集题大全Java算法集题大全Java算法集题大全Java算法集题大全Java算法集题大全Java算法集题大全Java算法集题大全Java算法集题大全Java算法集题大全Java算法集题大全Java算法集题大全Java算法...
《程序员算法趣题——随书源码》是一个与算法相关的学习资源,包含了增井敏克著作《程序员算法趣题》中的实例代码。增井敏克是算法领域知名的专家,他的书籍通常深入浅出,旨在帮助程序员提升算法思维和解决实际问题...
《韩顺平程序员一周玩转算法的PPT、图解及笔记》是针对初学者和有一定编程基础的程序员设计的一套全面的算法学习资源。这个资料包由知名IT教育专家韩顺平老师精心制作,旨在帮助学员在短时间内理解和掌握算法的核心...
2007年《程序员》杂志第10期PART2
java程序员面试交流项目经验java程序员面试交流项目经验java程序员面试交流项目经验java程序员面试交流项目经验java程序员面试交流项目经验java程序员面试交流项目经验java程序员面试交流项目经验java程序员面试交流...
2009年程序员杂志第十期 (希望大家有收获)
程序员实用算法+源码,本书一共七个部分全部下载才可正常解压
Java常用的8大排序算法是程序员必备的技能之一,这些排序算法根据排序过程中是否需要使用额外的存储空间,可以分为内排序和外排序两大类。内排序指所有排序操作都在内存中完成,而外排序则涉及到将数据存放到外部...
《程序员的算法趣题》是一本专门为IT从业者和有志于进入这个领域的学习者准备的算法书籍。它通过一系列有趣且富有挑战性的题目,旨在帮助读者深入理解和掌握计算机科学中的核心算法,提升解决实际问题的能力。这本书...
读书笔记:Java程序员面试笔试宝典——算法实现
【标题】"程序员06第2期" 涉及的是一期专门针对程序员的杂志或期刊,这通常包含了丰富的IT技术文章、行业动态、编程技巧、软件开发实践等内容。"06第2期"表明这是该系列的第六个年份的第二期,可能包含了当年最前沿...
程序员06第10期 免分
在IT行业中,算法是程序员解决问题的关键工具,它们是编程的基础,能够帮助我们高效地处理数据和执行任务。本文档集合中的四个PHP文档深入探讨了程序员应掌握的一些经典算法,这对于提升编程技能至关重要。 首先,...