猜拳问题

chenlei1700 2009-12-15 01:26:57
假设计算机的一个猜拳算法是根据你出的拳的最后一次和最后第2次的概率来决定计算机的下一次的出拳概率

问题 :如何战胜计算机?


比如说我的出拳概率如下

我这一次出 石头 剪子 布 的概率
我上一次出 石头 20% 30% 50%
。。 剪子 30% 20% 50%
。。 布 50% 30% 20%
(看不懂的这里做一下解释,看的懂得请忽略此行。我上一次出石头的话 那么我这一次出石头的概率为20% 剪子为30% 布50%)


那么计算机会对我出的拳进行统计 得出上面的结论 然后计算机计算出它的出拳概率如下


计算机出 石头 剪子 布 的概率
对手上一次出 石头 30% 50% 20%
。。 剪子 20% 50% 30%
。。 布 30% 20% 50%


如何设计我的出拳规律来战胜计算机呢?
...全文
258 9 打赏 收藏 转发到动态 举报
写回复
用AI写文章
9 条回复
切换为时间正序
请发表友善的回复…
发表回复
chenlei1700 2009-12-18
  • 打赏
  • 举报
回复
[Quote=引用 7 楼 litaoye 的回复:]
既然LZ有别人的算法源程序,那么比较简单的方法就是,提前算出他会出什么,
然后出一个能够赢他的就行了,如果他的程序没有随机部分的话应该每次都可以赢!
[/Quote]
他的源程序是也是按照概率出拳的,要想达到100%战胜肯定不可能 因为我查看了下他根据我的出拳推算出的它的出拳概率基本接近33%,33%,33%。
我又改了一下ko5, 让他达到了50.08% 。。。。。
/////
char ko5(int n){

/* 親の状態遷移確率 親の一つ前の手から次ぎの親の手を決定(3回目以降に利用) */
float ko_no_te_trans_table[3][3] =
{{0.0,0.0,0.0},
{0.0,0.0,0.0},
{0.0,0.0,0.0}} ;

int pre_last_oya_no_te,last_oya_no_te, last_ko_no_te, ko_no_te ; /* 最後の一つ前,最後,次ぎの親の手 */

float rnd; /* 乱数用 */

if((n==0)||(n<=1)) /* スタートの2手目までは,相手の手の履歴がないので乱数で状態を決定 */
/* 乱数発生 */
ko_no_te = (int)(rand()/(1.0 + RAND_MAX) * 3) % 3;
else{
pre_last_oya_no_te = oyanote[n-2]; /* 親の最後の一つ前の手 */
last_oya_no_te = oyanote[n-1] ; /* 子の最後の手 */
last_ko_no_te=konote[n-1];

ko_no_ko_no_te_trans_count[pre_last_oya_no_te][last_ko_no_te]++ ; /* 状態遷移のカウント */
/* 二つ前の親の手から一つ前の子の手を場合に分けてカウント */

ko_no_te_trans_table_update(ko_no_te_trans_table) ; /* 親の状態遷移表の更新 */

rnd= (float)rand()/(1.0 + RAND_MAX) ;

if (ko_no_te_trans_table[last_oya_no_te][GU] > ko_no_te_trans_table[last_oya_no_te][CHOKI]&&ko_no_te_trans_table[last_oya_no_te][GU]>ko_no_te_trans_table[last_oya_no_te][PA])
ko_no_te = GU ;
else if(ko_no_te_trans_table[last_oya_no_te][CHOKI]>ko_no_te_trans_table[last_oya_no_te][PA])
ko_no_te = CHOKI;
else
ko_no_te = PA ;

}
return(ko_no_te) ;
}

