簡體   English   中英

如何減少數學調用? (JavaScript)

[英]How to minimize Math calls? (Javascript)

我正在執行Codewars挑戰 ,其中我必須創建一個參數a,b和c與對應於二次方程ax ^ 2 + bx + c = 0的函數,並求解x。 目標不僅是解決x,而且還應盡量減少浪費的Math.sqrt調用次數。 (您還必須返回具有唯一解決方案的數組)。

我想出了一個解決方案:

function solveQuadratic(a, b, c) {
  if ((4*a*c > b*b) || ((a === 0) && (b === 0))) { return undefined;}
  else if (a === 0) {return [-c/b];}
  else {
   var xVals = [];
   var sqrt = Math.sqrt(b*b - 4*a*c);
   xVals.push((-b - sqrt)/2*a);
   xVals.push((-b + sqrt)/2*a);
   if (xVals[0] === xVals[1]) {xVals.pop();}
   return xVals;
  }
}

我收到錯誤消息:

您通過6個Math.sqrt調用通過了測試。 您應該能夠通過4個Math.sqrt調用或更少調用來通過這些測試。

我認為將表達式的平方根部分的結果存儲在變量(sqrt)中可以防止多次調用它來評估表達式並將值分配給變量。 但是事實並非如此。

所以我有幾個問題:

  • 有沒有一種方法可以存儲(靜態)值,以便在您的代碼中使用它時無需重新評估它?
  • 除了它調用Math.sqrt太多之外,該解決方案還缺少其他明顯的東西嗎?

添加當c為零時的情況:

....
var sqrt = c==0?Math.abs(b):Math.sqrt(b*b - 4*a*c);
....

[編輯]

另外,要通過所有測試,請在此處進行拆分時,解決方案需要帶括號:

xVals.push((-b - sqrt)/(2*a));
xVals.push((-b + sqrt)/(2*a));

一種簡單的方法是使用備忘錄 使用閉包來保留使用的值的靜態列表,這樣就不必為已計算出的值調用Math.sqrt

var cachingSqrt = (function() {
    var inputs = {};
    return function(val) {
        if (inputs.hasOwnProperty(val)) {
            return inputs[val];
        } else {
            return inputs[val] = Math.sqrt(val);
        }
    }
})();

這個過程的概括將是

function createCachedResults(fn, scope) {
    var inputs = {};
    return function(val) {
        if (inputs.hasOwnProperty(val)) {
            return inputs[val];
        } else {
            return inputs[val] = fn.call(scope, val);
        }
    }
}

cachingSqrt  = createCachedResults(Math.sqrt, Math);

你可以像這樣使用它

var cachingSquareRoot = createCachedResults(Math.sqrt, Math);
function solveQuadratic(a, b, c) {
    if ((4*a*c > b*b) || ((a === 0) && (b === 0))) { 
        return undefined;
    }
    else if (a === 0) {
         return [-c/b];
    } else {
        var xVals = [];
        var sqrt = cachingSquareRoot(b*b - 4*a*c);
        xVals.push((-b - sqrt)/2*a);
        xVals.push((-b + sqrt)/2*a);
        if (xVals[0] === xVals[1]) { 
            xVals.pop();
        }
        return xVals;
    }
}

關鍵是在x === 0x === b^2時避免Math.sqrt(x) ,因為已知答案。 b^2 === 4ac4ac === 0 ,會發生這兩種情況,因此代碼需要將這兩種情況短路,以避免額外的Math.sqrt()調用。

因此,所有特殊情況是:

  • b^2 - 4ac < 0a === 0 && b === 0 ,答案undefined
  • a === 0 (在這種情況下,方程是線性的,不是二次方程)時,答案是-c / b
  • c === 0 ,使4ac === 0所以它只是-b / a0
  • b^2 - 4ac === 0時,答案僅為-b / (2 * a)

使用Ruud的建議和Joanvo的建議的固定版本的組合,它將僅通過以下四個Math.sqrt()調用來傳遞:

function solveQuadratic(a, b, c) {
    var delta = (b * b) - (4 * a * c), sqrt;
    if ((delta < 0) || ((a === 0) && (b === 0))) {
        return undefined;
    } else if (a === 0) {
        return [-c / b];
    } else if (c === 0) {
        return b === 0 ? [0] : [-b / a, 0];
    } else if (delta == 0) {
        return [-b / (2 * a)];
    } else {
        sqrt = Math.sqrt(delta);
        return [(-b - sqrt) / (2 * a), (-b + sqrt) / (2 * a)];
    }
}

這是一個基於上述版本的版本,並從Juan的答案中添加了緩存。 在初始標准測試中,這僅報告一個Math.sqrt()操作。

function solveQuadratic(a, b, c) {
    var delta = (b * b) - (4 * a * c), sqrt;
    if ((delta < 0) || ((a === 0) && (b === 0))) {
        return undefined;
    } else if (a === 0) {
        return [-c / b];
    } else if (c === 0) {
        return b === 0 ? [0] : [-b / a, 0];
    } else if (delta == 0) {
        return [-b / (2 * a)];
    } else {
        sqrt = sqrt2(delta);
        return [(-b - sqrt) / (2 * a), (-b + sqrt) / (2 * a)];
    }
}

var sqrt2 = (function() {
    var cache = {0:0, 1:1, 4:2, 9:3};
    return function(x) {
        if (cache.hasOwnProperty(x)) {
            return cache[x];
        } else {
            var result = Math.sqrt(x);
            cache[x] = result;
            return result;
        }
    }
})();

對於判別為零的情況,您應該添加快捷方式。

...
else if (b*b == 4*a*c) return [-b / (2*a)];
...

暫無
暫無

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

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