Loop
Arvin Liu
一個方便找投影片的目錄
Syllabus - 0
內容 | 快速連結 |
---|---|
前導 | Section 1 |
goto 最基礎的重複結構 | Section 2 |
Ex I - I - 可控隨機數 (介紹) | Section 3 |
Ex I - II - 可控隨機數 (解析) | Section 4 |
Ex II - 考拉茲猜想 3n+1問題 | Section 5 |
do-while 結合 goto + if 的重複結構 | Section 6 |
Ex III - I - 什麼是二進位? | Section 7 |
Ex III - II - 進位制轉換 - I | Section 8 |
To do or not to do? | Section 9 |
while 事先檢查的重複結構 | Section 10 |
Ex IV - 質數判斷 | Section 11 |
for 最重要的重複結構 | Section 12 |
break & continue 流程控制 | Section 13 |
Loop p.s. 重複結構注意事項 | Section 14 |
內容 | 快速連結 |
---|---|
Loop sp. 盤點那些奇怪的迴圈 | Section 15 |
Ex V - APCS觀念題練習 | Section 16 |
Ex VI - 第二大數字判斷 | Section 17 |
Review 複習所有重複結構 | Section 18 |
Nested loop 巢狀重複結構 - 你今晚的惡夢 | Section 19 |
Double for 最經典的雙重迴圈 | Section 20 |
Ex VII - 指數表 | Section 21 |
Double break !! 跳出雙重迴圈 | Section 22 |
Ex VIII - I - 孤單的機器人 | Section 23 |
Ex VIII - II - 孤單的機器人 (補充) | Section 24 |
Ex IX - 幾何解析小幫手 | Section 25 |
Ex X - 三角形 | Section 26 |
結語 | Section 27 |
重要章節
主要章節
這個投影片怎麼用呢?
Before Before Class - 1
- 上下左右切換投影片,用ESC可以看到整個投影片的大綱。
- 每個直排都是一個主題,右上角有編號,你可按右上角的 回第一頁。
- 正確觀看順序:一直往下,到底往右。
Esc後大概長這樣,你可以先Esc在上下左右選你要看哪頁投影片。
關於這堂課
Before Before Class - 0
- 廢圖和影片很多,希望大家可以不要這麼嚴肅:)
-
這堂課(loop)非常多地方需要思考,我也會問問題讓大家思考。
- 每個問題我都會讓大家思考的30s~1min,認真的想想看吧!
- 在接下來的課程中 (迴圈 + 進階迴圈 + 遞迴) ,
我會不時的讓大家做有計時的練習題,雖然有計時不過大家還是別太緊張(?)-
寫練習題時,如果真的不會就趁機趕快複習以前的東西,
沒寫出來回家再看也沒關係,code都有附。
-
寫練習題時,如果真的不會就趁機趕快複習以前的東西,
- 有問題不要害羞,可以直接匿名發問。
Before Class
老樣子,耍個廢起個頭吧 😎😎😎
上課前,耍個廢
Before Class - 0
你看了什麼?
Before Class - 1
派大星很靠北。再摔一次,我沒看到。-
沙山很危險。 - 你看了重複的兩段,在程式裡面可不可以重複做類似的事情呢?
- 可以,這種叫做重複結構。
剛剛影片的流程圖
耍廚廢文
Before Class - 2
- 找找看右邊流程圖的重複結構在哪
其實寫成這樣會比較簡單一點其實我沒有推Ayame
一個流程圖告訴你為什麼推ayame
goto
一個最基本的重複結構
最簡單的重複結構 - goto
goto - 0
流程圖
- 可以,這種叫做重複結構。怎麼做呢?
- 首先,我們需要一個標誌,告訴程式到時候回到哪裡。
- 再來,在你想要時間回朔的地方回朔回去。
- 像下面這樣:
- 用 表示標記點。
- 用 表示讓流程圖回到標記點。
#include <iostream>
using namespace std;
int main() {
int x;
// 標記點 (label)
start:
cin >> x;
cout << x+1 << endl;
goto start;
return 0;
}
[標誌名稱]:
goto [標誌名稱];
if-else + goto
goto - 1
一個比之前更完整的猜數字流程圖
- 你可以使用if-else配合goto做到一些讓人想不到的神奇的效果(?)
3 mins
int x = 7, guess;
cin >> guess;
if (guess > x) {
cout << "Smaller.\n";
} else if (guess == x) {
cout << "Atari!\n";
} else {
cout << "Larger.\n";
}
原本猜數字的code
if-else + goto
goto - 2
一個比之前更完整的猜數字流程圖
- 你可以使用if-else配合goto做到一些讓人想不到的神奇的效果(?)
3 mins
#include <iostream>
using namespace std;
int main() {
int x = 7, guess;
start:
cin >> guess;
if (guess > x) {
cout << "Smaller.\n";
goto start;
} else if (guess == x) {
cout << "Atari!\n";
} else {
cout << "Larger.\n";
goto start;
}
return 0;
}
猜數字 + goto 的code
小小的腦筋急轉彎
goto - 2
- 你能夠跳出時間輪迴嗎?
- 你可以在第六行或第九行加入一些code,
但不能改動其他的程式,或是在其他地方新增code。 - 有很多種解法,你可以想到多少種?
- 你可以在第六行或第九行加入一些code,
5 mins
#include <iostream>
using namespace std;
int main() {
/* you can NOT add some code here. */
start:
/* you can add some code here. */
cout << "PEKOPEKO!" << endl;
goto start;
/* you can add some code here. */
return 0;
}
start:
if (false) {
cout << "PEKOPEKO!" << endl;
goto start;
}
start:
goto end;
cout << "PEKOPEKO!" << endl;
goto start;
end:;
聰明的你可能會用if。
腦子有洞的你可能會用goto。
start:
/*
cout << "PEKOPEKO!" << endl;
goto start;
*/
你怎麼就沒想過註解呢?
start:
return 0;
cout << "PEKOPEKO!" << endl;
goto start;
性格灑脫的你可能會自行了斷。
小工商 - CTF
goto - 3
一種資安的競賽類型 - 奪旗比賽 (Capture the Flag)
- 資安比賽有很多種類型:
- CTF 奪旗大賽
- Attack & Defense 攻擊&防禦
- 剛剛你看到的這種類型在資安很常見(?)
- 就是要輸入或插入一些奇奇怪怪的東西,繞過一些東西防護機制或是正常程式之類的。
- 例如 SQL Injection 等等
- 如果有興趣歡迎之後走資安領域喔!
Exercise I - I
Controllable RNG - 可控隨機數 (介紹)
Exercise I - I - Controllable RNG 可控隨機數 - 0
171 - 前導影片
171 - 前導 - 什麼是RNG?
隨機數生成器是通過一些算法、物理訊號、環境噪音等來產生看起來似乎沒有關聯性的 數列的方法或裝置。丟硬幣、丟骰子、洗牌就是生活上常見的隨機數產生方式。
大部分計算機上的偽隨機數,並不是真正的隨機數,只是重複的周期比較大的數列,是按一定的算法和種子值生成的。
Random Number Generator
- by 維基百科
- 簡單來說,就是一種隨機產生亂數的方法。
- 為什麼電腦上都是偽RNG呢?
- 因為電腦只會跑固定的程式,它不會採集天地之靈氣後告訴你亂數。
- 那電腦的亂數都怎麼生成的呢?
-
魔法透過一些很奇怪的函式生成的,例如LFSR,Mersenne twister。
-
梅森旋轉算法
線性反饋移位暫存器
Exercise I - I - Controllable RNG 可控隨機數 - 1
171 - 前導 - 什麼是LFSR?
- 大概念就是對一個數字做一些奇奇怪怪的運算(如下面的code),就會產生出下一個奇奇怪怪的數字。(蛤?)
- 例如,初始化時如果lfsr為44257,那麼接下來呼叫產生出來的數字會幾乎無法預測。
wiki的LFSR範例圖
unsigned LFSR() {
static unsigned short lfsr = 44257;
unsigned bit = ((lfsr >> 0) ^ (lfsr >> 2) ^ (lfsr >> 3) ^ (lfsr >> 5) ) & 1;
return lfsr = (lfsr >> 1) | (bit << 15);
}
線性反饋移位暫存器
44257 -> 22128 -> 43832 -> 21916 -> 10958
-> 5479 -> 35507 -> 17753 -> 8876 -> ...
Exercise I - I - Controllable RNG 可控隨機數 - 2
171 - 前導 - 怎麼用LFSR做亂數生成?
- 以我們剛剛的code為例,如果初始化時lfsr的值為44257,那麼接下來呼叫產生出來的數字會幾乎無法預測。 (但你要是知道亂數是用LFSR生成的就另當別論)
- 那我們該怎麼利用LFSR所產生出來的數字做亂數生成呢?
- 產生0到n的亂數:
- 產生a到b的亂數:
- 知道以上這兩種作法其實就ok了。
- 正常生成亂數,其實用c/c++的rand()就可以了,不用自己寫RNG。
- 上面什麼是RNG,什麼是LFSR,聽不懂沒關係,當作聽故事就好了。
44257 -> 22128 -> 43832 -> 21916 -> 10958 -> 5479 -> 35507 -> 17753 -> 8876 -> ...
int num = LFSR() % (n + 1);
int num = LFSR() % (b - a + 1) + a;
Exercise I - I - Controllable RNG 可控隨機數 - 3
線性反饋移位暫存器
171 - 前導 - 怎麼用LFSR做亂數生成?
- 那我們該怎麼利用LFSR所產生出來的數字做亂數生成呢?
- 產生0到n的亂數:
- 產生a到b的亂數:
-
底下為一個產生範圍0~10亂數的一份完整的code,跑跑看吧!
- 你能不能讓它無窮的跑亂數呢?
- 你能不能把它改成生成1~6的亂數來模擬丟骰子呢?
int num = LFSR() % (n + 1);
int num = LFSR() % (b - a + 1) + a;
5 mins
#include <iostream>
using namespace std;
unsigned LFSR() {
static unsigned short lfsr = 44257;
unsigned bit = ((lfsr >> 0) ^ (lfsr >> 2) ^ (lfsr >> 3) ^ (lfsr >> 5) ) & 1;
return lfsr = (lfsr >> 1) | (bit << 15);
}
int main () {
int n = 10, x;
x = LFSR() % (n+1);
cout << x << " ";
x = LFSR() % (n+1);
cout << x << " ";
x = LFSR() % (n+1);
cout << x << " ";
x = LFSR() % (n+1);
cout << x << " ";
x = LFSR() % (n+1);
cout << x << " ";
// ...
}
Exercise I - I - Controllable RNG 可控隨機數 - 4
線性反饋移位暫存器
171 - 延伸 - 種子碼之所以叫種子
- 我們仔細看看LFSR,預設是44257的話,可以一直生成序列下去。
那麼其實這個44257,就稱之為PRNG的種子碼。
Exercise I - I - Controllable RNG 可控隨機數 - 5
44257 -> 22128 -> 43832 -> 21916 -> 10958 -> 5479 -> 35507 -> 17753 -> 8876 -> ...
- 例如在minecraft裡面,給它一個種子碼,它就會利用PRNG生出一個世界來。
- 同個種子碼生成同個世界,不同種子碼生成完全不同世界,即使只差1。
- 舉例來說,下面兩個世界就是不一樣的種子碼。(from 30 best Minecraft Seeds)
Exercise I - II
Controllable RNG - 可控隨機數 (解析)
Exercise I
程式練習題
接下來有一題請你用
程式實做重複結構的題目。
- 我會花1分鐘讀題。
- 總共計時15分鐘。
- 開好你的程式環境吧!
171 - 可控隨機數 - 題目
- 題目:給你一個RNG函式以及一個正整數n以及正整數k,請你生成並輸出五個隨機數,一行一個。
- 請按照LFSR的生成順序生成。
- 如果生成的數字是k的倍數,那麼這個數字我們就不要了,重新生成一個新的。
- 提示:可以照著右邊的流程圖寫。
- 小題醒:記得複製下面的code貼在main前。
15 mins
Exercise I - II - Controllable RNG 可控隨機數 - 0
unsigned LFSR() {
static unsigned short lfsr = 44257;
unsigned bit = ((lfsr >> 0) ^ (lfsr >> 2) ^ (lfsr >> 3) ^ (lfsr >> 5) ) & 1;
return lfsr = (lfsr >> 1) | (bit << 15);
}
這題的流程圖
171 - 可控隨機數 - 題目
15 mins
#include <iostream>
using namespace std;
unsigned LFSR();
int main() {
int n, k;
cin >> n >> k;
int cnt = 0;
start:
int x = LFSR() % (n+1);
if (x % k == 0)
goto start;
cout << x << endl;
cnt ++;
if (cnt != 5)
goto start;
return 0;
}
unsigned LFSR() {
static unsigned short lfsr = 44257;
unsigned bit = ((lfsr >> 0) ^ (lfsr >> 2) ^ (lfsr >> 3) ^ (lfsr >> 5) ) & 1;
return lfsr = (lfsr >> 1) | (bit << 15);
}
Exercise I - II - Controllable RNG 可控隨機數 - 1
這題的流程圖
- 題目:給你一個RNG函式以及一個正整數n以及正整數k,請你生成並輸出五個隨機數,一行一個。
- 按照流程圖一行一行跟著寫就可以囉😎😎😎
Exercise II
Collatz Conjecture - 考拉茲猜想
Exercise II
程式練習題
接下來有一題請你用
程式實做重複結構的題目。
- 我會花1分鐘讀題。
- 總共計時20分鐘。
- 開好你的程式環境吧!
170 - 考拉茲猜想 - 題目
Exercise I - Collatz Conjecture 考拉茲猜想 - 0
- 題目:輸出考拉茲猜想的數字過程。
- 例如,如果題目輸入1,請輸出
- 如果題目輸入18,請輸出
(為了方便觀看,這邊有換行,但你寫的時候只需要最後換行。) - 這次沒有流程圖了QQ,但是比較簡單!
- 小提示:你把你想做的事情直接寫出來,然後觀察哪裡重複了。
20 mins
wiki對考拉茲猜想的描述。
1 -> end
18 -> 9 -> 28 -> 14 -> 7 -> 22 -> 11
-> 34 -> 17 -> 52 -> 26 -> 13 -> 40
-> 20 -> 10 -> 5 -> 16 -> 8 -> 4
-> 2 -> 1 -> end
170 - 考拉茲猜想 - 引導
Exercise I - Collatz Conjecture 考拉茲猜想 - 1
20 mins
- 題目:請輸出考拉茲猜想的數字過程。
- 小提示:你把你想做的事情直接寫出來,然後觀察哪裡重複了。
cin >> n;
cout << n << " -> ";
if (n % 2 == 0)
n /= 2;
else
n = 3 * n + 1;
cout << n << " -> ";
if (n % 2 == 0)
n /= 2;
else
n = 3 * n + 1;
cout << n << " -> ";
if (n % 2 == 0)
n /= 2;
else
n = 3 * n + 1;
直接寫出來就是長這樣。
cin >> n;
start:
cout << n << " -> ";
if (n % 2 == 0)
n /= 2;
else
n = 3 * n + 1;
goto start;
用goto循環
170 - 考拉茲猜想 - 引導
Exercise I - Collatz Conjecture 考拉茲猜想 - 2
20 mins
- 題目:請輸出考拉茲猜想的數字過程。
- 小提示:你把你想做的事情直接寫出來,然後觀察哪裡重複了。
cin >> n;
start:
cout << n << " -> ";
if (n % 2 == 0)
n /= 2;
else
n = 3 * n + 1;
goto start;
用goto循環
cin >> n;
start:
cout << n << " -> ";
if (n % 2 == 0)
n /= 2;
else
n = 3 * n + 1;
if (n != 1)
goto start;
cout << "1 -> end" << endl;
如果n還沒到1才要回去。
170 - 考拉茲猜想 - 引導
Exercise I - Collatz Conjecture 考拉茲猜想 - 3
20 mins
- 題目:請輸出考拉茲猜想的數字過程。
- 小提示:你把你想做的事情直接寫出來,然後觀察哪裡重複了。
cin >> n;
start:
cout << n << " -> ";
if (n % 2 == 0)
n /= 2;
else
n = 3 * n + 1;
if (n != 1)
goto start;
cout << "1 -> end" << endl;
如果n還沒到1才要回去。
一開始n是1會錯QQ
cin >> n;
if (n != 1) {
start:
cout << n << " -> ";
if (n % 2 == 0)
n /= 2;
else
n = 3 * n + 1;
if (n != 1)
goto start;
}
cout << "1 -> end" << endl;
AC!
170 - 考拉茲猜想 - 解答
Exercise I - Collatz Conjecture 考拉茲猜想 - 4
#include <iostream>
using namespace std;
int main() {
int n;
cin >> n;
if (n != 1) {
start:
cout << n << " -> ";
if (n % 2 == 0)
n /= 2;
else
n = 3 * n + 1;
if (n != 1)
goto start;
}
cout << "1 -> end" << endl;
return 0;
}
20 mins
- 題目:請輸出考拉茲猜想的數字過程。
- 以下就是完整個一份code。
do-while
結合 if-else 和 goto 的重複結構
goto + if = do-while
do-while - 0
do-while流程圖
#include <iostream>
int main() {
int x;
do {
std::cin >> x;
} while (x != 0);
return 0;
}
do {
// 做事情
} while (true 或 false);
- 一個輸入到 0 就結束的程式:
-
do-while基本語法是這樣的:
- 大括號裡面做事情,結尾用一個 while 接條件+分號。
- 做到大括號判斷條件,如果是 true 就回去 { 重跑一次,如果是 false,就直接往下執行。
goto <-> do-while ?
do-while - 1
do-while流程圖
- 我們來看看goto 和 do-while 的差別:
-
goto 配合 if,是這樣子的:
如果條件成立,跳回之前重來一次。
-
do-while 是這樣子的:
如果條件成立,跳回之前重來一次。
- 好像一樣?就真的一樣。
-
goto 配合 if,是這樣子的:
do {
// 做事情
} while (true 或 false);
start:
// 做事情
if (true 或 false)
goto start;
goto -> do-while
do-while - 2
#include <iostream>
using namespace std;
int main() {
int n;
cin >> n;
if (n != 1) {
start:
cout << n << " -> ";
if (n % 2 == 0)
n /= 2;
else
n = 3 * n + 1;
if (n != 1)
goto start;
}
cout << "1 -> end" << endl;
return 0;
}
#include <iostream>
using namespace std;
int main() {
int n;
cin >> n;
if (n != 1) {
do {
cout << n << " -> ";
if (n % 2 == 0)
n /= 2;
else
n = 3 * n + 1;
} while(n != 1);
}
cout << "1 -> end" << endl;
return 0;
}
- 把goto轉成do-while!
- 為甚麼要用do-while寫呢?因為看起來比較簡單!
goto + if (剛剛的code)
do-while (改寫的code)
3 mins
其實,平常最好不要用goto...
do-while - 3
- 你的code會看起來很跳來跳去的。😖
- 這種混雜混雜的code,就稱speghetti code (麵條式代碼)
- BTW,有個非常特別的比賽叫做IOCCC (國際C語言混亂代碼大賽),右邊這個是某年競賽的產物(?)
/*[*/#include<stdio.h>//
#include<stdlib.h>//]++++[->++[->+>++++<<]<][(c)2013]
#ifndef e//[o
#include<string.h>//]![misaka.c,size=3808,crc=d0ec3b36][
#define e 0x1//
typedef struct{int d,b,o,P;char*q,*p;}f;int p,q,d,b,_=0//|
#include __FILE__//]>>>[->+>++<<]<[-<<+>>>++<]>>+MISAKA*IMOUTO
#undef e//[->[-<<+<+<+>>>>]<<<<<++[->>+>>>+<<<<<]>+>+++>+++[>]]b
#define e(c)/**/if((_!=__LINE__?(_=__LINE__):0)){c;}//[20002,+[-.+]
,O,i=0,Q=sizeof(f);static f*P;static FILE*t;static const char*o[]={//
"\n\40\"8oCan\40not\40open %s\n\0aaFbfeccdeaEbgecbbcda6bcedd#e(bbed$bbd",
"a6bgcdbbccd#ead$c%bcdea7bccde*b$eebbdda9bsdbeccdbbecdcbbcceed#eaa&bae$cbe",
"e&cbdd$eldbdeedbbdede)bdcdea&bbde1bedbbcc&b#ccdee&bdcdea'bbcd)e'bad(bae&bccd",
"e&bbda1bdcdee$bbce#b$c&bdedcd%ecdca4bhcdeebbcd#e$b#ecdcc$bccda7bbcc#e#d%c*bbda",
">bad/bbda"};static int S(){return(o[p][q]);}static/**/int/**/Z=0 ;void/**/z(int//
l){if(/**/Z-l){Z=l;q++;if(p<b*5&&!S()){p+=b;q=0;}}}int main(int I, /**/char**l){//
d=sizeof(f*);if(1<(O=_)){b=((sizeof(o)/sizeof(char*))-1)/4;q=22; p= 0;while(p<b*5){
/*<*/if(Z-1){d=S()>96;i=S()-(d?96:32) ;q++;if(p<b*5&&!S()){p+=b; q= 0;}Z=1;}/*[[*/
while(i){_=o[0][S()-97];I=_-10?b:1; for( ;I--;)putchar(_ );if (! --i||d)z(~i );}
if(p==b*5&&O){p-=b;O--;}}return 0U; }if(! (P=( f*)calloc /*]*/ (Q ,I)))return 1;
{;}for(_=p=1;p<I;p++){e(q=1);while (q< p&& strcmp( l[p ] ,l[(q)]))++ q;
t=stdin;if(q<p){(void)memcpy/* " */ (&P [p],&P [q ] ,Q);continue ;}
if(strcmp(l[p],"-")){t=fopen(l [ p] ,"rb" ) ;if(!t ){{;} ;
printf(05+*o,l[p ]);return+1; {;} }}_=b= 1<<16 ;
*&O=5;do{if(!(P[p].q=realloc (P[p].q,(P[p].P += b)+1))){return 01;}O &=72 /
6/*][*/;P[p].o+=d=fread(P[p] .q +P[ p ]. o, 1,b,t) ;}//
while(d==b) ;P [p].q[ P[ p] .o ]= 012;d =0;
e(fclose(t ) );P [p] .p =P[ p] .q;if (O)
{for(;d<P[ p] .o ;d= q+ 1) {q= d;
while(q<P[ p].o&&P[ p].q[q]- 10 ){
q++;}b=q-d; _=P [p]. d ;
if(b>_){/*]b */
P[p].d=b;}{; }
#undef/*pqdz'.*/ e// ;
#define/*s8qdb]*/e/**/0 //
//<<.<<.----.>.<<.>++.++< .[>]
/*P[*/P[p].b++;continue;}}}t= stdout;
for (p=1;p<I;p++){/**/if(P[p].b>i ){i=P[p].b;}}
if (O){for(p=0;p<i;p++){q=0;/*[*/while(I >++q){_=P[q].p-P[q ].q;
b= 0;if(_<P[q ].o){while(012-*P[q].p) {putchar(*(P[q].p++));b++;}P[q]. p++;
} ;while (P[ q].d>b++)putchar(040);} putchar(10);}return 0;}p =1;
for(; p<I ;p++)fwrite(P[p] .q,P[ p].o,1,t);return 0 ;}//
#/*] ]<. [-]<[-]<[- ]<[ -]< [- ]<;*/elif e //b
|(1 << ( __LINE__ /* >> `*//45)) | 01U
# /* */ endif //
這份code可以讓多個文件並排輸出。
你可以複製上面的這份code編譯看看(?)
(要把文件存成.c 不能是.cpp)
跳來跳去好難看啊....
Exercise III - I
介紹二進位制
172 - 什麼是二進位制?
Exercise III - I - Dec2bin 進位置轉換 - 0
15 mins
- 你會數數嗎?
172 - 什麼是二進位制?
Exercise III - I - Dec2bin 進位置轉換 - 1
- 你會數(十進位制的)數嗎?
- 數到十的時候會進位。
- 那你會數(二進位制的)數嗎?
- 數到二的時候會進位。
1
2
3
4
...
9
10
5
6
1
10
11
100
101
110
172 - 十進位轉二進位?
Exercise III - I - Dec2bin 進位置轉換 - 2
1
2
3
4
5
6
1
10
11
100
101
110
十進位制
二進位制
看的出來
規律嗎?
172 - 十進位轉二進位?
Exercise III - I - Dec2bin 進位置轉換 - 3
1
2
3
4
5
6
001
010
011
100
101
110
172 - 十進位轉二進位?
Exercise III - I - Dec2bin 進位置轉換 - 4
- 為了要得到2的冪次方的系數,我們會用
右邊這個除法來得到。- 如何做呢?我們以右圖的13做為舉例。
- 13 % 2 = 1,這表示
- 13 / 2 = 6
- 6 % 2 = 0,這表示
- 6 / 2 = 3
- 3 % 2 = 1,這表示
- 3 / 2 = 1
- 1 % 2 = 1,這表示
- 1 / 2 = 0
- 如何做呢?我們以右圖的13做為舉例。
一個將13轉成1101的過程
Exercise III - II
進位制轉換 - I
Exercise III
程式練習題
接下來有一題請你用
程式實做重複結構的題目。
- 我會花10秒讀題。
- 總共計時15分鐘。
- 開好你的程式環境吧!
172 - 進位制轉換 - 題目
- 題目:給你一個十進位數字 (範圍從 0 到 2^60 次方),
請你將這個數字轉成二進位後倒著印。 - 小提示:像考拉茲猜想一樣,想辦法把重複結構用do-while做出來。
15 mins
Exercise III - II - Dec2bin 進位置轉換 - 0
172 - 進位制轉換 - 解答
#include <iostream>
using namespace std;
int main () {
long long x;
cin >> x;
do {
cout << x % 2;
x /= 2;
} while (x != 0);
cout << endl;
return 0;
}
Exercise III - II - Dec2bin 進位置轉換 - 1
15 mins
- 題目:給你一個十進位數字 (範圍從 0 到 2^60 次方),
請你將這個數字轉成二進位後倒著印。 - 小提示:像考拉茲猜想一樣,想辦法把重複結構用do-while做出來。
- 記得到0跳出。
To do or not to do?
先做判斷 vs 後做判斷?
小小複習 - 語法
To do or not to do ? - 0
-
再繼續往下之前,我們來複習一下吧!
-
goto:跳轉到你曾經設下的標記點。
-
do{}while();:做{},判斷()決定要不要跳轉到一開始。
-
do-while流程圖
goto流程圖
// 設下標記點
labelA:
do_something();
// 跳到標記點
goto labelA;
// =======================
do {
do_something();
// 如果true會跳到do{那行
} while (true 或 false);
小小複習 - 題目
To do or not to do ? - 1
-
再繼續往下之前,我們來複習一下吧!
-
循環的猜數字 (goto)
-
猜數字後判斷有沒有猜中,沒有回去再猜一次。
-
-
可控隨機數 (看著流程圖用goto實作)
-
生亂數後判斷有沒有違反條件或還沒生五次,以此決定要不要回去重生亂數。
-
-
考拉茲猜想 (goto -> do-while)
-
判斷已經是不是到1了,還沒就/2或*3+1並回去重做一次。
-
-
進位制轉換 (do-while)
-
輸出第一位數後 /2,判斷是否到0了,沒有就回去重做一次。
-
-
做動作
做判斷
跳code
想想看之前的題目...
To do or not to do ? - 2
-
有些情況,是需要先做事再判斷要不要跳回去的。
-
例如進位制轉換,亂數生成。
-
-
但有些情況是需要先判斷再做事,做完再重新判斷決定要不要跳回去。
-
考拉茲猜想要先判斷是不是1,做完之後還要重新判斷是不是1,好麻煩。
- 有沒有更簡單的語法可以簡單做這件事情呢?
-
#include <iostream>
using namespace std;
int main() {
int n;
cin >> n;
if (n != 1) {
do {
cout << n << " -> ";
if (n % 2 == 0)
n /= 2;
else
n = 3 * n + 1;
} while(n != 1);
}
cout << "1 -> end" << endl;
return 0;
}
用do-while實作考拉茲猜想。
做動作
做判斷
跳code
while
先判斷再做事的重複結構
先判斷條件的迴圈 - while
while - 0
while流程圖
// while 一開始,如果判斷為F則跳到}後
while (T / F) {
// 做事情
} // 結束會回到while(){重新判斷
-
while就是一開始有先檢查的do-while。
-
do-while 一定會做第一次。
- 到}while(T/F);決定要不要回去。
-
while 不一定,一開始就會檢查。
- 到}會直接回while(T/F){判斷。
- 如果為T,就繼續往下做。
- 如果為F,會直接跳到while(){}之後。
- while結尾不用分號,但do-while要。
- 到}會直接回while(T/F){判斷。
-
do-while 一定會做第一次。
do-while流程圖
先判斷條件的迴圈 - while
while - 1
- while 可以把事前檢查的 if 和 do-while 合併。
#include <iostream>
using namespace std;
int main() {
int n;
cin >> n;
if (n != 1) {
do {
cout << n << " -> ";
if (n % 2 == 0)
n /= 2;
else
n = 3 * n + 1;
} while(n != 1);
}
cout << "1 -> end" << endl;
return 0;
}
#include <iostream>
using namespace std;
int main() {
int n;
cin >> n;
while (n != 1) {
cout << n << " -> ";
if (n % 2 == 0)
n /= 2;
else
n = 3 * n + 1;
}
cout << "1 -> end" << endl;
return 0;
}
考拉茲猜想用whlie實作的code。
一個用 while 的小例子 - 1+...+n
while - 2
- 如何算出 呢? (就是+1+2+...+n的總和)
- 嚴格上來說,你可以用 ,但先不要,
試著用重複結構來寫寫看。
1+2+3+4+5+6+....+n為止
int sum = 0;
sum += 1;
sum += 2;
sum += 3;
sum += 4;
.... 加到n為止。
你可能會想這樣想
但這個東西不是重複結構啊?
code不一樣不能重複QQ
int sum = 0, i = 1;
sum += i;
i += 1;
sum += i;
i += 1;
sum += i;
i += 1;
.... 加到i=n為止。
耶?怎麼突然可以了?
- 觀察規律,想辦法讓他變重複結構,這就是迴圈最重要的技巧,也是迴圈難的地方。
一個用 while 的小例子 - 1+...+n
while - 2
int sum = 0, i = 1;
do {
sum += i;
i += 1;
} while (i != n+1);
int sum = 0, i = 1;
while (i != n+1) {
sum += i;
i += 1;
}
- 如何算出 呢? (就是+1+2+...+n的總和)
- 觀察規律,想辦法讓他變重複結構,這就是迴圈最重要的技巧,也是迴圈難的地方。
int sum = 0, i = 1;
sum += i;
i += 1;
sum += i;
i += 1;
sum += i;
i += 1;
.... 加到i=n為止。
耶?怎麼突然可以了?
do-while的寫法
while的寫法
但n=0呢?
Exercise IV
isPrime - 質數判斷
Exercise IV
程式練習題
接下來有一題請你用
程式實做重複結構的題目。
- 我會花1分鐘讀題。
- 我會花1分鐘教你開平方根。
- 總共計時20分鐘。
- 第10分鐘會給個提示。
- 開好你的程式環境吧!
173 - 質數驗證 - 題目
Exercise IV - isPrime - 質數判斷 - 0
- 題目:給你一個數字,判斷其是否為質數。是輸出"Yes",否輸出"No"。(皆需換行)
20 mins
數字範圍:
173 - 質數驗證 - How2
Exercise IV - isPrime - 質數判斷 - 1
- 題目:給你一個數字,判斷其是否為質數。是輸出"Yes",否輸出"No"。(皆需換行)
20 mins
#include <iostream>
#include <cmath>
using namespace std;
int main() {
long long x;
cin >> x;
cout << sqrt(x) << endl;
return 0;
}
- 如何對一個數字開平方根呢?
- 在最前面加上 #include <cmath>
- 使用sqrt(變數),就可以得到該變數的平方根了! 簡單不?
- 為什麼要用到平方根來加速?你怎麼不問問你自己?
輸入一個數字,輸出平方根的小小程式例子
數字範圍:
173 - 質數驗證 - 小提示
Exercise IV - isPrime - 質數判斷 - 2
- 題目:給你一個數字,判斷其是否為質數。是輸出"Yes",否輸出"No"。(皆需換行)
20 mins
- 結合剛剛的 1 + 2 + 3 + ... + n 的概念,我們可以寫出一個程式
輸出 x % 2 , x % 3,x % 4 直到不超過x的平方根(sqrt(x))為止。
#include <iostream>
#include <cmath>
using namespace std;
int main() {
long long i = 2, x;
cin >> x;
while (i <= sqrt(x)) {
cout << (x % i) << " ";
i += 1;
}
return 0;
}
小小提示
數字範圍:
173 - 質數驗證 - 答案
Exercise IV - isPrime - 質數判斷 - 3
- 題目:給你一個數字,判斷其是否為質數。是輸出"Yes",否輸出"No"。(皆需換行)
20 mins
#include <iostream>
#include <cmath>
using namespace std;
int main() {
long long i = 2, x;
cin >> x;
bool is_prime = (x != 1);
while (i <= sqrt(x)) {
if (x % i == 0)
is_prime = false;
i += 1;
}
cout << (is_prime ? "Yes" : "No") << endl;
return 0;
}
- 外面多設一個布林值來判斷到底是不是就可以了。 (記得1要特判)
質數驗證的解答code
數字範圍:
173 - 質數驗證 - 接續
Exercise IV - isPrime - 質數判斷 - 4
- 我們仔細想想看1+2+...+n還有質數驗證的程式:
- 我們發現其實一直有個變數 i 在控制著迴圈的次數。
- 但是它散落在while的上面跟下面:(,有沒有辦法可以讓程式變的好看一點呢?
20 mins
long long i = 2, x;
cin >> x;
bool is_prime = (x != 1);
while (i <= sqrt(x)) {
if (x % i == 0)
is_prime = false;
i += 1;
}
cout << (is_prime ? "Yes" : "No") << endl;
質數驗證的解答code
int sum = 0, i = 1;
while (i != n+1) {
sum += i;
i += 1;
}
數字加總的code
for
一個被整合的迴圈語法
整合起來的迴圈 - for
for - 0
for 流程圖
- 我們稍微來回去看 1 + 2 + ... + n 的例子:
- 我們發現很多時候都需要依賴某變數控制迴圈,為了讓程式變的更看的懂一點,程式猿們就做出了for的語法。
- 這樣一來,你就可以很明顯知道i是控制迴圈的。
可讀性upup!
- 這樣一來,你就可以很明顯知道i是控制迴圈的。
int sum = 0, i = 1;
while (i != n+1) {
sum += i;
i += 1;
}
int sum = 0;
for (int i=1; i != n+1; i+=1) {
sum += i;
}
整合起來的迴圈 - for
for 流程圖
- 我們稍微來回去看 1 + 2 + ... + n 的例子:
- for迴圈的語法總共分三個區域,用分號切割。
- 你可以想像for迴圈就是while的進階版:
- 初始設置:在一開始設置控制迴圈變數。
- 條件判斷:這裡跟while一模一樣。
- 控制運算:做完事情後,操作一次控制迴圈的變數。
- 你可以想像for迴圈就是while的進階版:
int sum = 0, i = 1;
while (i != n+1) {
sum += i;
i += 1;
}
int sum = 0;
for (int i=1; i != n+1; i+=1) {
sum += i;
}
for (初始設置; True / False; 控制運算) {
// 做事情
}
for - 1
整合起來的迴圈 - for
for 流程圖
- 我們來模擬看看如果 n = 2 ,for的過程:
int sum = 0;
for (int i=1; i != n+1; i+=1) {
sum += i;
}
for - 2
- 初始程式: int i = 1
- 判斷條件: i != n+1 (True,因為 1 != 3)
- sum += i (sum從0變成1)
- 控制運算: i+=1 (i變成2)
- 判斷條件: i != n+1 (True,因為 2 != 3)
- sum += i (sum從1變成3)
- 控制運算: i+=1 (i變成3)
- 判斷條件: i != n+1 (False,因為 3==3)
- 結束程式
for 常用的方法
- 變數 i 從 a 跑到 b。 (不包含 b)。
for - 3
for 流程圖
- 變數 i 從 a 跑到 b。 (包含 b)。
for (int i=a; i<=b; i++) { }
- 變數 i 從 1 一直 * 2,直到超過 b 為止。
for (int i=1; i<=b; i*=2) { }
* i++ 和 ++i 和 i+=1; 和 i=i+1 在只有單獨出現的時候沒有什麼不同。
- 變數 i 從 0 跑到 n。 (總共跑n次)。
for (int i=a; i<b; i++) { }
變數 i 轉變的過程
for (int i=0; i<n; i++) { }
使用時機 / code
173 - 質數驗證 - 接續
long long i = 2, x;
cin >> x;
bool is_prime = (x != 1);
while (i <= sqrt(x)) {
if (x % i == 0)
is_prime = false;
i += 1;
}
cout << (is_prime ? "Yes" : "No") << endl;
質數驗證的解答code (while)
- 題目:給你一個數字,判斷其是否為質數。是輸出"Yes",否輸出"No"。(皆需換行)
- 你能夠改寫成 for 迴圈嗎?
- 仔細想想,這份code完美嗎?
- 你不覺得只要知道不是質數就可以不用判斷了嗎?
數字範圍:
long long x;
cin >> x;
bool is_prime = (x != 1);
for (int i=2; i <= sqrt(x); i++) {
if (x % i == 0)
is_prime = false;
}
cout << (is_prime ? "Yes" : "No") << endl;
質數驗證的解答code (for)
for - 4
5 mins
173 - 質數驗證 - 更快的答案
5 mins
long long x;
cin >> x;
bool is_prime = x != 1;
for (int i=2; is_prime && i<=sqrt(x); i++) {
if (x % i == 0)
is_prime = false;
i += 1;
}
cout << (is_prime ? "Yes" : "No") << endl;
- 其實如果is_prime是false,就可以不用跑了,這樣速度會變得更快。
-
在while多附加上條件就好了!
-
在while多附加上條件就好了!
- 那有沒有方法可以直接跳出去呢?
- 還真的有:那就是利用goto。
(但我們不建議。) - 有沒有比較不暴力的方法?
- 也有,叫做break;
- 接著讓我們直接講break吧!
- 還真的有:那就是利用goto。
使用變數判斷提早跳出迴圈。
long long x;
cin >> x;
bool is_prime = x != 1;
for (int i=2; i<=sqrt(x); i++) {
if (x % i == 0) {
is_prime = false;
goto out;
}
i += 1;
}
out:
cout << (is_prime ? "Yes" : "No") << endl;
使用goto提早跳出迴圈。
for - 5
break & continue
迴圈的流程控制
跳脫迴圈 - break
break & continue - 0
while + break
for + break
-
break;
-
加上這樣一行,
就可以跳出一層迴圈。 - 總之就是跳到迴圈的}後面。
-
加上這樣一行,
long long x;
cin >> x;
bool is_prime = x != 1;
for (int i=2; i<=sqrt(x); i++) {
if (x % i == 0) {
is_prime = false;
break;
}
i += 1;
}
cout << (is_prime ? "Yes" : "No");
cout << endl;
強制重來 - continue
break & continue - 1
whlie + continue
for + continue
-
continue;
-
加上這樣一行,
就可以直接重來這個迴圈。 - for + continue會回去多做控制運算。
-
加上這樣一行,
long long x;
cin >> x;
bool is_prime = x != 1;
for (int i=1; i<=sqrt(x); i++) {
if (i == 1)
continue;
if (x % i == 0) {
is_prime = false;
break;
}
i += 1;
}
cout << (is_prime ? "Yes" : "No");
cout << endl;
break & continue 的實驗
break & continue - 2
- break; 強制跳出這層迴圈。
- continue; 直接重來這個迴圈。
- 舉例來說,下面這份code會跑什麼呢?
#include <iostream>
using namespace std;
int main () {
for (int i=0; i<10; i++) {
if (i % 2 == 0)
continue;
if (i == 7)
break;
cout << i << " ";
}
}
1 3 5
for + break
for + continue
2 mins
Loop p.s.
一些需要注意的細節
p.s. 0 - 大括號省略問題
Loop p.s. - 0
- 我們來看以前的在講if-else的大括號省略。
- 其實for / while迴圈的大括號都是可以省略的,
不過一樣只能作用接下來的一行code或者一個結構 (選擇/重複都可以)。
p.s. 1 - for的三個區塊都必要嗎?
Loop p.s. - 1
- 事實上,for迴圈的三個區塊都可以省略。
- 初始程式:省略就是什麼都不做。
- 條件判斷:省略的話,預設為True。
- 控制運算:省略就是什麼都不做。
- 如果你打算要做無窮迴圈,你可能會用while這樣寫。
- 但其實你可以用for這樣寫。
比上面省下1個字(?)
while (1) {
}
for 流程圖
for (;;) {
}
p.s. 2 - 多變數控制迴圈
Loop p.s. - 2
- 控制迴圈的變數都寫一個i而已,可不可以有多個變數呢?
- 可以!用逗號隔開就好。
- 例如以下的 code,利用變數 i 和變數 j 一起控制迴圈。
- 如你所見,第一個跟第三個區段都可以用 , 來同時做事。
-
如果想同時宣告不同型態呢?
- 基本來說,沒有(簡單的)辦法 :(
-
如果想同時宣告不同型態呢?
for (int i = 0, j = 1; i < n && j <= m; i++, j*=2) {
// 做事情
}
p.s. 3 - 變數生命週期
Loop p.s. - 3
- 變數都有他的生命週期。
- 在大括號裡面宣告的變數,就只能活在大括號裡面。
- 這個裡面宣告的變數跟外面的變數是完全獨立,
是完全不一樣的個體。 - for / if / while / 甚至是main的大括號都一樣。
- 像右邊這份code,會輸出什麼呢?
variable scope
#include <iostream>
using namespace std;
int main () {
int i = 7;
if (true) {
cout << i;
int i = 5;
cout << i;
}
cout << i;
}
757
- 第6行的cout:
- 因為還沒看到第7行,所以這個i是指第4行的i。
- 第8行的cout:
- 這個i就是大括號裡面第7行的 int i。
- 第10行的cout:
- 因為變數生命週期,他看不到第7行的 int i,
所以是輸出第4行的 i。
- 因為變數生命週期,他看不到第7行的 int i,
一份看起來很混亂,實際上也很混亂的code
p.s. 3 - 變數生命週期
Loop p.s. - 4
- 看看右邊的這份code,會印出什麼呢?
variable scope
為什麼呢?
- for 只能接下面那一行,所以只會印出4個數字。
-
第5行的 for 迴圈的 int i; 只能作用在迴圈裡面,
所以第7行的 i 是吃到第4行的 int i;。
(A)
(B)
(C)
0 1 2 3 7
(D)
(C)
#include <iostream>
using namespace std;
int main () {
int i = 7, n = 3;
for (int i=0; i<n; i++)
cout << i << " ";
cout << i << endl;
}
0 0
1 1
2 2
3 3
Compiler Error
0 1 2 7
2 mins
Loop sp.
盤點那些你可能會遇到的神奇迴圈
sp. 0 - 輸入至EOF
Loop sp. - 0
- 有時候你會看到這種東西:輸入直到EOF結束。那麼什麼是EOF呢?
- EOF: End Of File,直翻檔案結尾,也就是請你的程式要輸入到沒東西後停止。
-
怎麼判斷已經沒東西輸入了呢?
- 好巧不巧,cin任何一個東西,要是它是false,就代表輸入完了。
- 下面這份程式代表如果cin >> x是false (表示沒東西了),就會跳出迴圈。
取自 zerojudge a004 的輸入說明
int x;
while (cin >> x) {
// 做事情
}
輸入一個數字直到EOF的範例
sp. 1 - while(Q--)
- 如果題目說"接下來有Q筆操作"...
-
那麼我們會習慣寫while(Q--){...}
-
那麼我們會習慣寫while(Q--){...}
- 接著我們來複習一下:
Loop sp. - 1
*例如loop12
你曾經看過的 if-else 的投影片
- 所以如果Q=2,模擬迴圈的話...
- while(2),進迴圈,並且Q會變1。
- while(1),進迴圈,並且Q會變0。
- while(0),出迴圈,並且Q會變-1。
- 迴圈會跑兩次,恰好跟Q的次數一樣,這樣你懂while(Q--)的用法了嗎?
sp. 2 - --> / <-- 是什麼鬼語法?
Loop sp. - 2
- 我們先來看一則stackoverflow上的奇聞軼事。
我沒有故意要湊表情符號@@
"-->"是什麼運算子?
在讀完...,我驚奇的發現下面這份code可以編譯&成功執行
我想這是C的語法,因為GCC編譯器也可以執行。請問他定義在編譯器標準的哪裡?
// x 趨近於0
stackoverflow
一個(通常)發問程式問題的地方。
sp. 2 - --> / <-- 是什麼鬼語法?
Loop sp. - 2
- 所以 --> 到底是什麼?
- 如果是 while (x --> 0) 其實就是 while ((x--) > 0)
- 那 <-- 呢?
- 一樣的道理,while (0 <-- x) 其實就是 while (0 < (--x))
- !!!!不要這樣寫,當作看故事就好!!!!
我沒有故意要湊表情符號@@
int Q = 4;
while (Q --> 0) {
cout << "(" << Q << ")";
}
(3)(2)(1)(0)
Q --> 0 的範例 (4次)
int Q = 4;
while (Q --> -2) {
cout << "(" << Q << ")";
}
(3)(2)(1)(0)(-1)(-2)
Q --> -2 的範例 (6次)
int Q = 4;
while (0 <-- Q) {
cout << "(" << Q << ")";
}
(3)(2)(1)
0 <-- Q 的範例 (3次)
sp. 2 - --> / <-- 是什麼鬼語法?
Loop sp. - 2
- 我們再來看其他人還有什麼創意回答 (?)
- 一個反斜線代表程式還沒結束,換行繼續寫。
- 雖然不能 x ----> 0,但是可以 0 <---- x。
- 這跟 x-- 和 --x 的本質有關係,
有興趣可以自己查 (?)
- 這跟 x-- 和 --x 的本質有關係,
我沒有故意要湊表情符號@@
你長度多一點遞減比較快(?)
用反斜線畫斜向箭頭。
Exercise V
來看看APCS觀念題吧!
Exercise V
APCS觀念考古題
接下來有四題重複結構的APCS觀念題目。
- 每題計時2~3分鐘。
- 在開始之前先複習一下之前的投影片喔!
APCS 105/3 - 觀念題 - 15
15. 若以f(22)呼叫右側 f()函式,總共會印出多少數字?
3 mins
void f(int n) {
printf ("%d\n", n);
while (n != 1) {
if ((n%2)==1) {
n = 3*n + 1;
}
else {
n = n / 2;
}
printf ("%d\n", n);
}
}
因為你可能看不懂,在這邊先解釋一下:
- f(22) 的意思就是把n代22進去看就對了。
- 就是
printf("%d\n", n);
cout << n << endl;
Exercise V - APCS觀念題 - I
這不就是考拉茲猜想 (3n+1問題)?
- 22 -> 11 -> 34 -> 17 -> 52 -> 26 -> 13 -> 40 -> 20 -> 10 -> 5 -> 16 -> 8 -> 4 -> 2 -> 1。
- 答案:總共16次。
APCS 105/3 - 觀念題 - 21
21. 右側程式碼,執行時的輸出為何?
2 mins
void main() {
for (int i=0; i<=10; i=i+1) {
// printf ("%d ", i);
cout << i << " ";
i = i + 1;
}
// printf ("%d ", i);
cout << endl;
}
0 2 4 6 8 10
0 1 2 3 4 5 6 7 8 9 10
(A)
(B)
0 1 3 5 7 9
(C)
0 1 3 5 7 9 11
(D)
(A)
- 一開始 i = 0,所以印出0。
-
因為第5行, i 變成 1。
-
因為for迴圈的i=i+1,i 變成 2。
-
輸出 2。 (接著下去直到 i = 12才會跳出去。)
Exercise V - APCS觀念題 - II
APCS 105/10 - 觀念題 - 12
12. 右側程式片段執行過程中的輸出為何?
2 mins
int a = 5;
// ...
for (int i=0; i<20; i=i+1){
i = i + a;
// printf ("%d ", i);
cout << i << " ";
}
5 10 15 20
5 11 17 23
(A)
(B)
6 12 18 24
(C)
6 11 17 22
(D)
(B)
Exercise V - APCS觀念題 - III
- 一開始進for迴圈不會跑控制運算 (i = i + 1),所以第一個數字是5。
-
接下來因為 i = i + a; 還有 i =i + 1; ,所以每次輸出前都會+6。
APCS 106/03 - 觀念題 - 13
13. 右側程式片段無法正確列印 20 次的"Hi!",請問下 列哪一個修正方式仍無法正確列印 20 次的"Hi!"?
for (int i=0; i<=100; i=i+5) {
// printf ("%s\n", "Hi!");
cout << "Hi!" << endl;
}
需要將 i<=100 和 i=i+5 分別修正為 i<20 和 i=i+1
需要將 i=0 修正為 i=5
(A)
(B)
需要將 i<=100 修正為 i<100;
(C)
需要將 i=0 和 i<=100 分別修正為 i=5 和 i<100
(D)
(D)
- (A) 最常用的跑20次的迴圈寫法。
- (B) 變數i的轉變: [5, 10, 15, 20, ... ,100] (到105就出去了) -> 20次。
- (C) 變數i的轉變: [0, 5, 10, 15, 20, ..., 95] (到100就出去了) -> 也是20次。
- (D) 變數i的轉變: [5, 10, ..., 95] (到100就出去了) -> 19次。
Exercise V - APCS觀念題 - IV
2 mins
Exercise VI
第二大數字判斷
Exercise VI
程式練習題
接下來有一題請你用
程式實做重複結構的題目。
- 我會花1分鐘讀題。
- 總共計時20分鐘。
- 第10分鐘我會給個提示。
- 開好你的程式環境吧!
174 - 第二大數判斷 - 前導
Exercise V - 第二大數字判斷 - -1
20 mins
174 - 第二大數判斷 - 題目
Exercise V - 第二大數字判斷 - 0
- 題目:給你一串數字,判斷第二大的數字是誰,重複也算。(數字範圍為0 ~ 2^64-1)
20 mins
- 提示:先想想看怎麼找出最大的數字?
- 提示的提示:你可以在外面開一個變數紀錄,像判斷質數的is_prime一樣。
- 不是提示的建議:如果你已經會陣列,不要開陣列做這題喔!
- 例如,題目輸入5個數字,5 4 3 2 1,那麼你應該輸出4。
174 - 第二大數判斷 - 提示的解答
20 mins
#include <iostream>
using namespace std;
int main () {
unsigned long long n, fmax;
cin >> n;
for (int i=0; i<n; i++) {
unsigned long long x;
cin >> x;
if (i == 0 || x > fmax)
fmax = x;
}
cout << fmax << endl;
}
找出最大的數字,想想看為甚麼要加上判斷 i == 0?
- 下一個提示:想想x,目前最大的數字,還有目前第二大數字,這三者關係。
- 提示:先想想看怎麼找出最大的數字?
- 提示的提示:你可以在外面開一個變數紀錄,像判斷質數的is_prime一樣。
- 題目:給你一串數字,判斷第二大的數字是誰,重複也算。(數字範圍為0 ~ 2^64-1)
Exercise V - 第二大數字判斷 - 1
174 - 第二大數判斷 - 引導
20 mins
- 下一個提示:想想x,目前最大的數字,還有目前第二大數字,這三者關係。
目前最大值
fmax
目前第二大值
smax
x >= fmax
fmax >= x x >= smax
smax >= x
- x 取代最大值
- 最大值淪為第二大值
- x 取代第二大值
- 第二大值消失:(
- x 不需要啦
if (x > fmax)
smax = fmax, fmax = x;
else if (x > smax)
smax = x;
大概寫出來的樣子
- 奇怪,那麼一開始變數設置怎麼辦呢?
- 題目:給你一串數字,判斷第二大的數字是誰,重複也算。(數字範圍為0 ~ 2^64-1)
Exercise V - 第二大數字判斷 - 2
174 - 第二大數判斷 - 解答
20 mins
#include <iostream>
using namespace std;
int main () {
unsigned long long n, fmax, smax;
cin >> n;
for (int i=0; i<n; i++) {
unsigned long long x;
cin >> x;
if (i == 0 || x > fmax)
smax = fmax, fmax = x;
else if (i == 1 || x > smax)
smax = x;
}
cout << smax << endl;
}
- 題目:給你一串數字,判斷第二大的數字是誰,重複也算。(數字範圍為0 ~ 2^64-1)
最終解答的長相
- 奇怪,那麼一開始變數設置怎麼辦呢?
if (x > fmax)
smax = fmax, fmax = x;
else if (x > smax)
smax = x;
大概寫出來的樣子
if (i == 0 || x > fmax)
smax = fmax, fmax = x;
else if (x > smax)
smax = x;
如果是第一個數字,強制設成fmax就可以了,那smax...?
Exercise V - 第二大數字判斷 - 3
174 - 第二大數判斷 - 更方便的解答
20 mins
- 題目:給你一串數字,判斷第二大的數字是誰,重複也算。(數字範圍為0 ~ 2^64-1)
- 奇怪,那麼一開始變數設置怎麼辦呢?
- 其實如果你知道最小值,
直接把兩個設成最小值就好了。
unsigned long long fmax = 0;
unsigned long long smax = 0;
if (x > fmax)
smax = fmax, fmax = x;
else if (x > smax)
smax = x;
大概寫出來的樣子
Exercise V - 第二大數字判斷 - 4
#include <iostream>
using namespace std;
int main () {
unsigned long long n, fmax=0, smax=0;
cin >> n;
for (int i=0; i<n; i++) {
unsigned long long x;
cin >> x;
if (x > fmax)
smax = fmax, fmax = x;
else if (x > smax)
smax = x;
}
cout << smax << endl;
}
最終解答的長相
Review
重複結構的語法小總結
複習 - 看過的題目們
Review - 0
-
再變得更難之前,我們來看看我們曾經做過什麼吧!
-
循環的猜數字 (goto)
-
可控隨機數 (看著流程圖用goto實作 -> for + do-while)
-
考拉茲猜想 (goto -> do-while -> while)
-
進位制轉換 (do-while)
-
累加數字 1+2+...+n (while -> for)
-
判斷質數 (while -> +break)
-
第二大數字判斷 (for + 複雜的if判斷)
-
- 接著我們來想想看什麼時候適合用哪些語法吧!
goto
Review - 1
- 最簡單的重複結構。
- 如果有多個情況都會跳到同個地方,比較適合。
- 在猜數字的時候,不管是猜的比較大還是比較小,都要回到一開始重猜一次。
-
在處理輸入錯誤的時候,不管是哪種錯誤訊息都要回去請使用者重輸入一次。
- 如果你熟悉後面的語法的話,這用try-catch會比較好。
- 還有一個很神奇的用法,我們之後會提到。
- goto並不能break和continue。程式不知道你要bre啥。
- 如果有多個情況都會跳到同個地方,比較適合。
goto流程圖
// 設下標記點
labelA:
do_something();
// 跳到標記點
goto labelA;
goto 語法
do-while
Review - 2
- 最簡單帶有條件的重複結構。
-
不固定次數,且至少做一次的時候使用。
- 例如生成亂數,就至少要做一次。
- 例如進位制轉換,因為就算是0也要輸出一個0,所以至少要做一次。
-
不固定次數,且至少做一次的時候使用。
- 小括號後最後有分號,不要忘記。
do {
do_something();
// 如果true會跳到do{那行
} while (true 或 false);
do-while流程圖
do-while 語法
while
Review - 3
while流程圖
// while 一開始,如果判斷為F則跳到}後
while (T / F) {
// 做事情
} // 結束會回到while(){重新判斷
while 語法
- 事先檢查的 do-while。
-
不固定次數,需要事先檢查的時候使用。
- 例如3n+1問題,一開始就要判斷是不是1。
-
不固定次數,需要事先檢查的時候使用。
for
Review - 4
for 流程圖
- 把控制變數放在一起的的重複結構。
- 常用在有固定次數的迴圈上。
- 例如固定跑n次的1到n加總。
- 最重要的重複結構。
- 常用在有固定次數的迴圈上。
- 三個區塊要用分號隔開。
for (初始設置; True / False; 控制運算) {
// 做事情
}
for 語法
break & continue
Review - 5
for + break
for + continue
- break; 強制跳出這層迴圈。
- continue; 直接重來這個迴圈。
- 當時的example code:
#include <iostream>
using namespace std;
int main () {
for (int i=0; i<10; i++) {
if (i % 2 == 0)
continue;
if (i == 7)
break;
cout << i << " ";
}
}
輸出: 1 3 5
Nested Loop
該來的終究是要來的 - 巢狀迴圈
什麼是巢狀迴圈
Nested Loop - 0
-
我們先來看看什麼是巢狀結構。
- 巢狀迴圈其實就是迴圈裡面還有迴圈。 That's it.
但很不好理解,好好學喔!
巢狀選擇結構的投影片。
在這之前,先看看第一個例題!
Nested Loop - 1
-
還記得Exercise I - 可控隨機數嗎?
-
你能不使用goto,用其他種迴圈實作出來嗎?
-
int cnt = 0;
start:
int x = LFSR() % (n+1);
if (x % k == 0)
goto start;
cout << x << endl;
cnt ++;
if (cnt != 5)
goto start;
可控隨機數的流程圖
可控隨機數的code
在這之前,先看看第一個例題!
Nested Loop - 2
-
還記得Exercise I - 可控隨機數嗎?
-
用do-while處理生亂數,因為次數不定,而且要先做一次。
-
int x, cnt = 0;
start:
do {
x = LFSR() % (n+1);
} while (x % k == 0);
cout << x << endl;
cnt ++;
if (cnt != 5)
goto start;
可控隨機數的流程圖
將生成亂數轉成比較可讀的迴圈
在這之前,先看看第一個例題!
Nested Loop - 3
-
還記得Exercise I - 可控隨機數嗎?
-
用for處理計數字,因為固定要5次。
-
你會發現,這竟然是for + do-while的雙重迴圈。
-
Wow!竟然我們一開始學的例題就是巢狀迴圈嗎?
-
int x;
for (int cnt=0; cnt < 5; cnt++) {
do {
x = LFSR() % (n+1);
} while (x % k == 0);
cout << x << endl;
}
可控隨機數的流程圖
將計數五次轉成比較可讀的迴圈
Double for
最經典的巢狀迴圈 - nm雙重迴圈
什麼是笛卡爾座標系?
Double for - 0
-
你知道什麼是一維座標系嗎?
-
我們可以用一個迴圈印出所有的點。
-
一個笛卡爾坐標系的範例圖
一個一維座標系(數線)的範例圖
for (int i=0; i<=n; i++) {
cout << "(" << i << ")\t";
}
-
那你知道什麼是二維(笛卡爾)座標系嗎?
- 我們可以用一個雙層迴圈印出所有的點。
* '\t'叫做水平定位符,簡單來說就是幫你空格到下一個八的倍數的地方。
印出笛卡爾座標
Double for - 1
一個笛卡爾坐標系的圖
int n, m;
cin >> n >> m;
//==========================
for (int j=0; j<=m; j++) {
cout << "(" << j << ")\t";
}
cout << endl;
//==========================
for (int j=0; j<=m; j++) {
cout << "(" << j << ")\t";
}
cout << endl;
//==========================
for (int j=0; j<=m; j++) {
cout << "(" << j << ")\t";
}
cout << endl;
// 重複做n+1次
-
那你知道什麼是二維(笛卡爾)座標系嗎?
- 我們可以用一個雙層迴圈印出所有的點。
- 把它想成是做n次的一維迴圈就可以了。
int n, m;
cin >> n >> m;
for (int i=0; i<=n; i++) {
for (int j=0; j<=m; j++) {
cout << "(" << j << ")\t";
}
cout << endl;
}
Double for - 2
一個笛卡爾坐標系的圖
-
那你知道什麼是二維(笛卡爾)座標系嗎?
- 在把 i 和 j 印出來,這裡的 i 就是在指
現在是第幾次跑裡面的迴圈。
- 在把 i 和 j 印出來,這裡的 i 就是在指
(0,0) (0,1) (0,2) (0,3) (0,4)
(1,0) (1,1) (1,2) (1,3) (1,4)
(2,0) (2,1) (2,2) (2,3) (2,4)
(3,0) (3,1) (3,2) (3,3) (3,4)
(4,0) (4,1) (4,2) (4,3) (4,4)
印出笛卡爾座標
#include <iostream>
using namespace std;
int main () {
int n, m;
cin >> n >> m;
for (int i=0; i<=n; i++) {
for (int j=0; j<=m; j++) {
cout << "(" << i << "," << j << ")\t";
}
cout << endl;
}
}
Double for - 3
- 印出來你會發現這個座標好像是順序怪怪的?
- 沒有錯,因為印字順序的關係,
這就是程式裡面常用的座標方向。 - 也就是x為往下縱軸,y為往右橫軸。
- 沒有錯,因為印字順序的關係,
(0,0) (0,1) (0,2) (0,3) (0,4)
(1,0) (1,1) (1,2) (1,3) (1,4)
(2,0) (2,1) (2,2) (2,3) (2,4)
(3,0) (3,1) (3,2) (3,3) (3,4)
(4,0) (4,1) (4,2) (4,3) (4,4)
關於程式的笛卡爾座標
輸入n, m,印出nm平面座標的程式。
#include <iostream>
using namespace std;
int main () {
int n, m;
cin >> n >> m;
for (int i=0; i<=n; i++) {
for (int j=0; j<=m; j++) {
cout << "(" << i << "," << j << ")\t";
}
cout << endl;
}
}
n, m = 4, 4的二維座標系
笛卡爾座標 → 九九乘法表
Double for - 4
-
接著我們利用座標系的雙層迴圈,做點轉換。
-
題目:你能夠依據右邊的程式,寫出一個
九九乘法表嗎? (從1到9,沒有0。) -
提示:觀察印出來的座標和99乘法表的關係。
-
把範圍跟輸出稍微改一下就可以了!
-
#include <iostream>
using namespace std;
int main () {
for (int i=1; i<=9; i++) {
for (int j=1; j<=9; j++)
cout << i * j << "\t";
cout << endl;
}
}
10 mins
輸入n, m,印出nm平面座標的程式。
#include <iostream>
using namespace std;
int main () {
int n, m;
cin >> n >> m;
for (int i=0; i<=n; i++) {
for (int j=0; j<=m; j++) {
cout << "(" << i << "," << j << ")\t";
}
cout << endl;
}
}
(0,0) (0,1) (0,2) (0,3) (0,4)
(1,0) (1,1) (1,2) (1,3) (1,4)
(2,0) (2,1) (2,2) (2,3) (2,4)
(3,0) (3,1) (3,2) (3,3) (3,4)
(4,0) (4,1) (4,2) (4,3) (4,4)
n, m = 4, 4的二維座標系
一般的九九乘法表。
一份code告訴你為甚麼不要濫用goto
-
如果你把九九乘法表的迴圈全部都用 goto 實作,你會覺得這到底是三小==。
用 goto 實作九九乘法表。
#include <iostream>
using namespace std;
int main() {
int i = 1;
outer:
int j = 1;
inner:
cout << i * j << "\t";
j += 1;
if (j <= 9)
goto inner;
cout << endl;
i += 1;
if (i <= 9)
goto outer;
}
Double for - 5
Exercise VII
指數表
Exercise VII
程式練習題
接下來有一題請你用
程式實做多重迴圈的題目。
- 我會花1分鐘讀題。
- 總共計時20分鐘。
- 第10分鐘我會給個提示。
- 開好你的程式環境吧!
179 - 指數表 - 題目
Exercise VII - 指數表 - 0
- 題目:給n, m,請輸出n, m的指數表 (中間用空格隔開,從0, 0開始),輸出都是int。
20 mins
- 提示1:使用像剛剛笛卡爾座標→九九乘法表的思考方式。
- 提示2:你的迴圈有可能會變三層。
"1\t0\t0\t0\t0\t0\n"
"1\t1\t1\t1\t1\t1\n"
"1\t2\t4\t8\t16\t32\n"
"1\t3\t9\t27\t81\t243\n"
"1\t4\t16\t64\t256\t1024\n"
"1\t5\t25\t125\t625\t3125\n"
輸入 5 5 應該要輸出的表
1 0 0 0 0 0
1 1 1 1 1 1
1 2 4 8 16 32
1 3 9 27 81 243
1 4 16 64 256 1024
1 5 25 125 625 3125
怕你格式有問題,這邊印出C++表達式。
(記得行末沒有\t。)
179 - 指數表 - 提示
20 mins
- 給定數字n, m,你會算出n的m次方嗎? 應該不難。
- 盡量不要使用cmath的pow,避免有浮點數誤差。(雖然還是會AC...)
Exercise VII - 指數表 - 1
int x = 1;
for (int k=1; k<=m; k++) {
x *= n;
}
- 題目:給n, m,請輸出n, m的指數表 (中間用空格隔開,從0, 0開始),輸出都是int。
給定數字 ,算出
179 - 指數表 - 答案
20 mins
Exercise VII - 指數表 - 2
#include <iostream>
using namespace std;
int main () {
int n, m;
cin >> n >> m;
for (int i=0; i<=n; i++) {
for (int j=0; j<=m; j++) {
cout << "(" << i << "," << j << ")\t";
}
cout << endl;
}
}
輸入 ,印出 平面座標的程式。
#include <iostream>
using namespace std;
int main () {
int n, m;
cin >> n >> m;
for (int i=0; i<=n; i++) {
for (int j=0; j<=m; j++) {
// 想要印出 i^j
int x = 1;
for (int k=1; k<=j; k++) {
x *= i;
}
cout << x << (j != m ? '\t' : '\n');
}
}
}
- 題目:給n, m,請輸出n, m的指數表 (中間用空格隔開,從0, 0開始),輸出都是int。
把 的程式貼上去改一改就可以了
179 - 指數表 - 更快的答案
20 mins
Exercise VII - 指數表 - 2
#include <iostream>
using namespace std;
int main () {
int n, m;
cin >> n >> m;
for (int i=0; i<=n; i++) {
int x = 1;
for (int j=0; j<=m; j++) {
cout << x << (j != m ? '\t' : '\n');
x *= i;
}
}
}
其實 可以透過 來算出,
利用前面算過的值就不用跑這麼多次了!
#include <iostream>
using namespace std;
int main () {
int n, m;
cin >> n >> m;
for (int i=0; i<=n; i++) {
for (int j=0; j<=m; j++) {
// 想要印出 i^j
int x = 1;
for (int k=1; k<=j; k++) {
x *= i;
}
cout << x << (j != m ? '\t' : '\n');
}
}
}
把 的程式貼上去改一改就可以了
- 題目:給n, m,請輸出n, m的指數表 (中間用空格隔開,從0, 0開始),輸出都是int。
Double Break !!
如何一次break兩個迴圈?
Double Break !!
Double Break!! - 0
- 有時候我們會使用break來提早結束運算。
- 在質數判斷的時候,我們用break來跳出一層迴圈。
- 那兩層呢?該怎麼辦?
10 mins
#include <iostream>
using namespace std;
int main () {
for (int i=1; i<=9; i++) {
for (int j=1; j<=9; j++)
cout << i * j << "\t";
cout << endl;
}
}
一般的九九乘法表。
- 題目:試著在九九乘法表裡面, 的時候直接跳出雙層迴圈。
Double Break !! - flag
Double Break!! - 1
10 mins
for (int i=1; i<=9; i++) {
bool flag = false;
for (int j=1; j<=9; j++) {
cout << i * j << "\t";
if (i*j == 49) {
flag = true;
break;
}
}
cout << endl;
if (flag)
break;
}
利用變數來跳脫雙重迴圈
- 題目:試著在九九乘法表裡面, 的時候直接跳出雙層迴圈。
- 你可以試著這樣思考:要怎麼樣才可以讓外面的迴圈知道要跳出呢?
- 用flag紀錄到底該不該跳出去。
Double Break !! - goto
Double Break!! - 2
10 mins
for (int i=1; i<=9; i++) {
bool flag = false;
for (int j=1; j<=9; j++) {
cout << i * j << "\t";
if (i*j == 49)
goto end;
}
cout << endl;
}
// 稍微注意一下,標記點之後
// 沒有code要加分號。
end:;
利用 goto 來跳脫雙重迴圈
- 題目:試著在九九乘法表裡面, 的時候直接跳出雙層迴圈。
- 你其實還可以使用 goto。 驚喜不?
- 「沒有不該用的語法,只有用的好不好而已。」
Exercise VIII - I
孤單的機器人
Exercise VIII
程式練習題
接下來有一題請你用
程式實做多重迴圈的題目。
- 我會花1分鐘讀題。
- 總共計時20分鐘。
- 第10分鐘我會給個提示。
- 開好你的程式環境吧!
180 - 孤單的機器人 - 題目
- 題目:給定地圖的高n與寬m,機器人的座標x, y,以及指令數量Q,
接下來Q個指令只會是"WASD"其中一個,代表要對機器人往哪個方向移動。 - 對於每個指令,輸出目前地圖長相,機器人的位置為'@',空地為'.'。
- 如果機器人的下一步會卡在牆裡面,則忽略該指令。
20 mins
Exercise VIII - I - 孤單的機器人 - 1
.@..
....
....
輸入:
n, m = 3, 4
x, y = 0, 1
Q = 3
初始地圖
(不用輸出)
..@.
....
....
..@.
....
....
....
..@.
....
D
W
S
往右移動
往上移動
但碰壁,不能移動
往下移動
結束!
忽略指令
也要輸出。
180 - 中場影片
Exercise VIII - I - 孤單的機器人 - 2
180 - 孤單的機器人 - 提示
- 題目:給定地圖的高n與寬m,機器人的座標x, y,以及指令數量Q,
接下來Q個指令只會是"WASD"其中一個,代表要對機器人往哪個方向移動。 - 對於每個指令,輸出目前地圖長相,機器人的位置為'@',空地為'.'。
- 如果機器人的下一步會卡在牆裡面,則忽略該指令。
20 mins
- 怎麼輸出地圖呢?
- 輸出nm格子,預設為'.',座標等於機器人就輸出'@'。
for (int i=0; i<n; i++) {
for (int j=0; j<m; j++) {
if (i == x && j == y)
cout << '@';
else
cout << '.';
}
cout << endl;
}
Exercise VIII - I - 孤單的機器人 - 3
180 - 孤單的機器人 - 引導
- 題目:給定地圖的高n與寬m,機器人的座標x, y,以及指令數量Q,
接下來Q個指令只會是"WASD"其中一個,代表要對機器人往哪個方向移動。 - 對於每個指令,輸出目前地圖長相,機器人的位置為'@',空地為'.'。
- 如果機器人的下一步會卡在牆裡面,則忽略該指令。
20 mins
- 你只要會畫圖,它其實就是一般的if-else題目了!
- 如果指令要往上 ('W'),且機器人還可以往上走 (x != 0),就往上走 (x--;)
- 四個方向寫完就像下面這樣 :
char cmd;
cin >> cmd;
if (cmd == 'W' && x != 0) x--;
if (cmd == 'A' && y != 0) y--;
if (cmd == 'S' && x != n-1) x++;
if (cmd == 'D' && y != m-1) y++;
Exercise VIII - I - 孤單的機器人 - 4
180 - 孤單的機器人 - 答案
- 題目:給定地圖的高n與寬m,機器人的座標x, y,以及指令數量Q,
接下來Q個指令只會是"WASD"其中一個,代表要對機器人往哪個方向移動。 - 對於每個指令,輸出目前地圖長相,機器人的位置為'@',空地為'.'。
- 如果機器人的下一步會卡在牆裡面,則忽略該指令。
20 mins
- 把引導(改座標)跟提示(畫圖)結合起來,就可以做出答案了!
#include <iostream>
using namespace std;
int main () {
int n, m, x, y, Q;
cin >> n >> m >> x >> y >> Q;
while (Q--) {
char cmd;
cin >> cmd;
if (cmd == 'W' && x != 0) x--;
if (cmd == 'A' && y != 0) y--;
if (cmd == 'S' && x != n-1) x++;
if (cmd == 'D' && y != m-1) y++;
for (int i=0; i<n; i++) {
for(int j=0; j<m; j++) {
if (i == x && j == y)
cout << '@';
else
cout << '.';
}
cout << endl;
}
}
return 0;
}
Exercise VIII - I - 孤單的機器人 - 5
感覺很長的解答code
Exercise VIII - II
孤單的機器人 (補充)
接下來我們要教的東西...
Exercise VIII - II - 孤單的機器人 (補充) - 0
GetKeyState(VK) - 介紹
- 你不覺得一直W⏎A⏎S⏎D⏎很麻煩嗎?
- 所以我們在這邊教你一個可以不用按Enter的方法。
Exercise VIII - II - 孤單的機器人 (補充) - 1
#include <windows.h>
#include <iostream>
using namespace std;
int main () {
while (true) {
cout << GetKeyState(VK_LEFT);
// 停0.1秒
Sleep(100);
}
}
跑跑看這份code!然後按按看左鍵(?)
5 mins
GetKeyState(VK) - 觀察
- 你不覺得一直W⏎A⏎S⏎D⏎很麻煩嗎?
- 所以我們在這邊教你一個可以不用按Enter的方法。
- 你會得到左邊的規律。
- 那該怎麼讓他按一下觸發一次呢?例如按一下左鍵就輸出一次LEFT?
Exercise VIII - II - 孤單的機器人 (補充) - 2
#include <windows.h>
#include <iostream>
using namespace std;
int main () {
while (true) {
cout << GetKeyState(VK_LEFT);
// 停0.1秒
Sleep(100);
}
}
跑跑看這份code!然後按按看左鍵(?)
0
-127
1
-128
按下
按下
放
開
放
開
10 mins
GetKeyState(VK) - 使用
- 你不覺得一直W⏎A⏎S⏎D⏎很麻煩嗎?
- 那該怎麼讓他按一下觸發一次呢?例如按一下左鍵就輸出一次LEFT?
- 開一個變數紀錄到底是不是從沒有按 -> 按下。
Exercise VIII - II - 孤單的機器人 (補充) - 3
#include <windows.h>
#include <iostream>
using namespace std;
int main () {
bool pressed = false;
while (true) {
int getKeyLeft = GetKeyState(VK_LEFT);
if (getKeyLeft >= 0)
pressed = false;
else if (!pressed) {
pressed = true;
cout << "Left!" << endl;
}
}
}
0
-127
1
-128
按下
按下
放
開
放
開
10 mins
pressed =
false
pressed =
true
pressed =
false
pressed =
true
GetKeyState(VK) - 應用
- 題目:請將剛剛你AC的code,稍加修改一下。變成只要有按上下左右任何一個按鍵,就輸出現在的地圖。
- 下面這是節錄虛擬按鍵(Virtual Key)的表,全部的表連結在這裡。
- 接著來看看實作出來的程式大概長怎樣吧!
Exercise VIII - II - 孤單的機器人 (補充) - 4
10 mins
按下A: GetKeyState('A'),只能用大寫,A-Z同理。
按上下左右: VK_UP / VK_DOWN / VK_LEFT / VK_RIGHT
滑鼠左/右鍵: VK_LBUTTON / VK_RBUTTON
Enter: VK_RETURN
左/右Shift: VK_LSHIFT / VK_RSHIFT
F1...F12: VK_F1 ... VK_F12
...
跑出來大概的樣子
GetKeyState(VK) - 應用
- 如果你學會陣列,你就可以不用寫這麼長的code喔!
- 一直往下印,好沒實感...
- 你可以用 來清空輸出,就像這份程式!
- 有沒有覺得漂亮很多了呢?
Exercise VIII - II - 孤單的機器人 (補充) - 5
10 mins
system("cls");
可以做到像這樣的事情。
Exercise IX
簡單的小應用 - 解析幾何小幫手
Exercise IX
程式練習題
接下來有一題請你用
程式實做重複結構的題目。
- 我會花1分鐘讀題。
- 總共計時20分鐘。
- 第10分鐘我會給個提示。
- 開好你的程式環境吧!
182 - 解析幾何小幫手 - 題目
- 題目:給定畫布 ,以及不等式
請在不等式成立的地方畫上'*',不是請留空白' '。
20 mins
Exercise IX - 解析幾何小幫手 - 1
*****
*****
*****
*****
****
***
***
***
**
範例測資1的輸入
不等式的長相
畫布該畫的區域
你的程式該輸出...
* 左下角(0, 0),
右上角(n, m)
182 - 解析幾何小幫手 - 解答
- 題目:給定畫布 ,以及不等式
請在不等式成立的地方畫上'*',不是請留空白' '。 - 注意雙層迴圈的順序就可以了。
- 先是y軸,從m印到0,再來才是x軸,0印到n。
20 mins
Exercise IX - 解析幾何小幫手 - 2
#include <iostream>
using namespace std;
int main() {
int n, m, a, b, c, d, e, f;
cin >> n >> m >> a >> b >> c >> d >> e >> f;
for(int y=m; y>=0; y--) {
for(int x=0; x<=n; x++){
bool constraint = (a*x*x + b*x*y + c*y*y + d*x + e*y >= f);
cout << (constraint ? '*' : ' ');
}
cout << endl;
}
return 0;
}
Exercise X
內外迴圈的交纏 - 數字三角形
Exercise VII
程式練習題
接下來有一題請你用
程式實做重複結構的題目。
- 我會花1分鐘讀題。
- 總共計時20分鐘。
- 開好你的程式環境吧!
181 - 數字三角形 - 題目
- 題目:給定n,請輸出一個兩股長度為n的數字三角形。
- 提示:剛好印出的是n*n的格子也?加上if會怎麼樣?
20 mins
Exercise X - 數字三角形 - 0
__+
_++
+++
n = 3 的樣子
n = 5 的樣子
___________+
__________++
_________+++
________++++
_______+++++
______++++++
_____+++++++
____++++++++
___+++++++++
__++++++++++
_+++++++++++
++++++++++++
n = 12 的樣子
____+
___++
__+++
_++++
+++++
181 - 數字三角形 - 引導 - 1
- 題目:給定n,請輸出一個兩股長度為n的數字三角形。
- 提示:剛好印出的是n*n的格子也?加上if會怎麼樣?
- 那些點要印出+呢?
- 那有什麼規律呢?
20 mins
____+
___++
__+++
_++++
+++++
n = 5的樣子
Exercise X - 數字三角形 - 1
181 - 數字三角形 - 解答 - 1
- 題目:給定n,請輸出一個兩股長度為n的數字三角形。
- 那些點要印出+呢?
20 mins
#include <iostream>
using namespace std;
int main() {
int n;
cin >> n;
for (int i=0; i<n; i++) {
for (int j=0; j<n; j++) {
if (i+j >= n-1)
cout << "+";
else
cout << "_";
}
cout << endl;
}
}
雙層迴圈 + if-else 的解答
Exercise X - 數字三角形 - 2
181 - 數字三角形 - 解答 - 2
20 mins
#include <iostream>
using namespace std;
int main() {
int n;
cin >> n;
for (int i=0; i<n; i++) {
for (int j=0; j<n-i-1; j++)
cout << "_";
for (int j=0; j<=i; j++)
cout << "+";
cout << endl;
}
}
內層迴圈拆解的解答
- 題目:給定n,請輸出一個兩股長度為n的數字三角形。
- 仔細想想,_要印幾次呢?
- 每一行 + 又要印幾次呢?
- 接著把迴圈分解是不是就好了呢?
Exercise X - 數字三角形 - 3
結語
兩天的迴圈終於結束了!
最終複習
- 在一般的重複結構,你學到了四種語法:
- goto - 一種可以達到簡單重複結構的語法。
- do-while - goto 結合 if-else 的迴圈。
- while - 事先檢查的 do-while 迴圈。
- for - 一種把控制迴圈的變數放在一起的簡化迴圈。
- 在巢狀的重複結構,你主要學到了靠座標化去思考問題。
- 在指數表中,你會用雙層迴圈 + 一層迴圈(n^m) 變成三層迴圈!
- 在數字三角形中,你會用規律來決定要印+還是_,還有拆解迴圈。
-
你還學到了一些奇奇怪怪的知識:
- 在可控隨機數 Controllable RNG,你學到了什麼是RNG。
- 在孤單的機器人,你學到了怎麼做鍵盤偵測。
Review - 0
接下來的課程 - 陣列
- 接下來你會上到甚麼呢?
- 陣列 / 多維陣列, 大致上就是讓你可以一次存一個數列或多維矩陣。
- 陣列很常利用迴圈來處理,所以希望大家回家可以好好複習迴圈喔 ~
Review - 1
Peipei的投影片
接下來的課程 - 陣列
- 陣列真的非常重要(?)
- 我們來看看多維陣列的例子:
陣列很重要,所以迴圈也很重要。
Review - 2
影片: 四維陣列 (時間, RGBA, X軸, Y軸)
格子狀地圖: 二維陣列 ((X,Y)放了甚麼)
End
明天就交給裴裴講師了(?)
題目表 - D1 - 單層迴圈
題目表 - 0
題目表 - D2 - 巢狀迴圈
題目表 - 1
APCS camp - Loop (+ Nested)
By Arvin Liu
APCS camp - Loop (+ Nested)
APCS Camp Loop
- 1,838