求救:请教一个猜数字算法

wanggangtjdx8211 2003-08-28 07:56:30
猜数字游戏大家都玩过吧?没玩过的我就介绍一下吧,玩过的就不用看了。
游戏简介:
  一方预先想好排好序的4个数字(0-9之内,不可重复),由另一方来猜。每猜一次,出题方都给出相应提示:?a?b。其中,a代表数字和位置均对,b代表数字对,但位置不对,?视体情况而定。因此,当一方猜对时,结果应为:4a0b。
  示例如下:
正确答案:5678
第一次猜:6928    1a1b
第二次猜:5061    0a2b
......

  现要编写一个猜数字游戏,由人出题,电脑来猜。请为电脑编写一个猜数字算法,以确保:对任意情况,均能在10次之内猜出正确答案。
  先谢谢大家了!

...全文
845 28 打赏 收藏 转发到动态 举报
写回复
用AI写文章
28 条回复
切换为时间正序
请发表友善的回复…
发表回复
birth_chen 2003-10-30
  • 打赏
  • 举报
回复
mark
wanggangtjdx8211 2003-10-18
  • 打赏
  • 举报
回复
skywind,这位朋友,也许你没注意看我的算法和程序,你所写的算法和程序和我的是完全一样的。而我所说的最多9步,就是在这个程序执行过程中证明的。你的统计恐怕有误,因为有5个数要用9步才能猜出来。
jwd_1_cool 2003-10-14
  • 打赏
  • 举报
回复
楼上的楼上讲的master-mind

这道ACM预赛的题目讲的就是猜数字。你们可以解解看啊!
The Game of Master-Mind

If you want to buy a new cellular phone, there are many various types to choose from. To decide which one is the best for you, you have to consider several important things: its size and weight, battery capacity, WAP support, colour, price. One of the most important things is also the list of games the phone provides. Nokia is one of the most successful phone makers because of its famous Snake and Snake II. ACM wants to make and sell its own phone and they need to program several games for it. One of them is Master-Mind, the famous board logical game.

The game is played between two players. One of them chooses a secret code consisting of P ordered pins, each of them having one of the predefined set of C colours. The goal of the second player is to guess that secret sequence of colours. Some colours may not appear in the code, some colours may appear more than once.

The player makes guesses, which are formed in the same way as the secret code. After each guess, he/she is provided with an information on how successful the guess was. This feedback is called a hint. Each hint consists of B black points and W white points. The black point stands for every pin that was guessed right, i.e. the right colour was put on the right position. The white point means right colour but on the wrong position. For example, if the secret code is "white, yellow, red, blue, white" and the guess was "white, red, white, white, blue", the hint would consist of one black point (for the white on the first position) and three white points (for the other white, red and blue colours). The goal is to guess the sequence with the minimal number of hints.

The new ACM phone should have the possibility to play both roles. It can make the secret code and give hints, but it can also make its own guesses. Your goal is to write a program for the latter case, that means a program that makes Master-Mind guesses.


Input Specification
There is a single positive integer T on the first line of input. It stands for the number of test cases to follow. Each test case describes one game situation and you are to make a guess. On the first line of each test case, there are three integer numbers, P, C and M. P ( 1 <= P <= 10) is the number of pins, C (1 <= C <= 100) is the number of colours, and M (1 <= M <= 100) is the number of already played guesses.

Then there are 2 x M lines, two lines for every guess. At the first line of each guess, there are P integer numbers representing colours of the guess. Each colour is represented by a number Gi, 1 <= Gi <= C. The second line contains two integer numbers, B and W, stating for the number of black and white points given by the corresponding hint.

Let's have a secret code S1, S2, ... SP and the guess G1, G2, ... GP. Then we can make a set H containing pairs of numbers (I,J) such that SI = GJ, and that any number can appear at most once on the first position and at most once on the second position. That means for every two different pairs from that set, (I1,J1) and (I2,J2), we have I1 <> I2 and J1 <> J2. Then we denote B(H) the number of pairs in the set, that meet the condition I = J, and W(H) the number of pairs with I <> J.

We define an ordering of every two possible sets H1 and H2. Let's say H1 <= H2 if and only if one of the following holds:

B(H1) < B(H2), or
B(H1) = B(H2) and W(H1) <= W(H2)
Then we can find a maximal set Hmax according to this ordering. The numbers B(Hmax) and W(Hmax) are the black and white points for that hint.

Output Specification
For every test case, print the line containing P numbers representing P colours of the next guess. Your guess must be valid according to all previous guesses and hints. The guess is valid if the sequence could be a secret code, i.e. the sequence was not eliminated by previous guesses and hints.

If there is no valid guess possible, output the sentence You are cheating!. If there are more valid guesses, output the one that is lexicographically smallest. I.e. find such guess G that for every other valid guess V there exists such a number I that:

GJ = VJ for every J<I, and
GI<VI.
Sample Input
3
4 3 2
1 2 3 2
1 1
2 1 3 2
1 1
4 6 2
3 3 3 3
3 0
4 4 4 4
2 0
8 9 3
1 2 3 4 5 6 7 8
0 0
2 3 4 5 6 7 8 9
1 0
3 4 5 6 7 8 9 9
2 0

Sample Output
1 1 1 3
You are cheating!
9 9 9 9 9 9 9 9

azure711 2003-10-13
  • 打赏
  • 举报
回复
#include<stdio.h>
#include<stdlib.h>
#include<dos.h>
#include<conio.h>
#define VIDEO 0x10

int guess, position, numbers;
int xx[9001];
int n=9000;

void movetoxy(int x, int y)
{
union REGS regs;
regs.h.ah=2;
regs.h.dh=x;
regs.h.dl=y;
regs.h.bh=0;
int86(VIDEO, ®s, ®s);
}

void int2char(int emp, char *temp)
{
int i=3;
while(emp)
{
temp[i--]=(char)(emp%10+48);
emp/=10;
}
}

int test1(char *now, char *temp)
{
int j, k, count=0;
int mark[4];
for(j=0;j<4;j++)
mark[j]=0;
for(j=0;j<4;j++)
{
k=0;
while(k<4&&(temp[k]!=now[j]||mark[k]==1))
k++;
if(k<4)
{
mark[k]=1;
count++;
}
}
return count;
}

int test2(char *now, char *temp)
{
int i, count=0;
for(i=0;i<4;i++)
if(now[i]==temp[i])
count++;
return count;
}

void init()
{
int i;
for(i=1;i<=9000;i++)
xx[i]=i+999;
}


void random_0_9999()
{
randomize();
do
{
guess=random(n);
}while(guess<1);
}

int get_reply()
{
movetoxy(3, 6);
printf("Output:%6d", xx[guess]);
do
{
movetoxy(5, 6);
printf("How many numbers is right ?");
scanf(" %d", &numbers);
movetoxy(7, 6);
printf("How many position is right?");
scanf(" %d", &position);
/*if(numbers<position)
{
movetoxy(8, 30);
printf("Input Error!");
}*/
}while(numbers<0||numbers>4||position<0||position>4||numbers<position);
if(position==4&&numbers==4)
return 1;
return 0;
}

void delete()
{
char now[4], temp[4];
int yy[9001];
int i, count=1;
int2char(xx[guess], now);
for(i=1;i<n;i++)
{
int2char(xx[i], temp);
if(test2(now, temp)==position&&test1(now, temp)==numbers)
yy[count++]=xx[i];
}
for(i=1;i<count+1;i++)
xx[i]=yy[i];
n=count;
}

main()
{
int count=0;
clrscr();
init();
do
{
random_0_9999();
count=get_reply();
if(count==1)
break;
delete();
}while(n!=1);
clrscr();
movetoxy(8, 27);
if(count)
printf("find the Number");
else
printf("The Number Not Exist");
getchar();
getchar();
clrscr();
}
psprite 2003-10-13
  • 打赏
  • 举报
回复
#define mun 1000*h+100*i+10*j+k
int X[7],A[7],B[7],s,f;
int end();
int fs(int h,int i,int j,int k);
main()
{int i,j,k,h,y;
printf("\n***欢迎使用文曲星猜数字游戏超级无敌魔鬼终结者程序***\n ==更新版本2.02==\n");
top:;
for(s=1;s<=7;s++)
{f=0;
if(s>=2)
{printf("\n\t<如果踩到狗屎蒙着了,再来一次请按'1',退出请按'0'>\n");}
printf("\n\n\t\t 请输入第 %d 组测试数据: \n",s);
scanf(" %d",&X[s]);
if(X[s]==1){printf("\n\n\n\n\n\n 新的一轮开始了,ready,go!");goto top;}
if(X[s]==0)goto end;

printf(" A : ");scanf("%d",&A[s]);
printf(" B : ");scanf("%d",&B[s]);
printf(" 满足条件的数组有:\n");
for(h=0;h<=9;h++){for(i=0;i<=9;i++){for(j=0;j<=9;j++){for(k=0;k<=9;k++)
{if(i!=j&&j!=h&&j!=k&&k!=h&&h!=i&&i!=k&&mun>=123&&mun<=9876)fs(h,i,j,k);
continue;}}}}
printf("\n ---共%d组数据\n",f);
if(f==1){
printf("\n 你要找的就是它了!!再来一次请按'1',退出请按'0'\n");
scanf("%d",&y);
if(y==1){printf("\n 新的一轮开始了,ready,go!");goto top;}
if(y==0)goto end;
}

}
end:;
}
int fs(int h,int i,int j,int k)
{int a[4],b[4],p,q,m,n,z;
a[0]=h;a[1]=i;a[2]=j;a[3]=k;
for(z=1;z<=s;z++){
b[0]=X[z]/1000;
b[1]=X[z]/100%10;
b[2]=X[z]%100/10;
b[3]=X[z]%10;
m=0;n=0;for(p=0;p<=3;p++){for(q=0;q<=3;q++)
{if(a[p]==b[q]&&p==q)m++;
if(a[p]==b[q]&&p!=q)n++;}}
if(A[z]==m&&B[z]==n)continue;
else goto fs;}
if(mun<1000)printf(" 0%d",mun);
else printf(" %d",mun);
f++;
fs:;
}


呵呵不好意思大家见笑了,本人用tc做的一个小程序,跟前几位兄长的比起来很幼稚的,不过小皮常用他以自娱,呵呵
由于版面有限,我把程序紧凑了一下,可能有些乱,请见谅!
stonemason 2003-10-09
  • 打赏
  • 举报
回复
有个叫Hunger的写了一个叫mmnd(mastermind,游戏的名字),遗憾,看不懂。但很厉害。
他把猜解过程先计算出来放到一个数据文件中,这样产生文件很费时间,但猜解很快。不是简单的多重循环,而是由某种算法支持,我猜是枚举所有组合,然后回朔。
用google 搜mmnd可是找出来。源码里没注释,看不懂,555555555。
看完了把算法讲讲好不好?
skywind 2003-09-15
  • 打赏
  • 举报
回复
哦,有道理啊~~ -_- 汗
theoldman 2003-09-15
  • 打赏
  • 举报
回复
skywind(今何再) :您好!我想你的程序恐怕未能足以证明7步内得出答案。
不妨把 :if (step>0&&step<=7) times[step]++;
改为: if (step>0&&step<=9) times[step]++;
执行结果为:
-- END --
test 9876
test nums=5040
find in 0 step is 0
find in 1 step is 1
find in 2 step is 13
find in 3 step is 108
find in 4 step is 596
find in 5 step is 1668
find in 6 step is 1768
find in 7 step is 752
find in 8 step is 129
find in 9 step is 5

这样总数才是 5040 。

(如有说得不妥之处还望指教)

wanggangtjdx8211 2003-09-14
  • 打赏
  • 举报
回复
上一个程序是第一次编的,用到了4重循环,较烦,下面的是第二次编的精简版
(推荐!)
#include<iostream.h>
#include<stdio.h>
#include<string.h>

struct RecordNode
{
int num;
int a;
int b;
int digit[4];
};


void Separate(RecordNode &Node)
{
Node.digit[0] = Node.num /1000;
Node.digit[1] = (Node.num /100)-(10*Node.digit[0]);
Node.digit[2] = (Node.num /10)-(100*Node.digit[0])-(10*Node.digit[1]);
Node.digit[3] = Node.num-(1000*Node.digit[0])-(100*Node.digit[1])-(10*Node.digit
[2]);
}


void Compare(RecordNode Base,RecordNode &Test)
{//test a & b of Test.num,(compare with Base)
//suppose the nodes' num have been separated,and there are no same digits
Test.a=0;
Test.b=0;
// cout<<"base:"<<Base.num<<" test:"<<Test.num<<" ";
for(int i=0;i<4;i++)
{
if(Base.digit[i]==Test.digit[i]) //get_A_part
{
Test.a++;
}
for( int j=0; j<4; j++) //get_B_part
{
if(j!=i)
{
if(Base.digit[i]==Test.digit[j])
{
Test.b++;
}
}
}//for(j..)
}//for(i..), the 4 digits in Base
// cout<<Test.a<<" A "<<Test.b<<" B "<<endl;
}//Compare(Base,&Test)


int Repeated(RecordNode Node)
{//judge whether digit No.n(from 0) is the same as others
for(int outer=0; outer<3; outer++)
{
for(int inner=outer+1; inner<4; inner++)
{
if(Node.digit[outer]==Node.digit[inner])
{
return 1;
}
}
}
return 0;
}//Repeated(...)


void GuessNum()
{
struct RecordNode Record[10];
struct RecordNode CurNum;
int step=1;
for(CurNum.num=0123; CurNum.num<=9876; CurNum.num++) //try possible ones
{
Separate(CurNum);
if(!Repeated(CurNum))
{
int RecordNum=0;
if(step!=1) //need to check records
{
RecordNum=step-1;
do
{
Separate(Record[RecordNum-1]);
Compare(Record[RecordNum-1],CurNum);
RecordNum--;
}while(RecordNum>0 && CurNum.a==Record[RecordNum].a
&& CurNum.b==Record[RecordNum].b);
}//if(step!=1)

if(step==1 || (CurNum.a==Record[RecordNum].a
&& CurNum.b==Record[RecordNum].b)) //possible answer
{
cout<<"step "<<step<<": ";
if(CurNum.digit[0]==0) //begin with '0'
{
cout<<'0';
}
cout<<CurNum.num<<"?"<<endl;
cin>>CurNum.a>>CurNum.b;
while(CurNum.a + CurNum.b >4) //illegal input
{
cout<<"check your answer!"<<endl;
cout<<"step "<<step<<": ";
if(CurNum.digit[0]==0) //begin with '0'
{
cout<<'0';
}
cout<<CurNum.num<<"?"<<endl;
cin>>CurNum.a>>CurNum.b;
}

if(CurNum.a==4 && CurNum.b==0) //suppose type correctly,not check records
{
cout<<"Got it! ^-^"<<endl;
return; //needn't record the final answer!
}
else //it's not the answer,record it
{
Record[step-1]=CurNum;
step++;
}
}//if(... it may be the answer)

if(step>10)
{
cout<<"Fail to find it!"<<endl;return;
}
}//if(!Repeated)
}//for(CurNum.num...)

if( step<10 ) //can't get any number in 0123 ~ 9876
{
cout<<"Are you sure your answer's right?"<<endl;
cout<<"This is the list of your answer:"<<endl;
for(int seqNum=0; seqNum<step-1; seqNum++) //list records for user to check
{
cout<<"step"<<seqNum+1<<": ";
if(Record[seqNum].digit[0]==0)
{
cout<<'0';
}
cout<<Record[seqNum].num<<" ";
cout<<Record[seqNum].a<<'A'<<Record[seqNum].b<<'B'<<endl;
}
}
}//GuessNum()


