用暴力法求解24点
基本思路:
4个数进行四则运算有以下5种情况
((axb)yc)zd=
(axb)y(czd)=
(ax(byc))zd=
ax((byc)zd)=
ax(by(czd))=
a,b,c,d 属于 { x0 , x1 , x2 , x3 }
有 p = 4 * 3 * 2 * 1 = 24 种排列
x,y,z 属于 { + , - , * , / }
有 P = 4 * 4 * 4 = 64 种排列
所以一个24点问题共有 5 * 24 * 64 = 7680 个表达式
需要计算
计算每个表达式,与 goal = 24 进行比较,
如果相等,则该表达式是一种解法。
以下是我写的源程序
// 程序员:黄江斌
// 功能:求解24点问题,穷举策略
// 时间:11:36 2005-10-3
// 最后修改时间:11:36 2005-10-3
#include "iostream.h"
#include "string.h"
#include "math.h"
#define lineMax 256
void strFind( const char str[] , const char subStr[] , int &reti , int &retj )
{
//在str中找首次出现subStr的位置,reti前,retj后
//如果没有找到,返回 reti = -1 , retj = -1
reti = -1;
retj = -1;
int i = 0;
int j;
while( str[i] != 0 )
{
while( str[i] != 0 && str[i] != subStr[0] )
{
i++;
}
if( str[i] == subStr[0] )
{
j = 1;
while( str[i+j] != 0 && subStr[j] != 0 && str[i+j] == subStr[j] )
j++;
if( subStr[j] == 0 )
{
reti = i;
retj = i + j - 1;
return;
}
}
i++;
}
}
void strReplace( char str[] , const char subStr[] , char newStr[] )
{
//str:被替换的串
//subStr:要被替换的子串
//newStr:用来替换的串
int i , j;
int subs , sube;
char tempStr[lineMax] = { 0 };
strFind( str , subStr , subs , sube );
//没有找到子串,直接返回
if( subs < 0 || sube < 0 )
return;
//原串除子串后的前段
for( i = 0 ; i < subs ; i++ )
{
tempStr[i] = str[i];
}
//新串
for( j = 0 ; newStr[j] != 0 ; i++ , j++ )
{
tempStr[i] = newStr[j];
}
//原串除子串后的后段
for( j = sube + 1 ; str[j] != 0 ; i++ , j++ )
{
tempStr[i] = str[j];
}
for( i = 0 ; tempStr[i] != 0 ; i++ )
str[i] = tempStr[i];
str[i] = 0;
}
void double2str( char str[] , double n )
{
//整数转化为字符串数
long intPart = n;
double floatPart;
int index;
int i , j = 0;
//处理整数部分
while( intPart != 0 )
{
str[j++] = intPart % 10 + '0';
intPart /= 10;
}
index = j;
//字符串反转
char t;
i = 0;
j--;
while( i < j )
{
t = str[i];
str[i] = str[j];
str[j] = t;
i++;
j--;
}
//处理小数部分
intPart = n;
floatPart = n - intPart;
if( floatPart > 0 )
str[index++] = '.';
while( floatPart > 0 )
{
floatPart *= 10;
intPart = floatPart;
str[index++] = intPart + '0';
floatPart -= intPart;
}
if( index == 0 )
str[index++] = '0';
str[index] = 0;
}
void char2str( char str[] , char ch )
{
//将char转为char[]
str[0] = ch;
str[1] = 0;
}
void str2str( char str[] , const char str2[] )
{
//字符串复制
int i;
for( i = 0 ; str2[i] != 0 ; i++ )
str[i] = str2[i];
str[i] = 0;
}
void solve( double num1 , double num2 , char op , double &ret )
{
// 计算 ret = num1 op num2
switch( op )
{
case '+':
ret = num1 + num2;
break;
case '-':
ret = num1 - num2;
break;
case '*':
ret = num1 * num2;
break;
case '/':
if( num2 != 0 )
ret = num1 / num2;
break;
}
}
bool evalExpression( char exp[] , double &ret )
{
//计算表达式的值
//操作符栈
char opStack[lineMax];
int opTop = 0;
//操作数栈
double numStack[lineMax];
int numTop = 0;
//记录两数的运算结果
double result = 0;
double num1;
double num2;
char op;
//读入表达式的字符
char ch;
//表达式当前的字符指针
int p = 0;
//构造数字时用到
double t;
opStack[0] = '#';
opTop = 1;
ch = exp[p++];
while( ch != '=' )
{
if( ch >= '0' && ch <= '9' )
{
//读入数字
//这里只把输入的double数据当作整数来处理
//事实在算24点时,输入的数总是整数
//如果要扩展,在这里进行修改
//考虑输入数带小数部分的情况
t = 0;
while( ch >= '0' && ch <= '9' )
{
t = 10 * t + ( ch - '0' );
ch = exp[p++];
}
numStack[numTop++] = t;
}
else if( ch == '(' )
{
opStack[opTop++] = ch;
ch = exp[p++];
}
else if( ch == ')' )
{
//每次运算前检查是否有足够的操作数及操作符
if( numTop <= 1 || opTop <= 1 )
return false;
num2 = numStack[--numTop];
num1 = numStack[--numTop];
op = opStack[--opTop];
solve( num1 , num2 , op , result );
numStack[numTop++] = result;
//括号不匹配
if( opStack[--opTop] != '(' )
return false;
ch = exp[p++];
}
else if( ( ch == '+' || ch == '-' ) && opStack[opTop-1] != '(' && opStack[opTop-1] != '#' )
{
if( numTop <= 1 || opTop <= 1 )
return false;
num2 = numStack[--numTop];
num1 = numStack[--numTop];
op = opStack[--opTop];
solve( num1 , num2 , op , result );
numStack[numTop++] = result;
}
else if( ( ch == '*' || ch == '/' ) && ( opStack[opTop-1] == '+' || opStack[opTop-1] == '-' ) )
{
opStack[opTop++] = ch;
ch = exp[p++];
}
else if( ( ch == '*' || ch == '/' ) && ( opStack[opTop-1] == '*' || opStack[opTop-1] == '/' ) )
{
if( numTop <= 1 || opTop <= 1 )
return false;
num2 = numStack[--numTop];
num1 = numStack[--numTop];
op = opStack[--opTop];
solve( num1 , num2 , op , result );
numStack[numTop++] = result;
}
else
{
opStack[opTop++] = ch;
ch = exp[p++];
}// end if
}// end while
if( numTop <= 1 || opTop <= 1 )
return false;
num2 = numStack[--numTop];
num1 = numStack[--numTop];
op = opStack[--opTop];
solve( num1 , num2 , op , result );
numStack[numTop++] = result;
ret = numStack[numTop-1];
return true;
}
bool solve24( double num[] , int n , int goal , char expression[][lineMax] , int &total )
{
//解决24点问题的主体部分
//n: total numbers
//num[]: the numbers
//goal: the result you want
//expression[][]: return the expression
int i;
int a , b , c , d;
int x , y , z;
char num1[lineMax];
char num2[lineMax];
char num3[lineMax];
char num4[lineMax];
char op1[lineMax];
char op2[lineMax];
char op3[lineMax];
char op[] = { '+' , '-' , '*' , '/' };
char formula[][lineMax] = {
"((axb)yc)zd=" ,
"(axb)y(czd)=" ,
"(ax(byc))zd=" ,
"ax((byc)zd)=" ,
"ax(by(czd))="
};
char exp[lineMax];
double ret;
total = 0;
for( a = 0 ; a < 4 ; a++ )
{
for( b = 0 ; b < 4 ; b++ )
{
if( a != b )
{
for( c = 0 ; c < 4 ; c++ )
{
if( a != c && b != c )
{
d = 6 - a - b - c;
// a b c d 是所给四个数的全排列
double2str( num1 , num[a] );
double2str( num2 , num[b] );
double2str( num3 , num[c] );
double2str( num4 , num[d] );
for( x = 0 ; x < 4 ; x++ )
{
for( y = 0 ; y < 4 ; y++ )
{
for( z = 0 ; z < 4 ; z++ )
{
if( x != z && y != z )
{
// x y z 从四种操作符中选出三种的排列
char2str( op1 , op[x] );
char2str( op2 , op[y] );
char2str( op3 , op[z] );
for( i = 0 ; i < 5 ; i++ )
{
str2str( exp , formula[i] );
strReplace( exp , "a" , num1 );
strReplace( exp , "b" , num2 );
strReplace( exp , "c" , num3 );
strReplace( exp , "d" , num4 );
strReplace( exp , "x" , op1 );
strReplace( exp , "y" , op2 );
strReplace( exp , "z" , op3 );
evalExpression( exp , ret );
if( fabs( ret - goal ) < .1e-4 )
{
//cout<<exp<<goal<<endl;
str2str( expression[total++] , exp );
}
}
}
}
}
}
}
}
}
}
}
return true;
}
void main()
{
double num[] = { 3 , 3 , 7 , 7 };
int n = 4;
int goal = 24;
char exp[1024][lineMax];
int total;
int i;
solve24( num , n , goal , exp , total );
for( i = 0 ; i < total ; i++ )
cout<<exp[i]<<endl;
}
程序分析:
当 num[] = { 3 , 3 , 7 , 7 } 时
输出结果为:
(3+(3/7))*7=
(3+(3/7))*7=
((3/7)+3)*7=
((3/7)+3)*7=
(3+(3/7))*7=
(3+(3/7))*7=
((3/7)+3)*7=
((3/7)+3)*7=
7*(3+(3/7))=
7*((3/7)+3)=
7*(3+(3/7))=
7*((3/7)+3)=
7*(3+(3/7))=
7*((3/7)+3)=
7*(3+(3/7))=
7*((3/7)+3)=
当 num[] = { 1 , 5 , 5 , 5 } 时
输出结果为:
(5-(1/5))*5=
(5-(1/5))*5=
5*(5-(1/5))=
5*(5-(1/5))=
(5-(1/5))*5=
(5-(1/5))*5=
5*(5-(1/5))=
5*(5-(1/5))=
(5-(1/5))*5=
(5-(1/5))*5=
5*(5-(1/5))=
5*(5-(1/5))=
当 num[] = { 4 , 5 , 6 , 7 } 时
输出结果为:
4*((5-6)+7)=
4*((5+7)-6)=
4*(5+(7-6))=
4*((7+5)-6)=
4*(7+(5-6))=
4*((7-6)+5)=
((5-6)+7)*4=
(5-(6-7))*4=
((5+7)-6)*4=
(5+(7-6))*4=
(5+7)*(6-4)=
(6-4)*(5+7)=
(6-4)*(7+5)=
((7+5)-6)*4=
(7+(5-6))*4=
(7+5)*(6-4)=
((7-6)+5)*4=
(7-(6-5))*4=
我们发现,程序并不能很还的解决多余括号问题,
如果有重复数出现时,程序也没有考虑结果式是否重叠。
这是本程序存在的缺点,
作者正在努力改进ing...
void char2str( char str[] , char ch )
void double2str( char str[] , double n )
bool evalExpression( char exp[] , double &ret )
void solve( double num1 , double num2 , char op , double &ret )
bool solve24( double num[] , int n , int goal , char expression[][lineMax] , int &total )
void str2str( char str[] , const char str2[] )
void strFind( const char str[] , const char subStr[] , int &reti , int &retj )
void strReplace( char str[] , const char subStr[] , char newStr[] )
以上是程序用到的函数,有些是比较实用的,也许在其它地方可能用到。