首頁 > web前端 > js教程 > 橋樑修復

橋樑修復

Mary-Kate Olsen
發布: 2024-12-22 04:17:09
原創
190 人瀏覽過

Bridge Repair

代碼來臨 2024 年第 7 天

第 1 部分

今年第一次遞迴

至少這就是我今天想要贏得一顆金星的方式:

  • 從完整清單開始
  • 檢查加法和乘法
  • 對於每個結果,繼續列表的其餘部分
  • 直到我超過或匹配總數

困難在於細節。

讓我們開始吧!

制定我的演算法

首先,我需要將每一行解析為數字列表:

let eqs = input.split('\n').map(line => {
  return [...line.matchAll(/\d+/g)].map(el => +el[0])
})
登入後複製
登入後複製
登入後複製

第一個元素是所需的總數。

其餘的是方程式的有序操作數。

我需要在遞歸函數中考慮到這一點。

這是我的遞迴函數:

function eqChecker(operands, amount, test) {
  if (amount > test) {
    return false
  } else if (amount == test && operands.length == 0) {
    return true
  } else if (operands.length) {
    let copy = operands.slice()
    let first = copy.shift()
    return eqChecker(copy, amount + first, test) || eqChecker(copy, amount * first, test)
  }
}
登入後複製
登入後複製
登入後複製

這是使用它的reduce:

let part1 = eqs.reduce((count, eq) => {
  if (eqChecker(eq.slice(2), eq[1], eq[0])) {
    count += eq[0]
  }
  return count
}, 0)
登入後複製
登入後複製
登入後複製

正如我所希望但從未預料到的,它為範例輸入產生了正確的答案!

它會完成處理我的拼圖輸入嗎?

如果是這樣,它會產生正確的答案嗎?

老實說我不確定......

確實如此! ! !

哇! ! !

儘管我很興奮,但我擔心下一部分要么會添加更多運算符,要么需要一些高級 CS 來使遞歸不再是可行的解決方案。

第2部分

完全出乎意料!而且難度更高

我該怎麼做?

...

幾天後...

回顧一下我的思考過程:

  • 就像在我的退貨條件中添加第三個條款一樣簡單嗎?
  • 我的第 1 部分遞歸函數是否配置正確才能成功?
  • 哦,不,透過先前的操作累積金額是否可行?
  • 我真的需要用新策略來解決這個問題嗎? 是的

考慮所有新的變化

對於這個方程式:

292: 11 6 16 20
登入後複製
登入後複製
登入後複製

給定三個運算符,這些都是可能的方程式:

11 
11+6 
11+6+16 
11+6+16+20 
11+6+16*20 
11+6+1620 
11+6*16 
11+6*16+20 
11+6*16*20 
11+6*1620 
11+616 
11*6 
11*6+16 
11*6+16+20 
11*6+16*20 
11*6+1620 
11*6*16 
11*616 
116 
116+16 
116+16+20 
116+16*20 
116+1620 
116*16 
11616 
登入後複製
登入後複製
登入後複製

也許我可以建立每個方程式的字串,並在遞歸函數中手動對其求值。

例如:
我在最外層函數呼叫中以空字串開始:

""
登入後複製
登入後複製
登入後複製

從那裡,我使用下一個數字創建三個變體:

"" + "+N"
"" + "*N"
"" + "N"
登入後複製
登入後複製
登入後複製

嗯,但這對第一個數字不起作用。

我需要用第一個數字開始我的第一個函數調用,而不是空字串:

"N"
登入後複製
登入後複製
登入後複製

同樣的事情:

"N" + "+N"
"N" + "*N"
"N" + "N"
登入後複製
登入後複製

是的,應該可以。

最後,我將獲得這些範例變體,所有這些都可以評估:

let eqs = input.split('\n').map(line => {
  return [...line.matchAll(/\d+/g)].map(el => +el[0])
})
登入後複製
登入後複製
登入後複製

跳至:我對其進行了編碼...並發現了一個更大的問題

我寫的程式碼成功產生了方程式的所有變體。

function eqChecker(operands, amount, test) {
  if (amount > test) {
    return false
  } else if (amount == test && operands.length == 0) {
    return true
  } else if (operands.length) {
    let copy = operands.slice()
    let first = copy.shift()
    return eqChecker(copy, amount + first, test) || eqChecker(copy, amount * first, test)
  }
}
登入後複製
登入後複製
登入後複製
  • i 用於沿著數字列表
  • 只有當 i 位於倒數第二個索引之前或位於倒數第二個索引時,最後一個子句才會繼續

函數取得四個值:

  1. 數字清單的副本,減去預期總數
  2. 下一個索引
  3. 由三個字串之一連接而成的方程式字串
  4. 相同的測試號碼

我使用與第 1 部分幾乎相同的簽名來呼叫該函數:

let part1 = eqs.reduce((count, eq) => {
  if (eqChecker(eq.slice(2), eq[1], eq[0])) {
    count += eq[0]
  }
  return count
}, 0)
登入後複製
登入後複製
登入後複製

差別在於我作為參數傳遞的內容:

  1. 沒有預期總金額的清單
  2. 從索引 0 開始
  3. 包含第一個數字的字串
  4. 預計總金額

好消息:

  • 它產生所有方程式變化

壞消息:

  • 它使用 PEMDAS 計算所有方程,而不是從左到右

我應該更清楚...內建的 JavaScript 求值器會預設使用正確的操作順序,而不是從左到右。

這確實給我的演算法帶來了更大的麻煩:

  • 我將不得不分解每個方程式並逐個部分評估它

嗚嗚嗚。

謝天謝地,我想我知道該怎麼做。

手動做數學

我需要 JavaScript 來計算這樣的方程式:

292: 11 6 16 20
登入後複製
登入後複製
登入後複製

依此順序:

11 
11+6 
11+6+16 
11+6+16+20 
11+6+16*20 
11+6+1620 
11+6*16 
11+6*16+20 
11+6*16*20 
11+6*1620 
11+616 
11*6 
11*6+16 
11*6+16+20 
11*6+16*20 
11*6+1620 
11*6*16 
11*616 
116 
116+16 
116+16+20 
116+16*20 
116+1620 
116*16 
11616 
登入後複製
登入後複製
登入後複製

我想將方程式分成幾個部分:

""
登入後複製
登入後複製
登入後複製

我了解的唯一方法是使用這個三鏈表達式:

"" + "+N"
"" + "*N"
"" + "N"
登入後複製
登入後複製
登入後複製

我用空格填充每個運算符,只是將其用作分隔符。

關於這個方程式部分列表的事實:

  • 它將始終包含 3 個或更多的奇數項目

如何在迭代每個操作數-運算符-操作數對的循環中利用這一事實?

這是我的想法:

  • 刪除前三項
  • 將它們作為字串連接,並將其作為數學表達式進行計算
  • 將結果重新附加到方程式清單的開頭
  • 重複直到方程式列表為空

希望它能起作用!

我在 JavaScript 工作的數學模擬器:

"N"
登入後複製
登入後複製
登入後複製

好消息:

  • 它向我顯示了預期的計算值

壞消息:

  • 我仍然沒有得到範例輸入中一個方程式的正確答案

範例答案不會錯...可以嗎?

我不斷產生的答案比預期答案少了大約 7k。

這讓我認為我的演算法沒有辨識出這個方程式是正確的:

let eqs = input.split('\n').map(line => {
  return [...line.matchAll(/\d+/g)].map(el => +el[0])
})
登入後複製
登入後複製
登入後複製

在範例輸入的解釋中,這是獲勝方程式:

function eqChecker(operands, amount, test) {
  if (amount > test) {
    return false
  } else if (amount == test && operands.length == 0) {
    return true
  } else if (operands.length) {
    let copy = operands.slice()
    let first = copy.shift()
    return eqChecker(copy, amount + first, test) || eqChecker(copy, amount * first, test)
  }
}
登入後複製
登入後複製
登入後複製

我的演算法評估該方程式並產生以下結果:

let part1 = eqs.reduce((count, eq) => {
  if (eqChecker(eq.slice(2), eq[1], eq[0])) {
    count += eq[0]
  }
  return count
}, 0)
登入後複製
登入後複製
登入後複製

那是因為我的演算法是這樣運作的:

292: 11 6 16 20
登入後複製
登入後複製
登入後複製

我不明白它怎麼可能是其他數字。

所以...我用谷歌搜尋了。

我找到了我的答案,它一如既往地隱藏在簡單的網站解釋中:

所有運算子仍然從左到右計算。

我在每個遞歸函數呼叫中預先連接值。

相反,我的演算法應該要這樣做:

11 
11+6 
11+6+16 
11+6+16+20 
11+6+16*20 
11+6+1620 
11+6*16 
11+6*16+20 
11+6*16*20 
11+6*1620 
11+616 
11*6 
11*6+16 
11*6+16+20 
11*6+16*20 
11*6+1620 
11*6*16 
11*616 
116 
116+16 
116+16+20 
116+16*20 
116+1620 
116*16 
11616 
登入後複製
登入後複製
登入後複製

現在我明白了應該發生什麼,我可以調整我的演算法以匹配該處理行為嗎?

從左到右......這次是真的

值得慶幸的是,調整我的演算法相對容易。

我新增了一個replaceAll()子句來解釋||。

我處理每三個項目的新 while 迴圈如下:

""
登入後複製
登入後複製
登入後複製

我調整了退貨聲明的||子句包含這些字符,而不是立即連接兩個數字。

測試和重新測試

我在範例輸入上運行了演算法。

終於產生了正確的答案! !

多麼輕鬆啊! !

我想知道它是否會完成運行並在我的拼圖輸入上產生正確的答案。

按運行...

...

...

我得到答案了!

它很大,所以這可能是一個好兆頭。

這是正確答案嗎?

...

不。太高了。

真糟糕。

我錯過了一個邊緣案例嗎?

我的獲勝方程式的條件很簡單,就是處理後的數學等於測試量。

但是,如果其中一個變體方程式允許數字子集產生正確答案怎麼辦?

為了捕捉並排除這種情況,我更新了 if 條件以包含另一個子句:

"" + "+N"
"" + "*N"
"" + "N"
登入後複製
登入後複製
登入後複製

這樣,只有當所有數字都處理完畢且結果數量等於測試數時,方程式才會被計算在內。

大問題:

  • 這會改變我得到的答案嗎?

再按下運作...

...

嗯,看起來確實還是一樣的答案。

哦,等等,末尾附近有兩個數字不同!

我的新答案比以前少了 80。

是否有一個以 80 為預期數量的方程式?

是的!

"N"
登入後複製
登入後複製
登入後複製

有沒有一種方法可以在不使用所有數字的情況下得到 80?

是的!

"N" + "+N"
"N" + "*N"
"N" + "N"
登入後複製
登入後複製

這是我唯一需要排除的邊緣情況嗎?

正在提交我的新答案...

正確! ! !

嗚呼! ! !

我做到了! ! !

那個。曾是。筋疲力盡。令人興奮。而且真的跑了。並且具有挑戰性。

以及我喜歡做這些謎題的所有原因。

繼續下一篇!

以上是橋樑修復的詳細內容。更多資訊請關注PHP中文網其他相關文章!

本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
作者最新文章
熱門教學
更多>
最新下載
更多>
網站特效
網站源碼
網站素材
前端模板