簡體   English   中英

你能簡化這個算法嗎?

[英]Can you simplify this algorithm?

一個是數學家。 這已經遍布辦公室,我們希望看到誰能想出更好的優化版本。

(((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);

如果公式有效且來自您的業務規則,則無需簡化它。 編譯器可能比我們更了解如何優化公式。

您應該做的唯一事情是使用反映業務邏輯的更好的變量名稱。

在進行單元測試之前,請注意應用任何建議的解決方案。

通過引入更多指示每個表達式含義的局部變量來簡化重構。 這對我們來說很難做到,不知道a,b和p是什么意思。

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

編輯:好的,這不起作用,但這個做:

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

它可能不是最簡單的,但應該是最可讀的。

我不會在那個表達式中做所有的數學運算。 例如b - (a + p)被評估兩次。 如果可能,請將它們拆分為變量。

此外,編寫一個拋光公差樹可能會幫助您優化它,您可以重復使用兩次看到的所有內容。

由於它們都是正面的,所以可以刪除很多重復:

所以作為第一步,

(((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 != 1替換(foo == 0 || foo > 1)模式

該模式在上面出現兩次,一次是foo = a,一次是foo = (b - (a+p))

由於整數是無符號的,(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。

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

這很簡單,我可以得到它。

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

用a,b,p從0到10000進行測試:

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

我認為它可以簡化得更多。

我為原始推導中的錯誤道歉。 這是重構后你不打擾單元測試時會發生的事情!

校正的推導如下,以測試程序的形式。

簡短的回答是:

((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;
    }
}
// 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);

如果您發布正在使用的處理器,我可以優化組裝。 =]

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.

((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)) 

第一次迭代:

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;

第五次迭代:

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;

好吧,我希望我在這里做數學,但如果我是對的,那么這簡化了很多。 雖然它最終看起來不一樣,但核心邏輯應該是相同的。

// 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)

但是,如果我們假設零既不是正數也不是負數,那么這意味着提供給方程的任何值都將大於或等於1。 這反過來意味着由於以下事實,等式總是會評估為假:

(a == 0 || a > 1)

只有當> = 2時才會評估為真; 但是,如果以下情況也是如此:

(b >= p)

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

((a + p) <= b)

通過替換成為:

((2 + b) <= b)

這顯然永遠不會評估為真。

我添加了這個作為對nickf答案的評論,但我認為我會提供它作為答案。 好的答案似乎都是他的變種,包括我的。 但是因為我們不依賴於編譯器來進行優化(如果OP是,我們甚至不會這樣做)然后將其從3個AND分析到下面意味着將存在這樣的值,其中3個部分中只有2個需要進行評估。 如果這是在腳本中完成的,那么與編譯代碼相反,它會產生影響。

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

基於評論,我將添加此wrt這比AND版本更好:

我想這取決於您的真實結果數據集是否大於輸入集的50%。 輸入越真實,我的變化就越好。 因此,使用此等式,看起來AND樣式會更好(至少對於我的輸入數據集0-500)。

如果a,b和p是正整數(假設正范圍包括0值)那么表達式(((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)

讓我演示一下:在表達式的第一部分有一個條件, ((a + p)<= b) ,如果估計為真,則渲染為真第二部分: ((b - (a + p)== 0 )||(b - (a + p)> 1)) 如果(b> =(a + p))然后(b - (a + p))必須大於或等於0 ,我們需要確保(b-(a + p))!= 1 把這個術語擱置一段時間然后繼續。

現在我們可以集中精力在第一部分(((a + p)<= b)&&(a == 0 || a> 1)&&(b> = p)) &&((b-(a +) p))!= 1)

如果a為正,那么它總是> = 0,因此我們可以放棄測試(a == 0 || a> 1),如果贊成(a!= 1)並將表達式的第一部分減少為(((a) + p)<= b)&&(b> = p)&&(a!= 1))

對於下一步的減少,你可以考慮如果b> =(a + p)那么,顯然b> = pa是正的)並且表達式可以減少到

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

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

即使b >= p也是多余的,因為a >= 1總是如此

關於以下邏輯如何,請評論它:

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

(((a + p)<= b)&&(a == 0 || a> 1)&&(b> = p))&&((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)&&(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> = a)並且我們正在使用&&操作,我們可以說(b - p> = a)涵蓋(b - p == a && b - p> 1 + a)。

因此,整個條件將減少到

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

有一種誘惑可以將它進一步降低到(b> = p),但這種減少不會涵蓋禁止b = p + 1,因此(a!= 1 &&(b - p> = a))就是條件。

這個問題在實踐中已經非常舒服地回答了,但是我在下面提到了一點,我還沒有看到其他人提出過。

因為我們被告知假設a = = 0,並且第一個條件確保b - (a + p)> = 0,所以括號|| 測試可以轉化為針對不平等的測試1:

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

很容易刪除支票(b> = p),這會給出nickf的表達式。 這幾乎可以肯定是正確的實際解決方案。 不幸的是,我們需要了解有關問題域的更多信息,然后才能說出這樣做是否安全。

例如,如果對a,b和p的類型使用C和32位無符號整數,請考慮a = 2 ^ 31 + 7,p = 2 ^ 31 + 5,b = 13的情況。我們有一個> 0,(a + p)= 12 <b,但b <p。 (我使用'^'表示取冪,而不是C bitwise xor。)

可能你的值不會接近這種溢出問題的那種范圍,但你應該檢查這個假設。 如果事實證明是可能的話,請用該表達式添加注釋來解釋這一點,這樣一些熱心的未來優化者不會隨意刪除(b> = p)測試。

我覺得(a!= 1)&&(a + p <= b)&&(a + p!= b - 1)稍微清楚一點。 另一種選擇是:

int t = bp; (a!= 1 && a <= t && a!= t-1)

基本上a是0,t或介於2和t-2之間(包括2和t-2)。

a!= 1 &&((b == a + p)||(b - p> a + 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)<= b)當a,b,p> = 0時,(b> = p)為真

因此((a + p)<= b)&&(a == 0 || a> 1)&&(b> = p))&&((b - (a + p)== 0)減少到

b>=(a+p)

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

因此整個方程式減少到

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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM