Lichen5221 / Report-Daily

記錄每日上課內容與作業。
0 stars 0 forks source link

2021-04-30 #11

Open Lichen5221 opened 3 years ago

Lichen5221 commented 3 years ago

寫程式最後一步:看清題目

弄清題意,翻成白話文。

考驗程式能力前,先考驗閱讀能力。

LOIJ 1008:幾個水桶

最重的小事:輸入範圍

因為需求不同,範圍會改變。不同範圍有不同限制。

基本限制:空間、時間、型態。

空間限制:

int: 4 bytes double: 8 bytes JS 中 的 Number : 8 bytes 一百萬個數字: (8 1e6) / 1024 = 7812 KB = 7.6 MB 十億個數字 (8 1e8) / 1024 = 781200 KB = 7600 MB = 7.4 GB

時間限制: 初學題目來說不重要,但講到 big O 就很重要。

寫出基本,才能增加效率。連基本都沒有,就不用講求效率了。

型態限制:

  1. int:-2147483648 ~ 2147483647
  2. JS 數字:Number.MAX_SAFE_INTEGER //有個安全範圍
  3. 浮點數精準度問題

LIOJ 1004 聯誼順序比大小

!解題前一定要先搞清楚範圍!

輸入與輸出方式

輸出方式:

  1. 函式
  2. 標準輸出入

Project 3 介紹

用白話文改寫題目,提出解題中有可能碰到的問題或需要注意的地方。

LIOJ 1010:靈魂伴侶

給定兩個數字 n ,介於 1 - 2147483647 之間,相同的話輸出 yes,不同的話輸出 no 。

即判斷兩數是否相同,範圍 OK 。

LIOJ 1015:音速小子

給定數字 n , 介於 1 - 100000 之間,求出其 * 340 的結果。

即簡單運算,範圍 OK。

LIOJ 1017:貪婪的小偷

我的理解:最大值取 C 次。

老師講解:取價值最高的 C 項物品,範圍 OK。

Lichen5221 commented 3 years ago

主角總是最後才登場:寫程式

從虛擬碼到程式碼

陣列總和虛擬碼:

let sum = 0
for (i from 0 to n-1) do 
  sum += arr[i]
end for
print sum

轉成程式碼:

let sum = 0
for (let i = 0; i < n; i++) { //也可以寫成 for (let i = 0; i <= n - 1; i++)
  sum += arr[i]
}
console.log(sum)

找最大值虛擬碼:

let max = arr[0]
for (i from 0 to n-1) do
  if (arr[i] > max) do
    max = arr[i]
  end if 
end for
print max

轉成程式碼:

let max = arr[0] // 設定成非空陣列
for (var i = 1; i < n; i++) {
  if (arr[i] > max) {
    max = arr[i]
  }
}
console.log(max)

注意輸入範圍,如果給予空陣列,前面需要再寫一個關於出現空陣列要做的事情。

函式填空法

避免雙層迴圈。

LIOJ 1020:判斷質數

虛擬碼試寫:

let number = n
for ( i from 1 to n ) do
  if ( i 為質數 ) do
    print 'prime'
  else do
    print 'composite'
  end if
end for

老師寫的:

for (i from 0 to n-1) do
  if (arr[i] 是質數 ) do
    print 'Prime'
  else 
    print 'Composite'
  end if
end for

差異:老師使用陣列,且從 0 開始到 n - 1 。因為是有 n 個數字,不是給定數字 n 。

翻成程式碼:

for (let i = 0; i < n; i++) {
  if (arr[i] 為質數) { // 變成:if (isPrime(arr[i]))
    console.log('Prime')
  } else {
    console.log('Composite')
  }
} 

function isPrime(n) { //不知道的地方就 function 
  if ( n === 1 ) return false // 1 當作特殊 case 處理
  for (let i = 2; i < n; i++) {
    if (n % i === 0 ) { //只要有能整除的數就不是質數
      return false
    }
  }
  return true //放在 for 迴圈外,因為放在迴圈內會變成找到一個沒整除就視為質數,但所謂質數應該是全部都不能整除(除了自己跟 1 ),所以要放在 for 迴圈外。
} 

拔掉 function 的話:

for (let i = 0; i < n; i++) {
  let isPrime = true //把函式拿掉,維持主邏輯不變。
  if (arr[i] === 1) { // edge case 也要加入
    isPrime = false
  }
  for (let j = 2; j < arr[i]; j++) { //加上判斷質數程式碼
    if (arr[i] % j === 0) {
      isPrime = false
      break
    }
  }
  if (isPrime) {
    console.log('Prime')
  } else {
    console.log('Composite')
  }
}

加 function 加爆:

for (let i = 0; i < n; i++) {
  handleNumber(arr[i])
}
function handleNumber(n) {
  if (isPrime(n)) {
    console.log('Prime')
  } else {
    console.log('Composite')
  }
}

function isPrime(n) {
  if (n === 1) return false
  for (j = 2; j < n; j++) { 
    if (n % j === 0) {
      return false
    }
  }
  return true
}
Lichen5221 commented 3 years ago

練習程式腦

在進行練習前,確保程式可以運行,但發現還要自定義陣列太累了,所以寫了一個前置 function ,只要給定 n 就好,自己有透過終端機的 node 執行確認 ok ,供參:

var n = 10
var arr = arr(n)

function arr(n){
    var ar = [ ]
    for(var i = 1; i <= n; i++) {
        ar.push(i)
    }
    return ar
}

for (let i = 0; i < n; i++) {
  let isPrime = true //把函式拿掉,維持主邏輯不變。
  if (arr[i] === 1) { // edge case 也要加入
    isPrime = false
  }
  for (let j = 2; j < arr[i]; j++) { //加上判斷質數程式碼
    if (arr[i] % j === 0) {
      isPrime = false
      break
    }
  }
  if (isPrime) {
    console.log('Prime')
  } else {
    console.log('Composite')
  }
}

正式練習程式腦:

for (let i = 0; i < n; i++) {
  let isPrime = true //把函式拿掉,維持主邏輯不變。
  if (arr[i] === 1) { // edge case 也要加入
    isPrime = false
  }
  for (let j = 2; j < arr[i]; j++) { //加上判斷質數程式碼
    if (arr[i] % j === 0) {
      isPrime = false
      break
    }
  }
  if (isPrime = true) {
    console.log('Prime')
  } else {
    console.log('Composite')
  }
}
  1. 給定 n = 5 ,陣列 arr = [1, 2, 3, 4, 5]。
  2. i 為 0 ,判定是否小於 n ,是,將 true 置於 isPrim,執行 if,arr[0] === 1,符合條件,將 false 置於(覆蓋?)isPrime ,執行 if,印出 'Composite' , i++。
  3. i 為 1 ,判定是否小於 n ,是,執行 if , arr[1] === 2 ,不符合條件,執行 for 迴圈。 j 為 2 , j 是否小於 arr[1] === 2 ,否,跳出迴圈,迴圈執行完畢, isPrime 仍為 true ,印出 'Prime' ,i++。
  4. i 為 2 ,判定是否小於 n ,是,執行 if,arr[2] === 3,不符合條件,執行 for 迴圈。 j 為 2 ,j 是否小於 arr[2] === 3 ,是,執行 if , arr[2] === 3 對 2 取餘數是否為 0 ,否,j++ 。 j 為 3 ,j 是否小於 arr[2] === 3 ,否,跳出迴圈,迴圈執行完畢, isPrime 仍為 true ,印出 'Prime' ,i++。
  5. i 為 3 ,判定是否小於 n,是,執行 if,arr[3] === 4 ,不符合條件,執行 for 迴圈。j 為 2 ,j 是否小於 arr[3] === 4 ,是,執行 if,arr[3] === 4 對 2 取餘數是否為 0,是,將 false 置於(覆蓋?)isPrime ,跳出迴圈。執行 if,印出 'Composite' , i++。
  6. i 為 4 ,判定是否小於 n,是,執行 if,arr[4] === 5,不符合條件,執行 for 迴圈。j 為 2 ,j 是否小於 arr[4] === 5,是,執行 if,arr[4] === 5 對 2 取餘數是否為 0 ,否,j++。j 為 3 ,j 是否小於 arr[4] === 5 ,是,執行 if ,arr[4] === 5 對 3 取餘數是否為 0 ,否,j++。j 為 4,j 是否小於 arr[4] === 5 ,是,執行 if ,arr[4] === 5 對 4 取餘數是否為 0,否,j++。j 為 5,j 是否小於 arr[4] === 5,否,跳出迴圈,迴圈執行完畢,isPrime 仍為 true ,執行 if ,印出 'Prime' 。
  7. i 為 5 ,判定是否小於 n ,否,跳出迴圈,迴圈執行完畢。
Lichen5221 commented 3 years ago

簡化法

難的解不出來,先寫簡化版。

LIOJ 1021:好多星星

自己試寫:

function star(n){
  for (var i = 1; i < n; i++){
    console.log('*'.repeat(i))
  }
}

star(6)

在 node 上可以執行也正確,但不知道為什麼在 LIOJ 上是錯誤答案,是因為 LIOJ 不能用 repeat 的語法嗎?

老師解法:

for (let i = 1; i <= N; i++) {
  printStar(i)
}

function printStar(n) {
  let s = ' '
  for (let i = 1; i <=n; i++) {
    s += '*'
  }
  console.log(s)
}

不是,結果是我少加一個等於⋯⋯好蠢⋯⋯但我也順便學了不用 repeat 怎麼跑 repeat 。

寫程式三寶:迴圈、函式、判斷式

內建函式使用這三個都可以解決。

實戰:LIOJ 1022:印出金字塔

規律要馬跟 n 有關,要馬跟 i 有關。

看出規律:

  1. 一共有 n 層
  2. 第 i 層會有 2i - 1 個星星 //自己看出的規律是 1 + 2*(i - 1)
  3. 星星會置中
  4. 需要 n - i 個空白 //沒有想出來,一直糾結兩邊都有空白。

老師解答:

for (let i = 1; i <= n; i++) {
  printLayer(i, n) //要把第 i 層跟全部有幾層一起傳進去
}

function printLayer(i, n) {
  let str = repeat(' ', n - i) + repeat('*', 2*i - 1) // JS 可直接加,其他程式不一定。
  console.log(str)
}

function repeat(str, n) { //輔助的函式
  let s = ' '
  for (let i = 1; i <= n; i++) {
    s += str
  }
  return s
}

實戰:印出九九乘法表

自己試寫:

for (i = 1; i <= 9; i++) {
  for (j = 1; j <= 9; j++ ){
    console.log(String(i),'*',String(j), '=', i*j)
  }
}

寫出來能成功跑出九九乘法表自己都感到訝異XD

老師解答:

function printTable(k) {
  for (let i = 1; i <= 9; i++) {
    console.log(k + '*' + i + '=' + k*i)
  }
}

for (let k = 1; k <= 9; k++) {
  printTable(k)
}

老師雙重 for 迴圈寫法:

for (let k = 1; k <= 9; k++) {
  for (let i = 1; i <= 9; i++) {
    console.log(k + '*' + i + '=' + k*i)
  }
}

差異:我還把 i j 轉成字串了XD

雙重迴圈的部分可能因為前面自己練習寫過判斷質數、第二週作業六的部分,所以九九乘法表的雙重迴圈還算好理解!

實戰:印出 1 - 100 的平方數

還以為是指把 1 - 100 的平方都印出來,原來是指 1 - 100 裡面是平方的數字(開根號為整數)。

自己試寫:

for (var i = 1; i <= 100; i++){
  if (Math.sqrt(i) % 1 === 0) {
    console.log(i)
  }
}

老師解法一:

for (let i = 1; i <= 100; i++) {
  if (isSquare(i)) {
    console.log(i)
  }
}

function isSquare(n) {
  let root = Math.floor(Math.sqrt(n))
  return root * root === n
}

差異:我覺得我的比較簡單。

老師解法二: 找出自己相乘小於 100 的數即 1 - 100 間的平方數。

let i = 1
while(i * i <= 100) {
  console.log(i * i)
  i++
}

Project 4 介紹

LIOJ 1023:印出聖誕樹

自己試寫: 把 for 迴圈拆成樹葉和樹幹。

var n = 3

function repeat(str, n) {
  let s = ''
  for(let i = 1; i <= n; i++) {
    s += str
  }
  return s
}

for (var i = 1; i <= n; i++) {
  let leaves = repeat(' ', n - i) + repeat('*', 2 * i - 1)
  console.log(leaves)
}

for (var i = 1; i <= n - 1; i++) {
  let trunk = repeat(' ', n - 1) + repeat('|', 1)
  console.log(trunk)
}

LIOJ 1024:印出NM乘法表

自己試寫: 就只是把九九乘法表的 9 換成 n 跟 m。

function ms(n, m){
  for (i = 1; i <= n; i++) {
    for (j = 1; j <= m; j++ ){
      console.log(String(i),'*',String(j), '=', i*j)
    }
  }
}
ms(9, 5)

奇怪欸我自己執行 node 答案就是對的,上去 LIOJ 就不對。

莫名其妙欸就一定要用 + 連接這些東西,我就喜歡用逗號分隔不行嗎?呈現出來明明就沒差。

LIOJ 1025:水仙花數

我竟然寫出來了!

var lines = [1, 10000]
var n = lines[0]
var m = lines[1]

for (var i = n; i <= m; i++) {
  var arr = String(i).split('')
  let ans = 0
  for (var k = 0; k < arr.length; k++) {
    ans += Number(arr[k])**arr.length
  }
  if (i === ans) {
    console.log(i)
  }
}

不敢置信 XD 現在只差在怎麼搞定那個以空白分隔兩個正整數的部分,在 LIOJ上要怎麼寫才行⋯⋯

結果我還是參考老師的方式寫前面的 n m 設置。我的寫法跟老師的寫法好像都不一樣,晚點回到家再來看老師怎麼寫。