你能简化这个表达式吗?

25

这个问题适合数学家。它已经在办公室里流传开来,我们想看看谁能想出一个更优化的版本。

(((a+p) <= b) && (a == 0 || a > 1) && (b >= p)) && 
    ((b - (a + p) == 0) || (b - (a + p) > 1))

编辑: 所有数据都是正整数

编辑: 更好等于更简单


变量有什么限制吗?例如整数类型、隐式int->bool转换等等? - mkoeller
1
亚当,你现在可能要对数百个工作小时的损失负责了,哈哈。 - PEZ
很高兴你们都在帮忙!我会运行其中一些并发布我的结果。 - Adam Naylor
那么你打算发布你们办公室得出的答案是什么,对吗? - rjzii
2
只是想推荐一下:http://codegolf.stackexchange.com/ 我认为这个问题更适合在那里提问 :) - The Unfun Cat
显示剩余7条评论
27个回答

37
(a + p <= b) && (a != 1) && (b - a - p != 1);

通过了我的测试(0-200的所有可能组合) - Brian Genisio
你如何从 (b >= p)) && ((b - (a + p) != 1) 转化为 (b - a - p != 1)?我可以看出最后一项是等价的,但是如何去掉 b>=p? - frankodwyer
嗯...我明白a+b <= b等价于b>=a+p,但我花了一些时间才看出这意味着b>=p...再加上a>=0的事实,是的,我明白你的观点。 - frankodwyer
不,只有0-200。假设输入范围可以接近最大值,一些最大值测试将非常有用。 - Brian Genisio
(a!= 1)是最简单的评估,可以首先完成。@Brian的建议适用于所有组合[0,600)。 - strager
显示剩余17条评论

17
如果公式有效且源自您的业务规则,则没有真正需要简化它的必要。编译器可能比我们更了解如何优化公式。
唯一需要做的是使用更好的变量名称来反映业务逻辑。
在单元测试之前,小心应用任何提出的解决方案。

我同意这一点。特别是如果公式来自某种科学论文。你希望这些类型的公式可以逐字阅读,这样你就可以引用来源。 - Brian Genisio
哦,“不要动脑筋”?简化复杂的条件语句并不是“优化”,而是重构/提高清晰度。 - ShreevatsaR
@Brian - 关于来源,你说得很好,但科学论文通常会在论文中给出最简单的方程式。一般来说,当你将它们编码时,这些公式往往会变得更长。 - rjzii
当业务规则在当前配置中有意义地反映出来时(更好的变量名称会使这一点显而易见),它本身并不过于复杂。 - Dan Vinton
我简直不敢相信我要说这话,但在这特定的情况下,说“编译器可能比我们更知道如何优化公式”并不正确。有些推断可能非常复杂。(续) - Jeffrey L Whitledge
显示剩余3条评论

5

通过引入更多表示每个表达式含义的本地变量来简化重构。如果没有对a、b和p的含义有所了解,这对我们来说是很困难的。


很棒的回答。用好名字构建函数——你的回答是正确的。 - rp.
强烈怀疑添加中间变量会使代码更易于理解 - 这有助于之后的简化。请注意,我并不是建议仅仅更改变量名称 - 要提取常见表达式并给它们赋予有意义的名称。 - Jon Skeet
但是提取常见表达式并将其封装在另一个名称后会导致无法消除子表达式的能力... - Steven A. Lowe
你忘记的关键词是“有意义的” :) - Jon Skeet
给那些点踩的人:请在点踩时留下评论,否则它几乎没有任何积极影响。 - Jon Skeet
显示剩余4条评论

4
b >= p && b != p+1

编辑: 好的,之前那个方法不行,但是这个可以:

a != 1 && b >= a+p && b-a-p != 1

你是怎么去掉 "(a == 0 || a > 1)" 部分的? - PEZ
@TomWij:是的,事实证明a并不是那么无关紧要 =)) - Can Berk Güder

4
(a!=1) && ((b==a+p) || (b>1+a+p))

这可能不是最简单的,但应该是最易读的之一。


我认为所有的答案都应该经过Tom Wizmap的测试并附上证明。 - v.oddou

2

在那个表达式中,我不会计算所有的数学运算。例如,b - ( a + p ) 被计算了两次。如果可能的话,应该将它们拆分为变量。

此外,编写波兰式表示法树可能有助于优化它,可以重复使用所有看到两次的内容。


2

由于它们都是正整数,因此可以删除很多重复的内容:

因此,作为第一步,

(((a+p) <= b) && (a == 0 || a > 1) && (b >= p)) && ((b - (a + p) == 0) || (b - (a + p) > 1))

变成

((a+p) <= b) && (a != 1) && (b >= p)) && ((b - (a + p) != 1) 

为了更加清晰易懂,这只是将(foo == 0 || foo > 1)的模式替换为foo != 1

该模式在上述内容中出现两次,一次是当foo=a时,另一次是当foo = (b - (a+p))时。


怎么做?两者都应该是false,因为它们具有相同的 b>=p 条件,这是false,并使主&&条件成为false。 - frankodwyer
@TomWij:a 和 b 不能等于 0,它们都是正整数(也就是大于 0)。 - Steven A. Lowe
由于原始代码包括“a == 0”,所以我认为可以安全地假设他的意思是“非负整数”,而不是“正整数”。 - nickf

2
s = a + p
b >= s && a != 1 && b - s - 1 > 0

Checked,返回与问题相同的布尔值。

我用来检查的程序:(编写它很有趣)

#include <iostream>
using namespace std;

typedef unsigned int uint;

bool condition(uint a, uint b, uint p)
{
        uint s = a + p;
        return uint(    b >= s && a != 1 && b - s - 1 > 0    )
        == uint(    (((a+p) <= b) && (a == 0 || a > 1) && (b >= p))
                 && ((b - (a + p) == 0) || (b - (a + p) > 1))    );
}

void main()
{
    uint i = 0;
    uint j = 0;
    uint k = 0;

    const uint max = 50;

    for (uint i = 0; i <= max; ++i)
        for (uint j = 0; j <= max; ++j)
            for (uint k = 0; k <= max; ++k)
                if (condition(i, j, k) == false)
                {
                    cout << "Fails on a = " << i << ", b = " << j;
                    cout << ", p = " << k << endl;

                    int wait = 0;
                    cin >> wait;
                }
}

使用您的测试夹具,我无法在我的解决方案上出现故障,尽管您已经评论说它在0,2,1处失败了。 - Brian Genisio
比“b - s - 1 > 0”更清晰。 - PEZ
2,4,2怎么样:4-(2+2)-1=-1。你在这里依赖于溢出吗?很巧妙,但如果进行范围检查呢?(顺便说一句,这就是为什么b-s>1不起作用的原因,因为4-(2+2)=0是通过(b - (a + p) == 0)有效的。) - malach
嗯,人们对我的观点投票反应过于敏感了。我的回答没有任何问题... - Tamara Wijsman
将单位转换是否会消除负结果?从而可能隐藏错误? - Steven A. Lowe
3 满足性问题?(3SAT - v.oddou

1

由于整数是无符号的,因此可以用(a==0 || a>1)代替(a !=1)。

通过第一次处理,您可以将其简化为:

uint sum = a + p;
return ((sum <= b) && (a != 1) && (b >= p)) && (b - sum != 1);

此外,如果您能够为变量赋予更有意义的名称,那么代码将更易读。例如,如果a和p代表压力,则a+p可以替换为PressureSum。

不,它没有失败。我刚测试过了。两种实现在Function(0, 2, 1)上都返回false。请撤回那条评论。 - Brian Genisio
@TomWij: a 不能等于0,它是正整数(也就是>0)。 - Steven A. Lowe

1
bap = b - (a + p)
bap >= 0 && bap != 1 && a != 1

编辑:现在我因为诚实地试图帮忙以及提供了一个我认为是有效的答案而被扣去了-2分。对于那些能使用Python的人,这里有两个函数,一个包含问题,另一个包含我的答案:

def question(a, b, p):
    return (((a+p) <= b) and (a == 0 or a > 1) and (b >= p)) or ((b - (a + p) == 0) or (b - (a + p) > 1))

def answer(a, b, p):
    bap = b - (a + p)
    return bap >= 0 and bap != 1 and a != 1

在a = 0,b = 0,p = 1的情况下失败;填写您的方程并在问题中,它不会给出相同的布尔值。 - Tamara Wijsman
只有当bap是有符号整数时,此代码才能正常工作。否则,bap将翻转为UINT_MAX。 - Can Berk Güder
在这两种情况下,它对我来说都返回False。 - PEZ
1
是的,我刚刚在0-199的所有组合上进行了测试,这是800万次测试(有点过头了),而且它可以正常工作。 - nickf

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接