V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
lygmqkl
V2EX  ›  程序员

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

  •  1
     
  •   lygmqkl · 2015-10-30 09:12:55 +08:00 · 2816 次点击
    这是一个创建于 3318 天前的主题,其中的信息可能已经有所发展或是发生改变。

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

    要求:
    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
    随机选择一个然后用数字去填充位置,貌似也不是很好

    16 条回复    2015-10-30 18:55:53 +08:00
    lygmqkl
        1
    lygmqkl  
    OP
       2015-10-30 09:13:13 +08:00
    希望能分享到合适的思路。
    bearice
        2
    bearice  
       2015-10-30 09:29:05 +08:00   ❤️ 1
    1 )随机生成一个自然数数作为运算结果
    2 )随机生成一个运算符
    3 )随机生成一个自然数作为运算符左参数,计算右参数
    4 )对每个参数重复此过程,直到得到需要的算式长度为止
    lygmqkl
        3
    lygmqkl  
    OP
       2015-10-30 09:30:21 +08:00
    @bearice 目前我是这样做的,但是 除法不好解决
    bearice
        4
    bearice  
       2015-10-30 09:37:10 +08:00
    除法生成参数的时候就在 [1,9] 之间选择然后乘以结果
    lygmqkl
        5
    lygmqkl  
    OP
       2015-10-30 09:39:28 +08:00
    @bearice 这样哦,好像明白你的意思了, that's a good idea.
    lygmqkl
        6
    lygmqkl  
    OP
       2015-10-30 09:40:46 +08:00
    @bearice 顺便问下 乘法 怎么保证结果的准确? 应该是用除法来解决对吧
    demo
        7
    demo  
       2015-10-30 09:42:54 +08:00
    算法这个东西还是需要一定的知识基础的
    icedx
        8
    icedx  
       2015-10-30 09:48:25 +08:00 via Android
    随便生成 然后用 eval 验证结果 只有四则运算的话
    lygmqkl
        9
    lygmqkl  
    OP
       2015-10-30 10:00:51 +08:00
    @demo 其实我是觉得一个数一个数的 loop 太普通了,想看看有没有更好的方法。
    shiye515
        10
    shiye515  
       2015-10-30 10:13:09 +08:00
    首先,我假设楼主的意思是 参与运算的每个值 以及 每步运算的结果 都是不超过 100 的。那么,
    只有一步的算式很好解决,如楼主举的加法的例子,
    两步的运算就是把两个一步的算式嵌套下,(嵌套的意思是把参与运算的算式的一个值替换成一个算式)
    同理 三步的算式就是 一个两步算式嵌套一个一步的算式。
    Karblue
        11
    Karblue  
       2015-10-30 13:58:44 +08:00 via Android
    以前做过出题小程序。就随机生成。然后算一遍把期望的结果组留下就行了
    fengyqf
        12
    fengyqf  
       2015-10-30 15:51:25 +08:00
    随机生成吧,不过在成生后先计算一个结果,是不是在 100 以内,如果不是,就再生成一次。多让机器计算个四则,增加的时间消耗可以忽略
    rogerchen
        13
    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
        14
    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
        15
    dnartz  
       2015-10-30 16:12:56 +08:00
    akira
        16
    akira  
       2015-10-30 18:55:53 +08:00
    温习一遍波兰表达式,应该就足够了
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1035 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 24ms · UTC 19:31 · PVG 03:31 · LAX 11:31 · JFK 14:31
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.