void ko_no_te_trans_table_update(float ko_no_te_trans_table[3][3]){

int i, j ;
long int total_count = 0 ;
long int sub_total_count[3] = {0,0,0};

for(i=0; i < 3 ; i++) /* 状態遷移のカウントを合計して */
for(j=0; j < 3 ; j++){
total_count += ko_no_ko_no_te_trans_count[i][j] ;
sub_total_count[i] += ko_no_ko_no_te_trans_count[i][j] ;
}

/* 親の一つ前の手がグーのとき親がグーを出す確率は,一つ前の手がグーのとき,子がチョキを出す確率とする */
ko_no_te_trans_table[GU][PA] =
(float)ko_no_ko_no_te_trans_count[GU][CHOKI] / sub_total_count[GU] ;
/* 親の一つ前の手がグーのとき親がチョキを出す確率は,親の一つ前の手がグーのとき,子がパーを出す確率とする */
ko_no_te_trans_table[GU][GU] =
(float)ko_no_ko_no_te_trans_count[GU][PA] / sub_total_count[GU] ;
/* 親の一つ前の手がグーのとき親がパーを出す確率は,親の一つ前の手がグーのとき,子がグーを出す確率とする */
ko_no_te_trans_table[GU][CHOKI] =
(float)ko_no_ko_no_te_trans_count[GU][GU] / sub_total_count[GU] ;

/*以下同様*/
ko_no_te_trans_table[CHOKI][PA] =
(float)ko_no_ko_no_te_trans_count[CHOKI][CHOKI] / sub_total_count[CHOKI] ;
ko_no_te_trans_table[CHOKI][GU] =
(float)ko_no_ko_no_te_trans_count[CHOKI][PA] / sub_total_count[CHOKI] ;
ko_no_te_trans_table[CHOKI][CHOKI] =
(float)ko_no_ko_no_te_trans_count[CHOKI][GU] / sub_total_count[CHOKI] ;

/*以下同様*/
ko_no_te_trans_table[PA][PA] =
(float)ko_no_ko_no_te_trans_count[PA][CHOKI] / sub_total_count[PA] ;
ko_no_te_trans_table[PA][GU] =
(float)ko_no_ko_no_te_trans_count[PA][PA] / sub_total_count[PA] ;
ko_no_te_trans_table[PA][CHOKI] =
(float)ko_no_ko_no_te_trans_count[PA][GU] / sub_total_count[PA] ;
}
kxalpah 2009-12-17
  • 打赏
  • 举报
回复
注释竟然是日文的
绿色夹克衫 2009-12-17
  • 打赏
  • 举报
回复
既然LZ有别人的算法源程序,那么比较简单的方法就是,提前算出他会出什么,
然后出一个能够赢他的就行了,如果他的程序没有随机部分的话应该每次都可以赢!
chenlei1700 2009-12-17
  • 打赏
  • 举报
回复
/*
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
/* 親のジャンケン関数(その1:子の状態遷移確率を逆手に取る方法) */
/* 強力版,5割に持ち込めたらまだ良い方 */

void oya_no_te_trans_table_update(float [3][3]) ;/* プロトタイプ宣言 */

long int oya_no_ko_no_te_trans_count[3][3] =
{{0,0,0},{0,0,0},{0,0,0}} ;

