经典二十四点程序算法

来源:http://www.xici.net/d190569991.htm

经典二十四点程序算法 –叶 宏
 

一、概述

算24点:任意给定四个整数,用加、减、乘、除以及适当的括号连接,无论顺序,使计算结果为24,也可能根本就无解。如何用程序简单实现找到算式是程序初学者关注的问题,百度上可以搜到许多这样的文章,有递归法、回溯法、穷举法。但穷举法最为简单、易于理解。

二、算法

穷举法就是把四个数字的所有运算式进行尝试计算,要设法把所有排列一点不差地穷举出,一、是四个整数位置的排列,用0,1,2,3表示位置,排列是不能重复的,所以有P(4,4)种情况,即4!=4*3*2*1=24种;二、是三个运算符的变化,每个运算符为+-*/ ,可以相同,所以,有4*4*4=64种; 三、三个运算符的优先级,就是括号位置的变化,可能性为P(3,3)-1=6-1=5种;所以,表达式的可能性为:24*64*5=7680种,C语言程序把这么多表达式都计算一遍,几乎不到1毫秒毫不费劲, 可见电脑的运行速度之快。

   四个数位置的排列,可用四重循环实现,四个循环变量i1,i2,i3,i4对应数组下标的变化,

不许它们之间相等,四个数放在数组v[0]、v[1]、v[2]、v[3]:

       for (int i1=0;i1<4;i1++)

        for (int i2=0;i2<4;i2++)                

           if (i2!=i1)               //  循环变量不许相同

           for (int i3=0;i3<4;i3++)

              if (i3!=i2 && i3!=i1)         //  循环变量不许相同,&& 是“并且”

              for (int i4=0;i4<4;i4++)

                 if (i4!=i3 && i4!=i2 && i4!=i1)   //  循环变量不许相同

                  {

                    //  v[i1],v[i2],v[i3],v[i4] 就是排列的结果,共有4!=24 种

                  }

   三个运算符的变化,可用三重循环实现,三个循环变量 f1,f2,f3对应位置的变化,允许相同,运算符放在数组op[0]、op[1]、op[2]、op[3]中:

        for (int f1=0;f1<4;f1++)      // 三重循环对运算符穷举

          for (int f2=0;f2<4;f2++)

            for (int f3=0;f3<4;f3++)  // 运算符 f1,f2,f3

                 {

                   // op[f1],op[f2],op[f3]就是排列结果,共有4*4*4=64 种                      

                 }

   上面两种排列组合后,四个数及运算符排列顺序就是:

    v[i1],op[f1],v[i2],op[f2],v[i3],op[f3],v[i4]

但运算符有优先级,一般是用括号表示。我们可以规定运算符的优先级来代替括号。设四张牌为a、b、c、d,运算符为①、②、③,表达式为a ① b ② c ③ d。 这3个运算符的运算顺序有3!=6种,分别是:

1.①②③    2.①③②    3.②①③    4.②③①    5.③①②    6.③②①

等价的表达式分别是:

1.((a①b②)c③)d          2.(a①b)②(c③d)            3.(a①(b②c))③d

4.a①((b②c)③d)        5.(a①b)②(c③d)            6. a①(b②(c③d))

显然,2和5是相同的,因此只考虑5种情况。这样,括号的问题就解决了。为了简单起见,这五种情况未用循环,大大减化了程序的复杂性,更易于理解。

 对这组排列逐一进行运算,看是否是24,就可以得到最终所有式子了。在运算过程中除法的特殊性——除数不能为零。因为可能会用到除法,所以要考虑精度问题,这里通过结果减去24取绝对值与一个接近0的小数(如0.001)比较,如小于它,即可判定结果是24。

