[讨论]如何能稳定的生成 带正负号的四则运算?

2015-10-30 09:12:55 +08:00
 lygmqkl

最近在做一个有意思的东西,里面有一块是生成四则运算给小朋友学习用的,基本已经都实现了,但是四则运算中有一部分算法我不是很满意,下面举例,希望能找到好的方向,谢谢各位参与。

要求:
A. 加减乘除 4 则运算,带正负号
B. 要能预先控制结果, 比如 A+B=C, 要求 100 以内,先 C = rand(1,100), A = rand(0, C), 这样就能算出 B 了

说明:
在简单模式下自然没有什么问题,我遇到的问题是在 A+(-B)*C/(-D+A), 在这种模式下只能大概控制结果的范围,无法做到精确控制

最新思路:
我准备下面考虑用填充法来做生成逻辑 比如
甲 A + B * C / D
乙 A*B + C/D
随机选择一个然后用数字去填充位置,貌似也不是很好

2800 次点击
所在节点    程序员
16 条回复
lygmqkl
2015-10-30 09:13:13 +08:00
希望能分享到合适的思路。
bearice
2015-10-30 09:29:05 +08:00
1 )随机生成一个自然数数作为运算结果
2 )随机生成一个运算符
3 )随机生成一个自然数作为运算符左参数,计算右参数
4 )对每个参数重复此过程,直到得到需要的算式长度为止
lygmqkl
2015-10-30 09:30:21 +08:00
@bearice 目前我是这样做的,但是 除法不好解决
bearice
2015-10-30 09:37:10 +08:00
除法生成参数的时候就在 [1,9] 之间选择然后乘以结果
lygmqkl
2015-10-30 09:39:28 +08:00
@bearice 这样哦,好像明白你的意思了, that's a good idea.
lygmqkl
2015-10-30 09:40:46 +08:00
@bearice 顺便问下 乘法 怎么保证结果的准确? 应该是用除法来解决对吧
demo
2015-10-30 09:42:54 +08:00
算法这个东西还是需要一定的知识基础的
icedx
2015-10-30 09:48:25 +08:00
随便生成 然后用 eval 验证结果 只有四则运算的话
lygmqkl
2015-10-30 10:00:51 +08:00
@demo 其实我是觉得一个数一个数的 loop 太普通了,想看看有没有更好的方法。
shiye515
2015-10-30 10:13:09 +08:00
首先,我假设楼主的意思是 参与运算的每个值 以及 每步运算的结果 都是不超过 100 的。那么,
只有一步的算式很好解决,如楼主举的加法的例子,
两步的运算就是把两个一步的算式嵌套下,(嵌套的意思是把参与运算的算式的一个值替换成一个算式)
同理 三步的算式就是 一个两步算式嵌套一个一步的算式。
Karblue
2015-10-30 13:58:44 +08:00
以前做过出题小程序。就随机生成。然后算一遍把期望的结果组留下就行了
fengyqf
2015-10-30 15:51:25 +08:00
随机生成吧,不过在成生后先计算一个结果,是不是在 100 以内,如果不是,就再生成一次。多让机器计算个四则,增加的时间消耗可以忽略
rogerchen
2015-10-30 16:01:43 +08:00
楼主这个问题和二十四点差不多,可以选定一个固定的算式 pattern ,枚举求解给定答案的所有算式。
举个例子,比如说
A op1 B op2 C op3 D op4 E = F
其中 F 给定, ABCDE 和 op1~op4 都可以在给定范围内遍历。

说实在的,这个方法虽然暴力,但是对处理小学生问题还手到擒来的,枚举几亿种可能对现代计算机不过洒洒水了。而且伸缩性也好,如果要缩减 operand 的数量,直接规定为*1 即可。所以楼主随便找个有 eval 算式的工具包,两分钟就搞定。
dnartz
2015-10-30 16:10:44 +08:00
可以考虑这样一个函数,返回一个以非叶子节点为运算符,叶子节点为数字(含正负号)的语法抽象树。
tree F(int c, int nOptr) ->
operator = +-*/ 随机生成一个
int a, b;
tree left, right;

a = rand(1,c);
switch operator
b 为算符的右操作数,我们在这个 switch 里根据 operator 反推出 a

// 如果我们满足继续拆分一个数字的条件,就继续拆分这个数字
// 拆分的条件就要依楼主的需求而确定了
// 比如只生成 n 个数字,那么我们可以引入一个参数 int nOptr
// 我们用随机函数将 int nOptr 划分为 nOptr1 和 nOptr2
// 表示以 operator 为根节点的语法树左右两侧可以拥有的算符的数目,如果 nOptr1 或者 nOptr2 为 0 ,那就不拆分对应的数字
// 显然地,一个含有 n 个数字的算式,最多只可能有 n-1 个四则运算符(不是括号)
// 所以对于一个含有 n 个数字,最后运算结果为 c 的算式,我们应该这样调用 F : F(c, n-1)
nOptr1 = rand(1, nOptr)
nOptr2 = nOptr - nOptr1
if nOptr1 > 0
left = f(a, nOptr1)
else
left = new leafNode(a)
if nOptr2 > 0
right = f(b ,nOptr2)
else
right = new leafNode(b)

在生成语法树之后,我们也就可以生成算式了

在执行完这个算法之后,我们就可以通过这个语法树生成一个算式了
dnartz
2015-10-30 16:12:56 +08:00
akira
2015-10-30 18:55:53 +08:00
温习一遍波兰表达式,应该就足够了

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/232177

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX