C语言递归实现24点游戏计算器:算法原理与VS2019完整教程
利用C语言中的递归思想与回溯策略,可以系统地枚举四个数字间的所有四则运算组合及括号排列方式,从而高效求解24点问题。
一、算法核心逻辑与数据模型
24点游戏的本质是在给定的四个整数中,寻找通过加、减、乘、除以及括号嵌套能够得到目标值(通常是24)的数学表达式。其难点在于运算顺序的多变性(如 (a + b) * (c - d) 与 a + (b * c) / d)。
算法采用递归(Recursive)与分治的思想:如果牌堆里只剩一张牌,判断该值是否等于24;否则,从当前牌堆中任取两张牌,将其进行四种基本运算(加、减、乘、除),并将结果放回牌堆,然后对剩下的牌堆(张数减一)继续递归计算。
为了同时输出具体的算式过程,程序需要维护每个中间状态的值及其对应的字符串表达式。使用浮点数(double)存储数值是为了兼容除法可能产生的小数情况,并通过设定极小值阈值(PRECISION = 1E-6)来判断浮点数比较时的相等关系。
二、开发环境准备
本例以 Visual Studio 2019 为例进行演示。其他编译器(如 GCC 或 MinGW)在语法上通用,但工程配置略有不同。
第1步:创建 C 语言项目
打开 VS2019,依次点击 创建新项目 → 搜索或选择 C++ 空项目 模式,由于是纯 C 代码,建议将文件名后缀统一改为 .c 以避免链接器警告,或者在属性中将文件视为 C 代码处理。此处展示标准新建步骤:
第2步:配置项目名称与路径
输入项目名称(如 Game24),选择存放路径并确认创建:
第3步:添加头文件与源文件
在项目视图中分别添加两个头文件和两个源文件,保持代码模块化:
三、源代码实现细节
第1步:定义公共依赖 (Main.h)
引入标准输入输出库和标准工具库:
#pragma once
#include <stdio.h>
#include <stdlib.h>
第2步:定义数据结构与接口声明 (PointGame.h)
这里定义了核心的 `Card` 结构体。它包含一个 `value` 用于存储当前数值,以及一个 `expression` 字符串数组用于记录当前的算式推导形式。宏定义限制了最大表达式长度以防越界。
#pragma once
#include <stdbool.h>
#include <math.h>
#define PRECISION 1E-6
#define TARGET 24
#define SIZE 4
typedef struct card
{
double value;
// 估算最大表达式长度:(SIZE-1)个运算符 + SIZE个数字 + 括号等开销
char expression[5 * (SIZE - 1) + SIZE * 2 + 1];
} Card, *pCard;
bool game24(pCard card, int size); // 递归入口函数声明
第3步:编写核心递归引擎 (PointGame.c)
这是最关键的逻辑层。`game24` 函数接收当前的“卡牌堆”及其剩余数量 size。循环遍历所有不重复的两两配对 (m, n),生成剩余卡片堆,并将这两张卡片的四则运算结果压入堆底作为新的递归子问题。
- 加法/乘法:满足交换律,只需进行一次组合即可。
sprintf(stack[size - 2].expression, "(%s + %s)", m.expr, n.expr); - 减法:不满足交换律,需分别计算
m - n和n - m。
注:原代码为节省空间优化了条件分支,实际上两种差值都要尝试。 - 除法:同样不满足交换律,且必须检查分母不为 0。
if (card[m].value != 0) { ... } if (card[n].value != 0) { ... }
#define _CRT_SECURE_NO_WARNINGS
#include "Main.h"
#include "PointGame.h"
bool game24(pCard card, int size)
{
Card stack[3]; // 临时存储下一层的4张牌变成3张牌
/* 递归基线条件:只剩一张牌 */
if (size == 1)
{
// 浮点数相等判断需考虑精度误差
if (fabs(card[0].value - TARGET) < PRECISION)
{
printf("%d = %s\n", TARGET, card[0].expression);
return true; // 找到解,可返回true终止后续搜索
}
else
{
return false;
}
}
/* 遍历选取两张牌的所有组合 */
int m, n, i, t;
for (m = 0; m < size - 1; m++)
{
for (n = m + 1; n < size; n++)
{
/* 首先把未被选中的其余牌拷贝到新堆栈 */
t = 0;
for (i = 0; i < size; i++)
{
if (i != m && i != n)
{
stack[t].value = card[i].value;
sprintf(stack[t].expression, "%s", card[i].expression);
t++;
}
}
/* --- 尝试加法 --- */
stack[size - 2].value = card[m].value + card[n].value;
sprintf(stack[size - 2].expression, "(%s + %s)", card[m].expression, card[n].expression);
if (game24(stack, size - 1)) return true;
/* --- 尝试减法 (双向) --- */
if (card[m].value > card[n].value)
{
stack[size - 2].value = card[m].value - card[n].value;
sprintf(stack[size - 2].expression, "(%s - %s)", card[m].expression, card[n].expression);
}
else
{
stack[size - 2].value = card[n].value - card[m].value;
sprintf(stack[size - 2].expression, "(%s - %s)", card[n].expression, card[m].expression);
}
if (game24(stack, size - 1)) return true;
/* --- 尝试乘法 --- */
stack[size - 2].value = card[m].value * card[n].value;
sprintf(stack[size - 2].expression, "%s * %s", card[m].expression, card[n].expression);
if (game24(stack, size - 1)) return true;
/* --- 尝试除法 (分母防零) --- */
if (card[m].value != 0)
{
stack[size - 2].value = card[n].value / card[m].value;
sprintf(stack[size - 2].expression, "%s / %s", card[n].expression, card[m].expression);
if (game24(stack, size - 1)) return true;
}
if (card[n].value != 0)
{
stack[size - 2].value = card[m].value / card[n].value;
sprintf(stack[size - 2].expression, "%s / %s", card[m].expression, card[n].expression);
if (game24(stack, size - 1)) return true;
}
}
}
return false;
}
第4步:主函数交互逻辑 (Main.c)
负责获取用户输入的4个整数,初始化 `Card` 结构体的初始值,并调用 game24。若存在解打印 Done,否则打印 No Answer。
#define _CRT_SECURE_NO_WARNINGS
#include "Main.h"
#include "PointGame.h"
int main()
{
system("color 3E"); // 设置控制台背景色
int i, poke;
Card card[SIZE];
printf("请输入4个数字 以判断解法:");
for (i = 0; i < SIZE; i++)
{
scanf("%d", &poke);
card[i].value = (double)poke;
// 将输入整数转为字符串存入表达式字段
sprintf(card[i].expression, "%d", poke);
}
if (game24(card, SIZE))
{
printf("Done\n");
}
else
{
printf("No Answer\n");
}
system("pause");
return 0;
}
四、编译与测试验证
第1步:生成解决方案
在 VS2019 菜单栏点击 生成 → 生成解决方案。确保无错误或警告产生:
第2步:调试运行示例
按下 F5 启动调试。以常见的牌面 3, 8, 8, 9 进行测试:
运行输出预期
程序会识别出多种等价组合,例如输出: 24 = ((9 - 8) * 8) * 3 或类似形式的算式。
五、总结与进阶思考
上述代码清晰展示了利用递归回溯算法自动穷举 24 点可能性的全过程。在实际应用中,还可以进一步扩展以下功能:
- 去重逻辑: 当前程序会输出所有找到的解,包括互为逆运算或符号差异的重复解,可根据需求增加过滤机制。
- 多解统计: 修改返回值类型为计数器累加,而非找到一个即停止。
- 复杂分数运算: 虽然使用了
double,但对于某些极端有理数精度丢失场景,可使用分数类(结构体分子分母)代替浮点数以获得更严谨的数学表达。
以上就是C语言实现24点游戏计算器的详细内容,更多关于C语言经典算法解析的资料请关注本站其它相关文章!
本站声明
本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。













