你能简化这个算法吗?

时间:2008-12-19 12:52:19

标签: algorithm refactoring

一个是数学家。这遍布办公室,我们希望看到谁能想出更好的优化版本。

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

编辑:所有数据均为正整数

修改:为了简单起见,更好==重构

27 个答案:

答案 0 :(得分:37)

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

答案 1 :(得分:16)

如果公式有效且来自您的业务规则,则无需简化它。编译器可能比我们更了解如何优化公式。

您应该做的唯一事情是使用反映业务逻辑的更好的变量名称。

在进行单元测试之前,请注意应用任何建议的解决方案。

答案 2 :(得分:5)

通过引入更多表示每个表达式含义的局部变量来简化重构。这对我们来说很难做到,不知道a,b和p是什么意思。

答案 3 :(得分:4)

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

编辑:好的,这不起作用,但这个确实有效:

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

答案 4 :(得分:4)

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

它可能不是最简单的,但应该是最具可读性的。

答案 5 :(得分:2)

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

另外,编写一个抛光公差树可能会帮助你优化它,你看到的所有东西都可以重复使用。

答案 6 :(得分: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))

答案 7 :(得分: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。

答案 8 :(得分:1)

jjngy在这里是对的。这是一个证据,证明他的简化公式与使用Coq Proof Assistant的原始公式相同。

Require Import Arith.
Require Import Omega.

Lemma eq : forall (a b p:nat),
(((a+p) <= b) /\ ((a = 0) \/ (a > 1)) /\ (b >= p)) /\ 
    ((b - (a + p) = 0) \/ (b - (a + p) > 1)) <-> 
((a + p <= b) /\ ~ (a= 1) /\ ~ (b - a - p = 1)).
Proof. intros; omega. Qed.

答案 9 :(得分:1)

// In one line:
return (a != 1) && ((b-a-p == 0) || (b-a-p > 1))

// Expanded for the compiler:
if(a == 1)
    return false;

int bap = b - a - p;

return (bap == 0) || (bap > 1);

如果您发布正在使用的处理器,我可以优化装配。 =]

答案 10 :(得分:1)

我为原始推导中的错误道歉。当您在重构后不打扰单元测试时会发生这种情况!

校正的推导如下,以测试程序的形式。

简短的回答是:

((a > 1) && (skeet == 0)) || ((a > 1) && (jon > 0) && (skeet < -1));

,其中

jon = (b - p)
skeet = (a - jon);

class Program
{
    static void Main(string[] args)
    {
        bool ok = true;
        for (int a = 1; a < 100; a++)
        {
            Console.Write(a.ToString());
            Console.Write("...");

            for (int b = 1; b < 100; b++)
            {
                for (int p = 1; p < 100; p++)
                {
                    bool[] results = testFunctions(a, b, p);
                    if (!allSame(results))
                    {
                        Console.WriteLine(string.Format(
                            "Fails for {0},{1},{2}", a, b, p));
                        for (int i = 1; i <= results.Length; i++)
                        {
                            Console.WriteLine(i.ToString() + ": " + 
                                results[i-1].ToString());
                        }

                        ok = false;
                        break;
                    }
                }
                if (!ok) { break; }
            }
            if (!ok) { break; }
        }
        if (ok) { Console.WriteLine("Success"); }
        else { Console.WriteLine("Failed!"); }
        Console.ReadKey();
    }

    public static bool allSame(bool[] vals)
    {
        bool firstValue = vals[0];
        for (int i = 1; i < vals.Length; i++)
        {
            if (vals[i] != firstValue)
            {
                return false;
            }
        }
        return true;
    }

    public static bool[] testFunctions(int a, int b, int p)
    {
        bool [] results = new bool[16];

        //given: all values are positive integers
        if (a<=0 || b<=0 || p<=0)
        {
            throw new Exception("All inputs must be positive integers!");
        }

        //[1] original expression
        results[0] = (((a+p) <= b) && (a == 0 || a > 1) && (b >= p)) && 
            ((b - (a + p) == 0) || (b - (a + p) > 1));

        //[2] a==0 cannot be true since a is a positive integer
        results[1] = (((a+p) <= b) && (a > 1) && (b >= p)) && 
            ((b - (a + p) == 0) || (b - (a + p) > 1));

        //[3] rewrite (b >= p) && ((a+p) <= b) 
        results[2] = (b >= p) && (b >= (a+p)) && (a > 1) && 
            ((b - (a + p) == 0) || (b - (a + p) > 1));

        //[4] since a is positive, (b>=p) guarantees (b>=(p+a)) so we 
        //can drop the latter term
        results[3] = (b >= p) && (a > 1) && 
            ((b - (a + p) == 0) || (b - (a + p) > 1));

        //[5] separate the two cases b>=p and b=p
        results[4] = ((b==p) && (a > 1) && ((b - (a + p) == 0) || 
            (b - (a + p) > 1))) || ((b > p) && (a > 1) && 
            ((b - (a + p) == 0) || (b - (a + p) > 1)));

        //[6] rewrite the first case to eliminate p (since b=p 
        //in that case)
        results[5] = ((b==p) && (a > 1) && ((-a == 0) || 
            (-a > 1))) || ((b > p) && (a > 1) && 
            (((b - a - p) == 0) || ((b - a - p) > 1)));

        //[7] since a>0, neither (-a=0) nor (-a>1) can be true, 
        //so the case when b=p is always false
        results[6] = (b > p) && (a > 1) && (((b - a - p) == 0) || 
            ((b - a - p) > 1));

        //[8] rewrite (b>p) as ((b-p)>0) and reorder the subtractions
        results[7] = ((b - p) > 0) && (a > 1) && (((b - p - a) == 0) || 
            ((b - p - a) > 1));

        //[9] define (b - p) as N temporarily
        int N = (b - p);
        results[8] = (N > 0) && (a > 1) && (((N - a) == 0) || ((N - a) > 1));

        //[10] rewrite the disjunction to isolate a
        results[9] = (N > 0) && (a > 1) && ((a == N) || (a < (N - 1)));

        //[11] expand the disjunction
        results[10] = ((N > 0) && (a > 1) && (a == N)) ||
            ((N > 0) && (a > 1) && (a < (N - 1)));

        //[12] since (a = N) in the first subexpression we can simplify to
        results[11] = ((a == N) && (a > 1)) || 
            ((N > 0) && (a > 1) && (a < (N - 1)));

        //[13] extract common term (a > 1) and replace N with (b - p)
        results[12] = (a > 1) && ((a == (b - p)) || 
            (((b - p) > 0) && (a < (b - p - 1))));

        //[14] extract common term (a > 1) and replace N with (b - p)
        results[13] = (a > 1) && (((a - b + p) == 0) || 
            (((b - p) > 0) && ((a - b + p) < -1)));

        //[15] replace redundant subterms with intermediate 
        //variables (to make Jon Skeet happy)
        int jon = (b - p);
        int skeet = (a - jon);   //(a - b + p) = (a - (b - p))
        results[14] = (a > 1) && ((skeet == 0) || 
            ((jon > 0) && (skeet < -1)));

        //[16] rewrite in disjunctive normal form
        results[15] = ((a > 1) && (skeet == 0)) || 
            ((a > 1) && (jon > 0) && (skeet < -1));

        return results;
    }
}

答案 11 :(得分:1)

使用a,b,p从0到10000进行测试:

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

我认为可以进一步简化。

答案 12 :(得分:1)

这很简单,我可以得到它。

def calc(a, b, p):
    if (a != 1):
        temp = a - b + p
        if temp == 0 or temp < -1:
            return True
    return False

它也可以写成:

def calc(a, b, p):
    temp = a - b + p
    return a != 1 and (temp == 0 or temp < -1)

或者作为:

def calc(a, b, p):
    temp = a - b + p
    return a != 1 and temp <= 0 and temp != -1

答案 13 :(得分:1)

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

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

我以前用来检查的程序:(写得很开心)

#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;
                }
}

答案 14 :(得分: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

答案 15 :(得分:0)

好吧,我希望我在这里做数学,但如果我是对的,那么这会简化很多。虽然它最终看起来不一样,但核心逻辑应该是相同的。

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

// ((a + p) <= b) iif a = 0 && p = b; therefore, b = p and a = 0 for this to work
(b == p) && ((b - (a + p) == 0) || (b - (a + p) > 1))

// Simplification, assuming that b = p and a = 0
(b == p) && (a == 0)

但是,如果我们假设零既不是positive or negative,那么这意味着提供给方程的任何值都将大于或等于1。这反过来意味着由于以下事实,等式总是评估为假:

(a == 0 || a > 1)

只有当&gt; = 2时才会评估为true;但是,如果以下情况也是如此:

(b >= p)

那意味着p至少等于b,因此:

((a + p) <= b)

通过替换成为:

((2 + b) <= b)

显然永远不会评估为真。

答案 16 :(得分:0)

我添加了这个作为对nickf答案的评论,但我认为我会提供它作为答案。好的答案似乎都是他的变种,包括我的。但是因为我们不依赖于编译器来进行优化(如果OP是,我们甚至不会这样做)然后将其从3个AND分析到下面意味着将存在这样的值,其中3个部分中只有2个需要进行评估。如果这是在脚本中完成的,那么与编译代码相反,它会产生影响。

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

基于评论,我将添加此wrt,这比AND版本更好:

我想这取决于您的真实结果数据集是否大于输入集的50%。输入越真实,我的变化就越好。因此,使用此等式,看起来AND样式会更好(至少对于我的输入数据集0-500)。

答案 17 :(得分:0)

如果a,b和p是正整数(假设正范围包括0值)那么表达式(((a + p)&lt; = b)&amp;&amp;(a == 0 || a&gt; 1)&amp;&amp;(b&gt; = p))&amp;&amp; ((b - (a + p)== 0)||(b - (a + p)> 1)) 可以减少到((a + p)&lt; = b)&amp;&amp; (a!= 1)&amp;&amp;的((B-(A + P))!= 1)

让我演示一下: 在表达式的第一部分中有一个条件,((a + p)&lt; = b), 如果估计为true则呈现true,则第二部分: ((b - (a + p)== 0)||(b - (a + p)> 1))。如果确实(b> =(a + p))那么(b - (a + p))必须大于或等于0 ,我们需要确保(b-(a + p))!= 1 。 把这个术语搁置一段时间然后继续。

现在我们可以集中精力完成第一部分 (((a + p)&lt; = b)&amp;&amp;(a == 0 || a&gt; 1)&amp;&amp;(b&gt; = p))&amp;&amp; ; ((B-(A + P))!= 1)

如果 a 为正,那么它总是> = 0,因此我们可以放弃测试(a == 0 || a&gt; 1) (a!= 1)并将表达式的第一部分减少为(((a + p)&lt; = b)&amp;&amp;(b&gt; = p)&amp;&amp;(a!= 1 ))

对于下一步减少,您可以考虑如果 b> =(a + p)那么,显然 b&gt; = p a 是正面的),表达式可以简化为

((a + p)&lt; = b)&amp;&amp; (a!= 1)&amp;&amp;的((B-(A + P))!= 1)

答案 18 :(得分:0)

b >= (a+p) && a>=1

偶数b >= p是多余的,因为a >= 1

总是如此

答案 19 :(得分:0)

关于以下逻辑如何,请评论它:

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

答案 20 :(得分:0)

  <(>(((a + p)&lt; = b)&amp;&amp;(a == 0 || a&gt; 1)&amp;&amp;   (b> = p))&amp;&amp; ((b - (a + p)== 0)   || (b - (a + p)> 1))

1)(a == 0 || a> 1)是(a!= 1)

2)(b> = p)是(b-p> = 0)

(a + p <= b)是(b-p> = a),其强于(b-p> = 0)。

第一个条件减少为(a!= 1)&amp;&amp; (b - p> = a)

3)(b - (a + p)== 0)是(b - a - p == 0)是(b - p == a)。

(b - (a + p)> 1)是(b-a-p> 1)是(b-p> 1 + a)。

