24点游戏算法(C语言实现)

  • Post author:
  • Post category:其他

转自:点击打开链接

 

一般而言,我理解的24点游戏是用4个数字经过数学运行,得到24. 一般推广版的应该使用扑克的52张牌(出去大小王),A表示1,K表示13.任意4张牌看是否能够组合得到24.
我这里所设计的就是这样的情况,而不是任意的4个数。(因为这里需要用一个办法表示分数,而我使用了比较简单的一种,能表示范围较小,所以我只处理了1到13这个情况。虽然实现里边可以将13这个范围向上推一段。)
4个数进行四则运算,一般的思维是四个数中间的3个空放置3个运算符,然后可能还需要在不同的地方加上括号,表示优先级的不同。仅仅是数字和运算符好好做,一旦要添加括号就会有点乱七八糟。前两天看网页突然看到后缀表达式这个东西,我一下想起来了。(网页作者说后缀表达式可以实现无括号的优先级运算)那我使用后缀表达式的这种方法来做就简单了。反正就只有4个数。
假设I、J、M、N是4个数,r、s、t是3个运算符。那么使用后缀表达式只有如下4种情况:
1)I J r M N s t ==> (I r J) t (M s N)
2)I J r M s N t ==> ((I r J) s M) t N
3)I J M r s N t ==> (I s (J r M)) t N
4)I J M N r s t ==> I t (J s (M r N))
不理会所有的重复,上面4种情况分别有1536中可能,那么总计就是 6144中可能情况。每种情况计算3次,也就20000次计算而已。对计算机来说这个数目是极小的。
在实现中,我没有采用任何取巧的办法,而是每种情况使用了7个for循环进行计算。唯一的取巧是前两种的前边4个for循环是一样,我就将两种拼起来了。
处理整数计算还有一个问题是,计算机无法精确的表示分数,特别是那种无限小数,比如1/3这种。如果不能精确计算,最后就无法判断是否精确等于24,这是计算机的一个弱点,这点不如人啊。
我的方法是,每个数字都用一个int表示,int表示为4字节。由于最大的单个数为13,那么即使是13的连乘也不过28561,不足两个字节能表示的65536 。因此我使用一个int的低两个字节表示分子或者实际的数值(没有分母的,也就等于整个int表示的数),高两个字节表示分母(不存在分母则表示为0)。在做计算时将分子统一为等分母的数,然后计算之后与分母作用得到最后的数。这样的话4/5就表示为了0x00050004了。
由于找到一种解法就结束,所以根据输入的数据,得到的可能与通常人们计算得到的结果有出入,比如2 2 6 6 ,人的计算结果一般为2*6+2*6,但是程序结果可能为(2+6)*(6/2),当然结果也是正确的。
另外需要注意的是,对于任何两个数计算得到负数,我都没有继续啊向下计算,因为这个必须得加一个正数或者乘以一个辅助才可能得到24 。而既然能够得到负数,两个数的位置调换一下就是一个正数了,没有必要在数值前边加上负号来处理。
具体实现代码如下,c语言实现:

* 24.c 计算4个1到13之间的数(包含)是否能够通过加减乘除达到24.*/
#include <stdio.h>

#define MASK 0xFFFF
#define SHIFT 16
enum {FAILURE = 0, SUCCESS};
const char ops[] = "+*-/";

int test (const int nums[], char *result);
int calculate (int num1, int num2, char op);

int main(int argc, char * argv[])
{
    int len = 0;
    int nums[4] = {0};
    int i;
    char *result;
    if (argc != 5)
    {
        printf("Usage: 24.exe num1 num2 num3 num4\n");
        exit(1);
    }
    for (i = 0 ;i < 4 ;i++ )
    {
        len += strlen (argv[i+1]);
        nums[i] = atoi (argv[i+1]);
        if (nums[i] < 1 || nums[i] > 13)
        {
            printf ("所有的数字都应该是1到13正整数\n");
            exit(2);
        }
    }
    /*2 pairs of paren, 3 operators, 1 for NULL. */
    len += 4 + 3 + 1;
    result = (char *)malloc (len);
    
    if (test(nums, result))
        printf ("%s\n", result);
    else
        printf ("No Matched.\n");
    
    if (result != NULL)
        free (result);

    return 0;
}
/* 测试所有可能的情况,找到一种解法*/
int test (const int nums[], char * result)
{
    int I, J, M, N;
    int r, s, t;
    int ret, ret1, ret2;

    /*first loop: I J r M N s t ==> (I r J) t (M s N) */
    for (I = 0; I < 4; I++)
    {
        for (J = 0; J < 4; J++)
        {
            if (J == I)
                continue;
            for (r = 0; r < 4; r++)
            {
                ret1 = calculate (nums[I], nums[J], ops[r]);
                if (ret1 <= 0)
                    continue;
                for (M = 0; M < 4; M++)
                {
                    if (M == I || M == J)
                        continue;
                    for (N = 0; N < 4; N++)
                    {
                        if (N == I || N == J || N == M)
                            continue;
                        for (s = 0; s < 4; s++)
                        {
                            ret2 = calculate (nums[M], nums[N], ops[s]);
                            if (ret2 <= 0)
                                continue;
                            for (t = 0; t < 4; t++)
                            {
                                ret = calculate (ret1, ret2, ops[t]);
                                if (((ret&MASK)==24) && ((ret>>SHIFT)==0))
                                {
                                    sprintf (result, "(%d%c%d)%c(%d%c%d)", 
                                        nums[I], ops[r], nums[J], ops[t],
                                        nums[M], ops[s], nums[N]);
                                    return SUCCESS;
                                }
                            }
                        }
                    }
                    /* second loop: I J r M s N t ==> ((I r J) s M) t N */
                    for (s = 0; s < 4; s++)
                    {
                        ret2 = calculate (ret1, nums[M], ops[s]);
                        if (ret2 <= 0)
                            continue;
                        for (N = 0; N < 4; N++)
                        {
                            if (N == I || N == J || N == M)
                                continue;
                            for (t = 0; t < 4; t++)
                            {
                                ret = calculate (ret2, nums[N], ops[t]);
                                if (((ret&MASK)==24) && ((ret>>SHIFT)==0))
                                {
                                    sprintf (result, "((%d%c%d)%c%d)%c%d", 
                                        nums[I], ops[r], nums[J], ops[s],
                                        nums[M], ops[t], nums[N]);
                                    return SUCCESS;
                                }
                            }
                        }
                    }
                }
            }
        }
    }
    /* third loop: I J M r s N t ==> (I s (J r M)) t N */
    for (I = 0; I < 4; I++)
    {
        for (J = 0; J < 4; J++)
        {
            if (J == I)
                continue;
            for (M = 0; M < 4; M++)
            {
                if (M == I || M == J)
                    continue;
                for (r = 0; r < 4; r++)
                {
                    ret1 = calculate (nums[J], nums[M], ops[r]);
                    if (ret1 <= 0)
                        continue;
                    for (s = 0; s < 4; s++)
                    {
                        ret2 = calculate (nums[I], ret1, ops[s]);
                        if (ret2 <= 0)
                            continue;
                        for (N = 0; N < 4; N++)
                        {
                            if (N == I || N == J || N == M)
                                continue;
                            for (t = 0; t < 4; t++)
                            {
                                ret = calculate (ret2, nums[N], ops[t]);
                                if (((ret&MASK)==24) && ((ret>>SHIFT)==0))
                                {
                                    sprintf (result, "(%d%c(%d%c%d))%c%d", 
                                        nums[I], ops[s], nums[J], ops[r],
                                        nums[M], ops[t], nums[N]);
                                    return SUCCESS;
                                }
                            }
                        }
                    }
                }
            }
        }
    }
    /* forth loop: I J M N r s t ==> I t (J s (M r N)) */
    for (I = 0; I < 4; I++)
    {
        for (J = 0; J < 4; J++)
        {
            if (J == I)
                continue;
            for (M = 0; M < 4; M++)
            {
                if (M == I || M == J)
                    continue;
                for (N = 0; N < 4; N++)
                {
                    if (N == I || N == J || N == M)
                        continue;
                    for (r = 0; r < 4; r++)
                    {
                        ret1 = calculate (nums[M], nums[N], ops[r]);
                        if (ret1 <= 0)
                            continue;
                        for (s = 0; s < 4; s++)
                        {
                            ret2 = calculate (nums[J], ret1, ops[s]);
                            if (ret2 <= 0)
                                continue;
                            for (t = 0; t < 4; t++)
                            {
                                ret = calculate (nums[I], ret2, ops[t]);
                                if (((ret&MASK)==24) && ((ret>>SHIFT)==0))
                                {
                                    sprintf (result, "%d%c(%d%c(%d%c%d))", 
                                        nums[I], ops[t], nums[J], ops[s],
                                        nums[M], ops[r], nums[N]);
                                    return SUCCESS;
                                }
                            }
                        }
                    }
                }
            }
        }
    }

    return FAILURE;
}
/* 计算两个特定的数和操作符的结果*/
int calculate (int num1, int num2, char op)
{
    int numerator_num1, numerator_num2;
    int denominator_num1, denominator_num2;
    int ret = 0;
    int denominator, numerator;

    denominator_num1 = num1 >> SHIFT;			//分母
    denominator_num2 = num2 >> SHIFT;
    numerator_num1 = num1 & MASK;			//分子
    numerator_num2 = num2 & MASK;
    /* 确定分母,将分子同一化*/
    if (denominator_num1 > 0 && denominator_num2 > 0)
    {
        denominator    = denominator_num1 * denominator_num2;
        numerator_num1 = denominator_num2 * numerator_num1;
        numerator_num2 = denominator_num1 * numerator_num2;
    }
    else if (denominator_num1 > 0 && denominator_num2 == 0)
    {
        denominator    = denominator_num1;
        numerator_num2 = denominator_num1 * numerator_num2;
    }
    else if (denominator_num1 == 0 && denominator_num2 > 0)
    {
        denominator    = denominator_num2;
        numerator_num1 = denominator_num2 * numerator_num1;
    }
    else
    {
        denominator = 0;
    }
    /* 计算*/
    if (op == '+')
    {
        numerator = numerator_num1 + numerator_num2;
    }
    else if (op == '-')
    {
        numerator = numerator_num1 - numerator_num2;
    }
    else if (op == '*')
    {
        numerator    = numerator_num1 * numerator_num2;
        denominator *= denominator;
    }
    else if (op == '/')
    {
        if (numerator_num2 > 0 && numerator_num1%numerator_num2 == 0)
        {
	    /* 分子可以整除,分母就没有必要了。*/
            numerator   = numerator_num1 / numerator_num2;
            denominator = 0;
        }
        else
        {
            numerator   = numerator_num1;
            denominator = numerator_num2;
        }
    }
    if (denominator>0 && numerator%denominator == 0)
    {
        numerator   = numerator / denominator;
        denominator = 0;
    }
    ret = (numerator<=0)?numerator:((numerator&MASK) | (denominator<<SHIFT));
    return ret;
}