void main(void)
{
cout<<"I'm going to guess the number you get in your mind."<<endl;
char c;
do
{
cout<<"Continue?(Y/N)"<<endl;
cin>>c;
if(c=='Y' || c=='y')
{
cout<<"Think of a number,and tell me 'a b ' for my answer."<<endl;
GuessNum();
}
}while(c!='n' && c!='N');
}
wanggangtjdx8211 2003-09-14
  • 打赏
  • 举报
回复
以下是我的猜数字程序标准版(需要由人来输入?a?b):
#include<iostream.h>
#include<stdio.h>
#include<string.h>

struct RecordNode
{
char Num[5];
int a;
int b;
};

struct RecordNode Record[10];
struct RecordNode CurNum;


void Compare(RecordNode StrBase,RecordNode &StrTest)
{//test a & b of StrTest.Num,(compare with StrBase)
//suppose no same characters are in the strings
StrTest.a=0;
StrTest.b=0;
cout<<"base:"<<StrBase.Num<<" test:"<<StrTest.Num<<" ";
for(int i=0;i<4;i++)
{
if(StrBase.Num[i]==StrTest.Num[i]) //get A
{
StrTest.a++;
}
for( int j=0; j<4; j++) //get B
{
if(j!=i)
{
if(StrBase.Num[i]==StrTest.Num[j])
{
StrTest.b++;
}
}
}//for(j..)
}//for(i..), the 4 chars in StrBase
cout<<StrTest.a<<" A "<<StrTest.b<<" B "<<endl;
}//Compare(StrBase,&StrTest)


int Repeated(RecordNode NewNum,int n)
{//judge whether bit No.n(from 0) have same chars with outer ones
int RetValue=0;
for(int m=0; m<n; m++)
{
if(NewNum.Num[n]==NewNum.Num[m])
{
RetValue=1;
return RetValue;break;
}

}//for(...)
if(RetValue!=1)
{
return 0;
}
}//Repeated(...)


void GuessNum()
{
Record->Num[4]='\0';
CurNum.Num[4]='\0';
int step=1;
for(CurNum.Num[0]='0'; CurNum.Num[0]<='9'; CurNum.Num[0]++)
{
for(CurNum.Num[1]='0'; CurNum.Num[1]<='9'; CurNum.Num[1]++)
{
if( !Repeated(CurNum,1) )
{
for(CurNum.Num[2]='0'; CurNum.Num[2]<='9'; CurNum.Num[2]++)
{
if( !Repeated(CurNum,2))
{
for(CurNum.Num[3]='0'; CurNum.Num[3]<='9'; CurNum.Num[3]++)
{
if( !Repeated(CurNum,3))
{
int RecordNum=0;
if(step!=1) //check records
{
RecordNum=step-1;
do
{
Compare(Record[RecordNum-1],CurNum);
RecordNum--;
}while(RecordNum>0 && CurNum.a==Record[RecordNum].a
&& CurNum.b==Record[RecordNum].b);
}//if(step!=1)

if(step==1 || (CurNum.a==Record[RecordNum].a
&& CurNum.b==Record[RecordNum].b))
{//print the possible answer
cout<<"step "<<step<<": "<<CurNum.Num<<"?"<<endl;
cin>>CurNum.a>>/*"A">>*/CurNum.b/*>>"B"*/;
while(CurNum.a + CurNum.b >4)
{
cout<<"check your answer!"<<endl;
cout<<"step "<<step<<": "<<CurNum.Num<<"?"<<endl;
cin>>CurNum.a>>/*"A">>*/CurNum.b;//>>"B";
}

if(CurNum.a==4 && CurNum.b==0)
{//suppose man kicks the correct key
cout<<"Got it! ^-^"<<endl;
return; //needn't record the final answer!
}
else
{//it's not the answer,record it
Record[step-1]=CurNum;
step++;
}
}//if(... it may be the answer)

if(step>10)
{
cout<<"Fail to find it!"<<endl;return;
}
}//if( no same chars in [3])
}//for(CurNum.Num[3]...)
}//if( no same chars in [2])
}//for(CurNum.Num[2]...)
}//if ( no same chars in[1])
}//for(CurNum.Num[1]...)
}//for(CurNum.Num[0]...)
if( step<10 )
{
cout<<"Are you sure your answer's right?"<<endl;
cout<<"This is the list of your answer:"<<endl;
for(int recordNum=0; recordNum<step-1; recordNum++)
{
cout<<Record[recordNum].Num<<" ";
cout<<Record[recordNum].a<<'A'<<Record[recordNum].b<<'B'<<endl;
}
}
}//GuessNum


void main(void)
{
cout<<"I'm going to guess the number you get in your mind."<<endl;
char c;
do
{
cout<<"Continue?(Y/N)"<<endl;
cin>>c;
if(c=='Y' || c=='y')
{
GuessNum();
}
}while(c!='n' && c!='N');
}
wanggangtjdx8211 2003-09-14
  • 打赏
  • 举报
回复
谢谢大家!
我已经自己编出来了,并且已经证明:最多需要9步,(共有5个数字需要9步)
我的思路是:
电脑采用最擅长的穷举法来猜测,即:第一次,固定猜 0123;然后,人必定给出一个提示?a?b(1)。然后,电脑从0124开始搜索,每次加1,并分别和0123相比较,当找到第一个与标准答案一样为?a?b的不重复数字串时,第二步便猜测它;如果对,则结束,如果不对,则人又会给出第二个提示?a?b(2),此时,我们从第二步猜的数的下一个开始继续搜索,当遇到第一个同时满足?a?b(1)和?a?b(2)的不重复数字串时,第三步便猜它;以次类推·····
可见,越往后猜,候选答案需要满足的约束条件越多,即越有可能是标准答案。如:当猜到第7步还没猜中时,第8步猜测的候选答案必须同时满足前7次的猜测记录,因此,越往后,排除掉的越多,加速越快。当搜索到9876仍然没有找到正确答案时,则说明人的提示有误。
到这里,大家是否明白这种算法的原理?就是,电脑从0124开始,假设它是正确答案,那么它和0123相比得到的?a?b必然与标准答案和0123相比得到的?a?b相同;对于步数更多的情况,此原理依然满足,即我们所猜测的候选答案与之前猜测的数字串相比,得到的?a?b序列,必然与历史记录完全相同,否则便可排除。
此算法的优点:实现简单,时间空间复杂度都不高,而且可以保证猜测最多不超过9步,这一点在我的测试版中已经得到了证明,即:我们设一个循环,让标准答案从0123一直到9876各猜一次,统计其中的最大步数和相应记录即可。
前面各位朋友给的程序我没细看,不过不知道和我得思路是否一样呢?还有就是最重要的一点:前面附的三个程序,能否满足最多步数不超过10步这个条件?如果可以证明的话,那么你的程序也是可行的,否则一概都不符合条件。
稍后我会附上我的程序的标准版和测试版,供大家参考。然后散分!
skywind 2003-09-14
  • 打赏
  • 举报
回复
大家好,这个程序我同学上学期问过我,我刚好写了,这个程序就是七步以内可以找初来的,(详细看程序的论证)经过测试0123-9876几个数据的模拟测试,分别在一步到七步之内找初来的数字个数分别统计为下:

最后测试结果:
总数据 =5040 (10*9*8*7)
一步内找到的数据有 1
两步内找到的数据有 13
三步内找到的数据有 108
四步内找到的数据有 596
五步内找到的数据有 1668
六步内找到的数据有 1768
七步内找到的数据有 752
由此证明出合法的5040个数据每个都可以在七步内猜出来

具体的程序在下面,这个程序帮同学得了满分,希望能对大家有用:

/* 猜数字程序 (筛选法)

将要猜的数据表示为 int nv[4]
然后将四个数字整数化: n=nv[0]*1000+nv[1]*100+nv[2]*10+nv[3]
以后所有算法都用整数化的数字表示
筛选器:int nums[10000] 表示所有选择结果的筛选状态
可以选择(没有被筛选)用一表示,不可以选择(被筛选了)设零
初始化筛选:将位重复的数字除去,如0000,1102,3255都位重复了
主算法表示为:

WHILE TRUE BEGIN
1.在nums中取出一个可以用的数字N来,如果全部不能用则EXIT
2.判断N的情况,得出A和B。如果A=4则成功找到,EXIT
3.扫描nums中所有可以用的数字和N比较,得出a,b如果a,b和A,B
不同则在nums中排除所扫描到的数字。
END

最后测试结果:
总数据 =5040 (10*9*8*7)
一步内找到的数据有 1
两步内找到的数据有 13
三步内找到的数据有 108
四步内找到的数据有 596
五步内找到的数据有 1668
六步内找到的数据有 1768
七步内找到的数据有 752
由此证明出合法的5040个数据每个都可以在七步内猜出来

Author: Linwei, skywindt@yeah.net
Date: 13/3/2003
*/

#include <stdio.h>
#include <stdlib.h>

class game
{
// 下面是电脑产生数字的部分
protected:
int num; // 原始数字
public:
void make_num();// 随机产生四位数字到num
void check(int n1,int n2,int &a,int &b); // 检测猜测结果
// 下面是电脑猜数字的部分
protected:
char nums[10000];// 数字筛选器,1可以选择,0不可以选择
void nums_init();// 初始化筛选器
void check_num(int n,int& a,int& b);// 和结果作比较
public:
void guess(int &result,int &step); // 电脑猜数字主程序
void test(); // 0123....9876数据测试算法
};

void game::test()
{
static char _nums[10000];
int times[10]={0,0,0,0,0,0,0,0,0,0}; // 记录测试结果
int step,result,failed=0,total=0,i;
nums_init(); // 首先得出可以用于测试的合法数据集
for (i=0;i<10000;i++) _nums[i]=nums[i]; // 复制数据集
for (i=0;i<10000;i++) { // 在筛选器中除去不可能的结果
if (_nums[i]) { // 如果是可以测试的数据
printf("test %d\r",i);
num=i;
guess(result,step);
if (step>0&&step<=7) times[step]++;
else failed++;
total++;
}
}
printf("\ntest nums=%d\n",total);
for (i=0;i<10;i++) {
printf("find in %d step is %d\n",i,times[i]);
}
}

void game::guess(int &result,int &step)
{
int i,j,n;
int A,B,na,nb;
int times=0;
nums_init();
while (1) {
for (n=0;n<10000;n++) if (nums[n]) break;// 找一合法数字
if (n==10000) { result=-1; break; }
times++;
check_num(n,A,B); // 检测数字n把结果送回a,b中
if (A==4) { result=n; step=times; break; }

for (i=0;i<10000;i++) if (nums[i]) {
check(n,i,na,nb); // 除去和n比较不满足A,B的数字
if (na!=A||nb!=B) nums[i]=0;
}
}
}
// 检测电脑猜测结果和实际数据的关系,得出A,B
// 此函数可以改写成A,B由人输入
void game::check_num(int x,int &a,int &b)
{
check(x,num,a,b);
}

void game::nums_init()
{
static char _nums[10000];
static char flag=0;
int v[4]={0,0,0,0},i;
if (flag==0) {
for (i=0;i<10000;i++) { // 在筛选器中除去不可能的结果
_nums[i]=1;
if (v[0]==v[1]||v[0]==v[2]||v[0]==v[3]|| // 除去位重复或
v[1]==v[2]||v[1]==v[3]||v[2]==v[3]) _nums[i]=0;
if (++v[3]>9) { v[3]=0; if (++v[2]>9) {
v[2]=0; if (++v[1]>9) v[1]=0, v[0]++; }
}
}
flag=1;
}
for (i=0;i<10000;i++) nums[i]=_nums[i];
}

void game::make_num()
{ // 随机产生有效数字,采用随机交换x数组任意两个数
int x[10]={0,1,2,3,4,5,6,7,8,9};
int i,randp,v;
for (i=0;i<10;i++) {
randp=rand()%10;
v=x[randp];
x[randp]=x[i];
x[i]=v;
}
num=x[4]*1000+x[5]*100+x[6]*10+x[7];
}

void game::check(int n1,int n2,int &a,int &b)
{
int v1[4],v2[4],i,j;
v1[0]=n1/1000; n1%=1000; v1[1]=n1/100; n1%=100;
v1[2]=n1/10; n1%=10; v1[3]=n1;
v2[0]=n2/1000; n2%=1000; v2[1]=n2/100; n2%=100;
v2[2]=n2/10; n2%=10; v2[3]=n2;
for (i=0,a=0,b=0;i<4;i++)
for (j=0;j<4;j++) if (v1[i]==v2[j]) {
if (i==j) a++; else b++;
}
}

#include <conio.h>
#include <time.h>

game guess;

int main()
{
int times;
int result;

srand(time(NULL)); // 用当前时间初始化随机数
guess.make_num();
guess.guess(result,times);
printf("in step %d find %d\n",times,result);
printf("\n-- END --\n");
guess.test();
getch();
return 0;
}

theoldman 2003-09-14
  • 打赏
  • 举报
回复
很遗憾!不知楼主的程序是如何运行的??怎么我输入如:1a0b 或 0a3b 时程序不停地显示却不停下来???

另外,楼主不妨再想想,其实不用9步,正如BlueSky2008() 所说的最多只用7步就能猜出答案了,当然其中也有一点点技巧。早前我用VC做了个这样的游戏程序,开始时的确最多用了9步,但优化后只用了7步(我已用相应的功能证明了),如有兴趣,我可给你执行程序参考参考。

FlatHuge 2003-09-02
  • 打赏
  • 举报
回复
筛选法,就是每次从可能的里面随机找一个,再把不符合要求的剔除出去,很简单的,我有源代码,Turbo Pascal7.0的,要就留下邮箱。
littlecpu 2003-08-31
  • 打赏
  • 举报
回复
看看Java版,超简洁

//排除5040(10 * 9 * 8 * 7)种排列中的不可能的值,直到确定最后一个值解猜数字
public class Guess
{

public static void main(String[] argv) throws Exception
{
GuessNum gn= new GuessNum();
java.io.BufferedReader br = new BufferedReader(new java.io.InputStreamReader(System.in, "GBK"));
int i = 1;
while(true)
{
String guessSeed = gn.getNextSeed();
if(guessSeed.startsWith("我猜") || guessSeed.startsWith("你是"))
{
System.out.println(guessSeed);
System.exit(0);
}

System.out.println();
System.out.println("我第" + i++ + "次猜, 这个数是" + guessSeed);
System.out.println("请你说位置及值都对的有几个?");
String s = br.readLine();
int a = Integer.parseInt(s);
if(a == 4)
{
System.out.println("看,我猜对了吧,服不服,哈哈哈!!!");
System.exit(0);
}
System.out.println("请你说值对但位置不对的有几个?");
s = br.readLine();
int b = Integer.parseInt(s);
gn.setGuessReuslt(a, b);
}
}
}

class GuessNum
{

boolean[] isValid; //是否有可能,为false时为不可能,true继续排除
int[] nums; //所有可能值,共5040 * 4长,每四个作一个可能值,上面的isValid记录这5040个可能性是否还有可能
int a, b, c, d; //上次猜测值,暂存

public GuessNum()
{
isValid = new boolean[5040];
java.util.Arrays.fill(isValid, true); //刚开始时全有可能
nums = new int[5040 * 4];
int pos = 0; //5040 * 4数组中的位置

//初始化,把所有可能值的a, b, c, d位全按四个一组放入nums中
for(int a=0;a<10;a++)
{
for(int b=0;b<10;b++)
{
if(a == b)
continue;
for(int c=0;c<10;c++)
{
if(a == c || b==c)
continue;
for(int d=0;d<10;d++)
{
if(a == d || b == d || c == d)
continue;
nums[pos++] = a;
nums[pos++] = b;
nums[pos++] = c;
nums[pos++] = d;
}
}
}
}
}


/**
* 得到下一个猜测目标,如果没有了可能性,证明操作者前面输入错误,如果可能性只有一,立即确定
* @return
*/
public String getNextSeed()
{
int others = 0;
int pos = 0;
for(int i=0;i<5040;i++)
{
if(isValid[i])
{
if(others > 0)
{
others ++;
break;
}
else
{
others++;
pos = i;
}
}
}

if(others == 0)
{
return "你是个赖皮狗,前面你回答错了,查查吧";
}

if(others == 1)
{
int i =pos * 4;
return "我猜这个数一定是" + nums[i++] + nums[i++] + nums[i++] + nums[i++];
}


for(int i=0;i<5040;i++)
{
if(isValid[i])
{
i*=4;
a = nums[i];
b = nums[i+1];
c = nums[i+2];
d = nums[i+3];
return new StringBuffer().append(nums[i++]).append(nums[i++]).append(nums[i++]).append(nums[i++]).toString();
}
}

//理论上不可能到这一步
throw new java.lang.RuntimeException("你是个赖皮狗,前面你回答错了,查查吧");
}

//设置操作者的反馈(x 个 a, y 个 b)
public void setGuessReuslt(int x, int y)
{
if(x == 4)
{
return;
}
for(int pos = 0;pos < 5040; pos++)
{
if(!isValid[pos])
continue;
int numspos = pos * 4;
if(getMyX(pos) != x)
{
isValid[pos] = false;
continue;
}
if(getMyY(pos) != y)
{
isValid[pos] = false;
continue;
}
}
}

//测试,把每个可能值都与上次猜测值比较,就会得到这个可能值与猜测值是几个a的关系
private int getMyX(int pos)
{
int myx = 0;
pos*=4;
if(nums[pos++] == a)
myx++;
if(nums[pos++] == b)
myx++;
if(nums[pos++] == c)
myx++;
if(nums[pos++] == d)
myx++;
return myx;
}
//测试,把每个可能值都与上次猜测值比较,就会得到这个可能值与猜测值是几个b的关系
private int getMyY(int pos)
{
int myy = 0;
pos *= 4;
if(nums[pos] == b || nums[pos] == c || nums[pos] == d)
myy ++;
if(nums[pos+1] == a || nums[pos+1] == c || nums[pos+1] == d)
myy++;
if(nums[pos+2] == a || nums[pos+2] == b || nums[pos+2] == d)
myy++;
if(nums[pos+3] == a || nums[pos+3] == b || nums[pos+3] == c)
myy++;
return myy;
}
}
loewe 2003-08-31
  • 打赏
  • 举报
回复
#include <iostream>

using namespace std ;

struct AnswerScope
{
int Num ;
bool ifNumPossible ;
};