char oya2(int n){

/* 親の状態遷移確率 親の一つ前の手から次ぎの親の手を決定(3回目以降に利用) */
float oya_no_te_trans_table[3][3] =
{{0.0,0.0,0.0},
{0.0,0.0,0.0},
{0.0,0.0,0.0}} ;

int pre_last_oya_no_te, last_oya_no_te, oya_no_te ; /* 最後の一つ前,最後,
次ぎの親の手 */
int last_ko_no_te ; /* 最後の子の手 */
float rnd; /* 乱数用 */

if((n==0)||(n==1)) /* スタートの2手目までは,相手の手の履歴がないので乱数で
状態を決定 */
/* 乱数発生 */
oya_no_te = (int)(rand()/(1.0 + RAND_MAX) * 3) % 3;
else{
pre_last_oya_no_te = oyanote[n-2]; /* 親の最後の一つ前の手 */
last_ko_no_te = konote[n-1] ; /* 子の最後の手 */
oya_no_ko_no_te_trans_count[pre_last_oya_no_te][last_ko_no_te]++ ; /* 状態
遷移のカウント */
/* 二つ前の親の手から一つ前の子の手を場合に分けてカウント */

oya_no_te_trans_table_update(oya_no_te_trans_table) ; /* 親の状態遷移表の
更新 */

rnd= (float)rand()/(1.0 + RAND_MAX) ;

last_oya_no_te = oyanote[n-1] ; /* 最後の親の手を設定 */

if (oya_no_te_trans_table[last_oya_no_te][GU] > rnd)
oya_no_te = GU ;
else
if((oya_no_te_trans_table[last_oya_no_te][GU]+oya_no_te_trans_table[last_oya_no_te][CHOKI])
> rnd)
oya_no_te = CHOKI ;
else
oya_no_te = PA ;
}
return(oya_no_te) ;
}

/* +++++++++++++++++++ */
void oya_no_te_trans_table_update(float oya_no_te_trans_table[3][3]){

int i, j ;
long int total_count = 0 ;
long int sub_total_count[3] = {0,0,0};

for(i=0; i < 3 ; i++) /* 状態遷移のカウントを合計して */
for(j=0; j < 3 ; j++){
total_count += oya_no_ko_no_te_trans_count[i][j] ;
sub_total_count[i] += oya_no_ko_no_te_trans_count[i][j] ;
}

/* 親の一つ前の手がグーのとき親がグーを出す確率は,一つ前の手がグーのとき,
子がチョキを出す確率とする */
oya_no_te_trans_table[GU][PA] =
(float)oya_no_ko_no_te_trans_count[GU][CHOKI] / sub_total_count[GU] ;
/* 親の一つ前の手がグーのとき親がチョキを出す確率は,親の一つ前の手がグーの
とき,子がパーを出す確率とする */
oya_no_te_trans_table[GU][GU] =
(float)oya_no_ko_no_te_trans_count[GU][PA] / sub_total_count[GU] ;
/* 親の一つ前の手がグーのとき親がパーを出す確率は,親の一つ前の手がグーのと
き,子がグーを出す確率とする */
oya_no_te_trans_table[GU][CHOKI] =
(float)oya_no_ko_no_te_trans_count[GU][GU] / sub_total_count[GU] ;

/*以下同様*/
oya_no_te_trans_table[CHOKI][PA] =
(float)oya_no_ko_no_te_trans_count[CHOKI][CHOKI] / sub_total_count[CHOKI] ;
oya_no_te_trans_table[CHOKI][GU] =
(float)oya_no_ko_no_te_trans_count[CHOKI][PA] / sub_total_count[CHOKI] ;
oya_no_te_trans_table[CHOKI][CHOKI] =
(float)oya_no_ko_no_te_trans_count[CHOKI][GU] / sub_total_count[CHOKI] ;

/*以下同様*/
oya_no_te_trans_table[PA][PA] =
(float)oya_no_ko_no_te_trans_count[PA][CHOKI] / sub_total_count[PA] ;
oya_no_te_trans_table[PA][GU] =
(float)oya_no_ko_no_te_trans_count[PA][PA] / sub_total_count[PA] ;
oya_no_te_trans_table[PA][CHOKI] =
(float)oya_no_ko_no_te_trans_count[PA][GU] / sub_total_count[PA] ;
}

/* ++++++++++++++++++++++++++++ */
/* 状態遷移確率を乱数で生成(未使用) */
void rand_s_trans_table(float s_trans_table[3][3]){

int i, j;
float sub_total[3]={0.0, 0.0, 0.0} ;

for(i=0 ; i< 3 ; i++)
for(j=0 ; j< 3 ; j++){
s_trans_table[i][j]= (float) rand();
sub_total[i] += s_trans_table[i][j] ;
}

for(i=0 ; i< 3 ; i++)
for(j=0 ; j< 3 ; j++)
s_trans_table[i][j]/=sub_total[i];
}
/****************************************************************************/
/***** メイン関数 **********************************************************
***/
/****************************************************************************/
int main (int argc, const char * argv[]) {

int n ;

/* 対戦 */
for(n=0; n < N ; n++){

oyanote[n] = oya2(n) ;
konote[n] = ko5(n) ; /* <------------------------------- */
kachimake[n] = jankenpon(oyanote[n],konote[n]) ;
}

hyouji(N) ;

return 0;
}
/***************************************************************************/
/* ジャンケンポンの勝敗関数 */
char jankenpon(int a, int b){

switch(a){
case GU :
switch(b){
case GU: return(EVEN) ; /* グーとグーなら引き分け */
case CHOKI: return(LOST) ;
case PA: return(WIN) ;
}
case CHOKI :
switch(b){
case GU: return(WIN) ;
case CHOKI: return(EVEN) ;
case PA: return(LOST) ;
}
case PA :
switch(b){
case GU: return(LOST) ;
case CHOKI: return(WIN) ;
case PA: return(EVEN) ;
}
}
return(DUMMY); /* dummy return for compile warning */
}
/***************************************************************************/
/* 結果の表示関数 */
void hyouji(int n){

int i,j ;
int kukan_win = 0 ; /* 区間の勝ち数 */
int total_win = 0 ; /* 勝ち数の累計 */
int kukan_lost = 0 ; /* 区間の負け数 */
int total_lost = 0 ; /* 負け数の累計 */

system("date '+DATE: %Y-%m-%d, TIME: %H:%M:%S'") ;

/* 子の手を1行当たりHABA分出力 */
for(j = 0 ; j < (n / HABA) ; j++){
printf("\n========================================================\n") ;
printf("対戦回数:%d-%d\n", j*HABA+1, (j+1)*HABA) ;
printf("========================================================\n") ;

/* 相手の手を1行当たりHABA分出力 */
printf("親 ") ;

for(i = 0 ; i < HABA ; i++)
switch(oyanote[j*HABA+i]){
case GU :
putchar('g') ;
break ;
case CHOKI :
putchar('c') ;
break ;
case PA :
putchar('p') ;
break ;
}

printf("\n") ;

/* 子の手を出力 */
printf("子 ") ;

for(i = 0 ; i < HABA ; i++)
switch(konote[j*HABA+i]){
case GU :
putchar('g') ;
break ;
case CHOKI :
putchar('c') ;
break ;
case PA :
putchar('p') ;
break ;
}
printf("\n") ;

/* 勝ち負けを1行当たりHABA分出力 */
printf("勝敗 ") ;

/* 集計 */
kukan_win = 0 ;
kukan_lost = 0 ;
for(i = 0 ; i < HABA ; i++){
putchar(kachimake[j*HABA+i]) ;
switch(kachimake[j*HABA+i]){
case WIN :
kukan_win++;
total_win++;
break ;
case LOST :
kukan_lost++;
total_lost++;
break ;
}
}
printf("\n----") ;
printf("\n区間(引き分けは含まない) %6d/%6d(%4.2f%%)\n",
kukan_win, kukan_win+kukan_lost,
((float)kukan_win/(kukan_win+kukan_lost)*100)) ;
printf("累計(引き分けは含まない) %6d/%6d(%4.2f%%)\n", total_win, total_win
+total_lost,
((float)total_win/(total_win+total_lost)*100)) ;
}
}
/***************************************************************************/

计算机的算法是oya2
我写的算法是ko5
让我不解的是为什么在mian函数里我吧ko5()写在oya2()上面的时候没法战胜它 但是把ko5()写在oya2下面的时候却可以 胜率50.11%
chenlei1700 2009-12-17
  • 打赏
  • 举报
回复
计算机算法如下:
统计计算机倒数第2次出拳后, 对手在下一次的出拳概率 ,根据这一规律得出计算机这一次的出拳概率
代码如下
//////////////////////////////////////////////////////////
#include <stdio.h>
#include <stdlib.h>
#include <time.h>

#define N 1000000 /* 試行回数 */

/* 表示幅 */
#define HABA 50

/* グーチョキパーは,それぞれ,'g','c','p'という一文字で表す.*/
#define GU 0 /* グー */
#define CHOKI 1 /* チョキ */
#define PA 2 /* パー */

/* 勝敗も勝ち,負け,引き分けを'W", 'L', 'E'という一文字で表す.*/
#define WIN 'W' /* 勝ち */
#define LOST 'L' /* 負け */
#define EVEN 'E' /* 引き分け */

#define DUMMY 0 /* return文用ダミーの値 */

int oyanote[N] ; /* 相手の手の履歴(main関数で利用) */
int konote[N] ; /* 子の手の履歴(main関数で利用) */
char kachimake[N] ; /* 勝敗(main関数で利用) */

/* プロトタイプ宣言 */
/* main()の以前で宣言される関数はプロトタイプ宣言の必要はない */

void hyouji(int);
char jankenpon(int, int) ;

/*
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
/* 子のジャンケン関数(その2:チョキしか出さない) */


void ko_no_te_trans_table_update(float [3][3]) ;/* プロトタイプ宣言 */

long int ko_no_ko_no_te_trans_count[3][3] =
{{0,0,0},{0,0,0},{0,0,0}} ;
int ko5(int n){


int pre_last_oya_no_te, last_ko_no_te, ko_no_te ; /* 最後の一つ前,最後,次
ぎの親の手 */
int last_oya_no_te;
float rnd; /* 乱数用 */



/* 状態遷移確率(親の手がグーであれば,グーを出す確率 1/5, チョキは,1/5, パー
は,3/5) */
/* 状態遷移確率(親の手がチョキであれば,グーを出す確率 3/5, チョキは,1/5,
パーは,1/5) */
/* 状態遷移確率(親の手ががパーであれば,グーを出す確率 1/5, チョキは,1/5,
パーは,1/5) */
float ko_no_te_trans_table[3][3] =
{{0.0,0.0,0.0},
{0.0,0.0,0.0},
{0.0,0.0,0.0}} ;

if(n>=0&&n<=2) /* スタートは,相手の手の履歴がないので乱数で状態を決定 */
{ last_oya_no_te = oyanote[n-1] ;

/* 1までの乱数を発生させ */
rnd = (float)rand()/(1.0 + RAND_MAX) ;

/* 状態遷移確率と照合し */
if (ko_no_te_trans_table[last_oya_no_te][GU] > rnd)
ko_no_te = GU ;
else
if((ko_no_te_trans_table[last_oya_no_te][GU]+ko_no_te_trans_table[last_oya_no_te][CHOKI])
> rnd)
ko_no_te = CHOKI ;
else
ko_no_te = PA ;
}


else{
pre_last_oya_no_te = oyanote[n-2]; /* 親の最後の一つ前の手 */
last_ko_no_te = konote[n-1] ; /* 子の最後の手 */
ko_no_ko_no_te_trans_count[pre_last_oya_no_te][last_ko_no_te]++ ; /* 状態
遷移のカウント */
/* 二つ前の親の手から一つ前の子の手を場合に分けてカウント */

ko_no_te_trans_table_update(ko_no_te_trans_table) ; /* 親の状態遷移表の更
新 */

last_oya_no_te = oyanote[n-1] ;

/* 1までの乱数を発生させ */
rnd = (float)rand()/(1.0 + RAND_MAX) ;

/* 状態遷移確率と照合し */
if (ko_no_te_trans_table[last_oya_no_te][GU] > rnd)
ko_no_te = GU ;
else
if((ko_no_te_trans_table[last_oya_no_te][GU]+ko_no_te_trans_table[last_oya_no_te][CHOKI])
> rnd)
ko_no_te = CHOKI ;
else
ko_no_te = PA ;
}
return(ko_no_te) ;
}

void ko_no_te_trans_table_update(float ko_no_te_trans_table[3][3]){

int i, j ;
long int total_count = 0 ;
long int sub_total_count[3] = {0,0,0};

for(i=0; i < 3 ; i++) /* 状態遷移のカウントを合計して */
for(j=0; j < 3 ; j++){
total_count += ko_no_ko_no_te_trans_count[i][j] ;
sub_total_count[i] += ko_no_ko_no_te_trans_count[i][j] ;
}

/* 親の一つ前の手がグーのとき親がグーを出す確率は,一つ前の手がグーのとき,
子がチョキを出す確率とする */
ko_no_te_trans_table[GU][PA] =
(float)ko_no_ko_no_te_trans_count[GU][CHOKI] / sub_total_count[GU] ;
/* 親の一つ前の手がグーのとき親がチョキを出す確率は,親の一つ前の手がグーの
とき,子がパーを出す確率とする */
ko_no_te_trans_table[GU][GU] =
(float)ko_no_ko_no_te_trans_count[GU][PA] / sub_total_count[GU] ;
/* 親の一つ前の手がグーのとき親がパーを出す確率は,親の一つ前の手がグーのと
き,子がグーを出す確率とする */
ko_no_te_trans_table[GU][CHOKI] =
(float)ko_no_ko_no_te_trans_count[GU][GU] / sub_total_count[GU] ;

/*以下同様*/
ko_no_te_trans_table[CHOKI][PA] =
(float)ko_no_ko_no_te_trans_count[CHOKI][CHOKI] / sub_total_count[CHOKI] ;
ko_no_te_trans_table[CHOKI][GU] =
(float)ko_no_ko_no_te_trans_count[CHOKI][PA] / sub_total_count[CHOKI] ;
ko_no_te_trans_table[CHOKI][CHOKI] =
(float)ko_no_ko_no_te_trans_count[CHOKI][GU] / sub_total_count[CHOKI] ;

/*以下同様*/
ko_no_te_trans_table[PA][PA] =
(float)ko_no_ko_no_te_trans_count[PA][CHOKI] / sub_total_count[PA] ;
ko_no_te_trans_table[PA][GU] =
(float)ko_no_ko_no_te_trans_count[PA][PA] / sub_total_count[PA] ;
ko_no_te_trans_table[PA][CHOKI] =
(float)ko_no_ko_no_te_trans_count[PA][GU] / sub_total_count[PA] ;
}



绿色夹克衫 2009-12-16
  • 打赏
  • 举报
回复
恐怕还是要了解对方具体的算法才能比较有针对性的设计算法!
记得以前有过这类程序的比赛,好像最后夺冠的用的方法很简单,反而赢了许多很复杂的方法!

[Quote=引用 2 楼 chenlei1700 的回复:]
100万次 胜率超过50%
[/Quote]
gelu1040 2009-12-15
  • 打赏
  • 举报
回复
每回合结束之后重新统计自己的下次出拳概率,通过自己的下次出拳概率,求得电脑下次出拳概率;选电脑下次出拳概率最大的,出克制之的拳法.

第一回合时候,将自己的出拳概率初始化为和电脑的一样.
于是,每回合的胜利概率都是>=1/3,失败概率+和平概率<=2/3,总体胜率>=50.
特例:
回合 1 2 3 4 5 6
布 石 刀 布 刀 石

1~6循环.气死电脑
chenlei1700 2009-12-15
  • 打赏
  • 举报
回复
100万次 胜率超过50%
绿色夹克衫 2009-12-15
  • 打赏
  • 举报
回复
怎么叫做战胜?是指赢一次,还是胜率超过50%?

33,009

社区成员

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

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