#include "stdio.h"                         // 输入输出定义
double  cal(double a,double b,int op)      // op: 0:+,1:-,2:*,3:/
{
        switch (op)                        // +-x/ 运算
         {
           case 0:  return(a+b);
           case 1:  return(a-b);
           case 2:  return(a*b);
         }
        if (b==0.0)                       //  分母为0
            return(999.0);
        else
            return(a/b);
}
bool    isEqual(double d1,double d2)     // 两个浮点数是否近似相等
{
        double d=d1-d2;
        if (d<0)
            d=-d;                        // 求绝对值
        return(d<=0.001);
}
void   D24(int v0,int v1,int v2,int v3)          // 穷举法求24点
{
       char op[4]={'+','-','*','/'};             //  +:0  -:1 *:2  /:3
 
       int v[4];                                 // 输入四整数
       v[0]=v0; v[1]=v1;
       v[2]=v2; v[3]=v3;
//-----------四重循环开始穷举四个数字的位置: 4!=24 种--------------------------
       int count=0;                              // 计数成功次数
       for (int i1=0;i1<4;i1++)
        for (int i2=0;i2<4;i2++)                 // 四重循环对四个数穷举
          if (i2!=i1)
          for (int i3=0;i3<4;i3++)
            if (i3!=i2 && i3!=i1)
            for (int i4=0;i4<4;i4++)
               if (i4!=i3 && i4!=i2 && i4!=i1)
                 {
//-----------三重循环开始穷举三个运算符: 4X4X4=64 种---------------------------
                   for (int f1=0;f1<4;f1++)      // 三重循环对运算符穷举
                     for (int f2=0;f2<4;f2++)
                       for (int f3=0;f3<4;f3++)  // 运算符 f1,f2,f3
                         {                       // 对运算优先级直接列举(5种)
//-----------未用循环,直接穷举三个运算符的优先级: 3!-1=5种--------------------
                           double t1,t2,t3;      // 存放计算的中间值
                           // 第1种情况  ((a 。b)。c)。d  :
                           t1=cal(v[i1],v[i2],f1);
                           t2=cal(t1,v[i3],f2);
                           t3=cal(t2,v[i4],f3);
                           if (isEqual(t3,24))    // 运算后是否为24
                            {
                              char *fs="((%d%c%d)%c%d)%c%d=24\\n";
                              printf(fs,v[i1],op[f1],v[i2],op[f2],v[i3],op[f3],v[i4]);
                              count++;
                            }
                           // 第2种情况(a 。b)。(c。 d) 开始计算 :
                           t1=cal(v[i1],v[i2],f1);
                           t2=cal(v[i3],v[i4],f3);
                           t3=cal(t1,t2,f2);
                           if (isEqual(t3,24))      // 运算后是否为24
                            {
                              char *fs="(%d%c%d)%c(%d%c%d)=24\\n";
                              printf(fs,v[i1],op[f1],v[i2],op[f2],v[i3],op[f3],v[i4]);
                              count++;
                             }
                           // 第3种情况 (a。(b。c))。d  开始计算 :
                           t1=cal(v[i2],v[i3],f2);
                           t2=cal(v[i1],t1,f1);
                           t3=cal(t2,v[i4],f3);
                           if (isEqual(t3,24))    // 运算后是否为24
                            {
                              char *fs="(%d%c(%d%c%d))%c%d=24\\n";
                              printf(fs,v[i1],op[f1],v[i2],op[f2],v[i3],op[f3],v[i4]);
                              count++;
                            }
                           // 第4种情况  a。((b。c)。d ) 开始计算:
                           t1=cal(v[i2],v[i3],f2);
                           t2=cal(t1,v[i4],f3);
                           t3=cal(v[i1],t2,f1);
                           if (isEqual(t3,24))    // 运算后是否为24
                           {
                             char *fs="%d%c((%d%c%d)%c%d)=24\\n";
                             printf(fs,v[i1],op[f1],v[i2],op[f2],v[i3],op[f3],v[i4]);
                             count++;
                           }
                           // 第5种情况 a。(b。(c。d)) 开始计算:
                           t1=cal(v[i3],v[i4],f3);
                           t2=cal(v[i2],t1,f2);
                           t3=cal(v[i1],t2,f1);
                           if (isEqual(t3,24))    // 运算后是否为24
                           {
                             char *fs="%d%c(%d%c(%d%c%d))=24\\n";
                             printf(fs,v[i1],op[f1],v[i2],op[f2],v[i3],op[f3],v[i4]);
                             count++;
                           }
                         }
                   }
//-------------- 穷举结束: 共 24*64*5=7680 种表达式 ---------------------------
                if (count==0)
                    printf("\\n%d,%d,%d,%d 不能算出24.",v0,v1,v2,v3);
                else
                    printf("\\n共找到算式 %d 条.",count);
}
void    main()
{
        int v0,v1,v2,v3;
        printf("输入四个整数: ");
        scanf("%d %d %d %d",&v0,&v1,&v2,&v3);
        D24(v0,v1,v2,v3);
        getchar();
        getchar();
}

 

    原文作者:常用算法
    原文地址: https://blog.csdn.net/smartboysboys/article/details/20765427
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