class GuessAnswer
{
private :
int isA , isB , AnswerNumber , times ;//用户输入的A,B个数
AnswerScope ans[9877] ; //去处相同数字的,可能答案的个数,用户猜次数
public :
GuessAnswer() ;
int ANumber(int,int,int,int,int) ;
int BNumber(int,int,int,int,int) ;
void ShowAnswer() ;
~GuessAnswer(){} ;
};

GuessAnswer::GuessAnswer()
{
times = 1 ; isA = 0 ; isB = 0 ;
int t,t1,t2,t3,t4 ;
for(t1 = 0 ; t1 < 10 ; t1++)
for(t2 = 0 ; t2 < 10 ; t2++)
for(t3 = 0 ; t3 < 10 ; t3++)
for(t4 = 0 ; t4 < 10 ; t4++)
{
t = t1*1000+t2*100+t3*10+t4 ;
ans[t].Num = t ;
if((t1==t2)||(t1==t3)||(t1==t4)||(t2==t3)||(t2==t4)||(t3==t4))
ans[t].ifNumPossible = false ;
else
ans[t].ifNumPossible = true ;
}
}

int GuessAnswer::ANumber(int t,int n1,int n2,int n3,int n4)
{
int n = 0 ;
int m1 = t/1000 ;
int m2 = (t-m1*1000)/100 ;
int m3 = (t-m1*1000-m2*100)/10 ;
int m4 = t-m1*1000-m2*100-m3*10 ;

if(m1==n1)
n++ ;
if(m2==n2)
n++ ;
if(m3==n3)
n++ ;
if(m4==n4)
n++ ;
return n ;
}

int GuessAnswer::BNumber(int t,int n1,int n2,int n3,int n4)
{
int n = 0 ;
int m1 = t/1000 ;
int m2 = (t-m1*1000)/100 ;
int m3 = (t-m1*1000-m2*100)/10 ;
int m4 = t-m1*1000-m2*100-m3*10 ;

if((m1==n2)||(m1==n3)||(m1==n4))
n++ ;
if((m2==n1)||(m2==n3)||(m2==n4))
n++ ;
if((m3==n1)||(m3==n2)||(m3==n4))
n++ ;
if((m4==n1)||(m4==n2)||(m4==n3))
n++ ;
return n ;
}

void GuessAnswer::ShowAnswer()
{
int n , n1 , n2 , n3 , n4 , a , b , t , AnswerNum = 0 ;
do
{
cout << "第" << times << "次:" << endl << "请输入一个四位数,各位数字不能相同:" <<endl ;
cin >> n ;
n1 = n/1000 ;
n2 = (n-n1*1000)/100 ;
n3 = (n-n1*1000-n2*100)/10 ;
n4 = n-n1*1000-n2*100-n3*10 ;
}
while((n1==n2)||(n1==n3)||(n1==n4)||(n2==n3)||(n2==n4)||(n3==n4)) ;
cout << "请输入A和B的个数:" << endl ;
cin >> a >> b ;
isA = a ; isB = b ;
cout << "可能的答案有:" << endl ;
for(t = 123 ; t < 9876 ; t++)
{
if(ANumber(t,n1,n2,n3,n4)!=isA||(BNumber(t,n1,n2,n3,n4)!=isB))
ans[t].ifNumPossible = false ;
else
if(ans[t].ifNumPossible == true)
{
AnswerNum++ ;
if(t<1000)
{
cout << "0" ; //3位数第一位补0
}
cout << t << " " ;
}
if(AnswerNum==0)
{
cout << "请检查输入是否正确:"<< endl ;
break ;
}
}
cout << "共有" << AnswerNumber << "个可能的答案." << endl ;
times ++ ;
while(AnswerNumber!=1)
cout << "您一共猜了" << --times << "次,猜数字结束!" << endl ;
}

int main()
{
char command ;
do
{
GuessAnswer m ;
m.ShowAnswer() ;
cout << endl ;
cout << "Again?(Y/N):" ;
cin >> command ;
}
while((command=='y')||(command=='Y')) ;
cout << "Bye!" ;
return 0 ;
}
WYlslrt 2003-08-29
  • 打赏
  • 举报
回复
咱们别用搜索,要按照人的逻辑方法来写一段代码,这样会更快。

我在文曲星最快1步蒙出来
azure711 2003-08-29
  • 打赏
  • 举报
回复
列出0~9999的所有数字,根据人的回答删除错误的数字就行啦,直到删的只剩一个数字,就是解了.
loewe 2003-08-29
  • 打赏
  • 举报
回复
这样吧,明天要去ACM比赛了,今天是没空了,到明天晚上给你写一个好了,不知道来得及不
SoftWare1999 2003-08-29
  • 打赏
  • 举报
回复
以上程序,是0000-9999的范围,数字可以重复

如果要改为手动设置数据,只要改command2_click即可
加载更多回复(8)

33,008

社区成员

发帖
与我相关
我的任务
社区描述
数据结构与算法相关内容讨论专区
社区管理员
  • 数据结构与算法社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