因为我们有(b - p&gt; = a)而我们正在使用&amp;&amp;操作,我们可以说(b - p&gt; = a)涵盖(b - p == a&amp;&amp; b - p&gt; 1 + a)。

因此,整个条件将减少到

(a!= 1&amp;&amp;(b - p&gt; = a))

存在将其进一步降低到(b> = p)的诱惑,但是这种减少将不包括禁止b = p + 1,因此(a!= 1&amp;&amp;(b-p&gt; = a))是条件。

答案 21 :(得分:0)

第一次迭代:

bool bool1 = ((a+p) <= b) && (a == 0 || a > 1) && (b >= p);
bool bool2 = (b - (a + p) == 0) || (b - (a + p) > 1);

return bool1 && bool2;

第二次迭代:

int value1 = b - (a + p);
bool bool1 = (value1 >= 0) && (a == 0 || a > 1) && (b >= p);
bool bool2 = (value1 == 0) || (value1 > 1);

return bool1 && bool2;

第三次迭代(所有肯定)

int value1 = b - (a + p);
bool bool1 = (value1 >= 0) && (a != 1) && (b >= p);
bool bool2 = (value1 == 0) || (value1 > 1);

return bool1 && bool2;

第4次迭代(所有肯定)

int value2 = b - p;
int value1 = value2 - a;
bool bool1 = (value1 >= 0) && (a != 1) && (b - p >= 0);
bool bool2 = (value1 == 0) || (value1 > 1);

return bool1 && bool2;

第5次迭代:

int value2 = b - p;
int value1 = value2 - a;
bool bool1 = (value1 >= 0) && (a != 1) && (value2 >= 0);
bool bool2 = (value1 == 0) || (value1 > 1);

return bool1 && bool2;

答案 22 :(得分:0)

这个问题在实践中已经非常舒服地回答了,但我在下面提到了一点,我还没有看到其他任何人加注。

由于我们被告知假设a> = 0,并且第一个条件确保b - (a + p)> = 0,所以括号||可以用1:

将测试转化为不等式测试

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

很容易删除支票(b&gt; = p),这会给出nickf的表达式。这几乎可以肯定是正确的实际解决方案。不幸的是,我们需要了解有关问题域的更多信息,然后才能说出这样做是否安全。

例如,如果对a,b和p的类型使用C和32位无符号整数,请考虑a = 2 ^ 31 + 7,p = 2 ^ 31 + 5,b = 13的情况。我们有一个&gt; 0,(a + p)= 12&lt; b,但b <页。 (我使用'^'表示取幂,而不是C按位xor。)

可能你的值不会接近这种溢出问题的范围,但你应该检查这个假设。如果事实证明是可能的话,请用该表达式添加注释来解释这一点,以便一些热心的未来优化者不会不小心删除(b&gt; = p)测试。

答案 23 :(得分:0)

好吧

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

Would be better writen as:
(b - (a + p) >= 0)  

Applying this to the whole string you get:

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


(a + p) <= b is the same thing as b - (a + p) >= 0

所以你可以摆脱那个离开:

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

答案 24 :(得分:0)

我觉得(a!= 1)&amp;&amp; (a + p <= b)&amp;&amp; (a + p!= b - 1)稍微清楚一些。 另一种选择是:

int t = b-p; (a!= 1&amp;&amp; a&lt; = t&amp;&amp; a!= t-1)

基本上a是0,t或介于2和t-2之间。

答案 25 :(得分:0)

a!= 1&amp;&amp; ((b == a + p)||(b - p> a + 1))

答案 26 :(得分:-1)

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

由于a> = 0(正整数),术语(a == 0 || a> 1)始终为真

if((a + p)&lt; = b)当a,b,p>> 0时,(b> = p)为真

因此((a + p)&lt; = b)&amp;&amp; (a == 0 || a&gt; 1)&amp;&amp; (b> = p))&amp;&amp; ((b - (a + p)== 0)减少到

b>=(a+p)

(b - (a + p)== 0)|| (b - (a + p)> 1)相当于b> =(a + p)

因此,整个等式减少到

**b>= (a+p)**