C++ 程式初探 IV 2015 暑期 ver. 1.0.2
C++ 程式 IV 大綱 1. 時間函式 2. 格式化輸出 3. 遞迴函式 (recursion) 4. 字串 5. 字串轉型 2
補充語法 時間計算 引入標頭檔 #include <ctime> #include <iostream> #include <Windows.h> #include <ctime> using namespace std; 時間變數 clock_t a, b, c; 取得現在時間 clock(); int main(void) { clock_t start = clock(); // 開始計算時間 Sleep(500); clock_t end = clock(); // 結束 double(end start)/clk_tck; cout<<"elapsed time: cout<<double(end-start)/clk_tck<<endl; // 計算時間差 return 0; 注意 clock() 函式精度 (precision) 不佳, 精度約 15 毫秒 ( 毫秒為 10-3 秒 ), 而準度 (accuracy) 則依系統而異, 常有誤差 3
補充語法 時間計算 #include <iostream> #include <Windows.h> #include <ctime> using namespace std; int main(void) { LARGE_INTEGER start, end, freq; QueryPerformanceFrequency(&freq); // 取得 CPU 時脈 QueryPerformanceCounter(&start); // 記錄開始時間 Sleep(500); QueryPerformanceCounter(&end); // 記錄結束時間 cout<<"elapsed time: "; // 計算時間差 cout<<(end.quadpart-start.quadpart)/(double)(freq.quadpart)<<endl; return 0; 參考資料 : https://msdn.microsoft.com/zh-tw/library/windows/desktop/dn553408(v=vs.85).aspx 引入標頭檔 #include <Windows.h> 此計時方法精度極高, 精度達微秒 (10-6 秒 ) 準度依系統而異, 而經實測發現準度超過精度 ( 至少微秒 ) 4
格式化輸出 #include<fstream> #include<iomanip> using namespace std; int main(void) { ofstream fout("result.txt"); for(int i=1; i<=3; i++) { for(int j=1; j<4; j++) fout<<left<<setw(5)<<(i*j); fout<<endl; 固定格式輸出 setw(n) 向右對齊 (default) right 向左對齊 left return 0; 5
格式化輸出 格式化標頭檔 #include<iostream> #include<iomanip> using namespace std; int main(void) { double a = 4560/17.0; 精確度的設定, 只要使用一次, 之後都會採用這個設定 #include <iomanip> 精確度 setprecision(n) 一般數字表示 fixed cout<< a <<endl; // 預設輸出 cout<<fixed<<setprecision(10)<< a <<endl; // 設定小數點 10 位 cout<<scientific<< a <<endl; // 科學記號表示 cout<<fixed<< a <<endl; // 一般數學表示 return 0; 科學記號表示 scientific 6
檔案輸出 #include<fstream> using namespace std; int main(void) ofstream fout("fname.txt",ios::app); { int number = 6; 不覆蓋, 接續在檔案後 ofstream fout("result.txt",ios::app); fout<<"number = "; fout<<number; fout<<endl; 輸出類別變數名稱 ( 檔名字串 ) ; ofstream fout("fname.txt"); return 0; 7
檔案輸出 開啟檔案 #include<fstream> fout.open("fname.txt"); #include<iostream> 關閉檔案 using namespace std; int main(void) fout.close(); { int number; 檢查是否開啟 ifstream fin; fout.is_open(); fin.open("data.txt"); if(!fin.is_open()) { cout<<"file cannot be found..."<<endl; exit(1); fin>>number; fin.close(); return 0; 開啟檔案後, 務必檢查 8
函式 int pow(int n, int p) { int r = 1; for(int i=0; i<p; i++) r *= n; return r; int max(int n, int a[]) { int max = a[0]; for(int i=1; i<n; i++) if(a[i]>max) max = a[i]; return max; 函數 回傳型別函式名稱 ( 變數型態變數名稱 ) int fun (int input) 函數 - 陣列引數 回傳型別函式名稱 ( 變數型態變數名稱 ) int fun (int ary[]) int fun (int ary[][3]) 9
補充語法函式多載 (overload) int max(int n, int a[]) { int max = a[0]; for(int i=1; i<n; i++) if(a[i]>max) max = a[i]; return max; int max(int a, int b) { if(a>b) return a; else return b; 函數多載 int fun (int a); int fun (int a, int b); int fun (double a, double b); int fun (int a, int b, int c); 回傳值不一樣不可多載 int fun (int a, int b); double fun (int a, int b); 10
補充語法函式預設參數值 int max(int a, int b = 0, int c = 0) { if(a>b) if(a>c) return a; 預設參數不得在中間 else return c; else int max(int a, int b=0, int c) if(b>c) return b; else return c; int main(void) { cout << max(5) << endl; cout << max(5,6) << endl; return 0; 11
函式 遞迴 (Recursion) 120 #include<fstream> #include<iostream> using namespace std; 5* factorial(4); 4* factorial(3); // 遞迴階層函數 int factorial(int number ) { if(number <= 1) return 1; else 3* factorial(2); 2* factorial(1); 2 return number * factorial(number-1); return 1; 24 6 1 main() { factorial(5);//=5*4*3*2*1=120 return 0; 12
函式 遞迴 120 #include<fstream> #include<iostream> using namespace std; // 遞迴階層函數 int factorial(int number ) 3* factorial(2); 2 { if(number <= 1) 終止條件 return 1; 2* factorial(1); else return number * factorial(number-1); return 1; 遞迴 main() { factorial(5);//=5*4*3*2*1=120 return 0; 5* factorial(4); 4* factorial(3); 24 6 1 13
練習 1 Fibonacci numbers: 1, 1, 2, 3, 5, 8, 13, 21, 34, 55,... Fibonacci 數列是由 {1,1 數字開始, 之後每個數字為前兩個數字之合, 嘗試以程式計算 Fibonacci 數列 1. 利用 for 迴圈計算第 12 個 Fibonacci 數列的數字 2. 利用遞迴函式計算第 12 個 Fibonacci 數列的數字 3. 思考一下, 兩個方法的計算效率 14
C-style 字串 a s t r i n g \0 C-style: int main(void) { c[0] c[1] c[2] c[3] c[4] c[5] a[6] a[7] a[8] char* a = "a string"; char b[] = "a string"; char c[] = {'a',' ','s','t','r','i','n','g','\0'; 長度 int length = strlen(c); int compare = strcmp(a, b); 比較 return 0; compare 數值所代表的意義 : compare>0: lexicographically greater a > b compare<0: lexicographically less a < b compare==0: a == b 15
C-style 字串 C-style: int main(void) { 結合 char* str1 = "a str"; char* str2 = "ing"; char* str3 = strcat(str1, str2); //str3 is "a string" char* str1 = "a string"; char* str2 = "copy"; strcpy(str2, str1); //str2 become "a string" return 0; 複製 16
C++ 字串 C++ style: #include <string> VS 已經將 string 包含至外部相依的文件庫中, 只要有使用 std 即可直接使用, 不再需要 #include <string> 'a' ' ' 's' 't' 'r' 'i' 'n' 'g' '\0' stint main(void) { string str1 = "a string"; string str2("a string"); string str3(str2, 2, 8); //str3 is "string" c[0] c[1] c[2] c[3] c[4] c[5] a[6] a[7] a[8] int length = str3.length(); str1.compare(str2); str1.compare("word"); return 0; 長度 比較 17
C++ 字串 C++ style: #include <string> stint main(void) { char* str1 = "a"; 結合 char* str2 = "string"; string str3 = str1 + str2; //"a string" string str4 = "a " + str2; //"a string" char* str5 = str4.c_str(); cout << str4.c_str() << endl; 轉換成 c-style return 0; 18
轉換不同型態至字串 VS 2010 後之版本 引入標頭檔 #include <string> #include <iostream> #include <sstream> using namespace std; int main(void) { 注意, 此方法沒有安全檢查, 請確保字串為正確的 " 數字字串 " string 轉型函數 string = to_string( _val) double = stod( _Str) int = stoi( _Str) double d = 23.7; string str = to_string(long double(d)); // VS 2010: 浮點數至字串 string str = to_string(d); //VS2013 double dd = stod(str); // 字串至浮點數 int i = 13; str = to_string(long long(i)); str = to_string(i); //VS2013 int ii = stoi(str); // VS 2010: 整數至字串 // 字串至整數 19
補充語法判斷是否為數字 適用任何版本 #include <iostream> #include <sstream> using namespace std; int main(void) { //string s = "is 12 loh"; //string s = "12 loh"; string s = "12"; char * p ; int x = strtol(s.c_str(), &p, 10) ; if(*p == 0) { cout << " 是數字 " <<endl; else { cout << " 不是數字 " <<endl; cout << " x = " << x <<endl; string 轉型函數 ( 含判斷 ) int = strtol(s.c_str(), &p, 10) double = strtod(s.c_str(), &p) 20
轉換不同型態至字串 適用任何版本 int 與 string 互轉 #include <iostream> #include <sstream> using namespace std; int main(void) { string str; int i = 100; stringstream i2str; // 將 int 轉成 string i2str << i; // 將數字 100 輸入 i2str i2str >> str; // 將數字 100 輸出至 str cout << str << endl ; 引入標頭檔 #include <sstream> 使用 sstream 物件 stringstream sstr; int j = 0; // 將 string 轉成 int stringstream str2i; str2i << str; // 將字串 "100" 輸入 str2i str2i >> j; // 將字串 "100" 輸出至 j cout << j << endl ; 21
轉換不同型態至字串 適用任何版本 double 與 string 互轉 #include <iostream> #include <sstream> using namespace std; int main(void) { string str; double d = 23.7; stringstream d2str; // 將 int 轉成 string d2str << d; // 將數字 23.7 輸入 i2str d2str >> str; // 將數字 23.7 輸出至 str cout << str << endl ; 引入標頭檔 #include <sstream> 使用 sstream 物件 stringstream sstr; double f = 0; // 將 string 轉成 int stringstream str2f; str2f << str; // 將字串 "23.7" 輸入 str2i str2f >> f; // 將字串 "23.7" 輸出至 f cout << f << endl ; 22
轉換不同型態至字串 重複使用 stringstream 物件 //... int main(void) { string str; double d = 23.7; stringstream coverter; // 將 int 轉成 string coverter << d; // 將數字 23.7 輸入 coverter >> str; // 將數字 23.7 輸出 cout << str << endl ; coverter.str(""); coverter.clear(); 清空 stringstream 物件 引入標頭檔 #include <sstream> 使用 sstream 物件.str("input");.clear(); double f = 0; // 將 string 轉成 int coverter << str; // 將字串 "23.7" 輸入 coverter >> f; // 將字串 "23.7" 輸出 cout << f << endl ; 23
串接多種型態至字串 引入標頭檔 #include <sstream> #include <iostream> #include <sstream> using namespace std; int main(void) { // 串接字串 & 整數 & 浮點數 使用 sstream 物件 stringstream sstr; // 方法 1: 轉型成字串 string filename = "test_data_" + to_string(long long(100)) + "_" + to_string(long double(20.7)); cout << filename <<endl; // 方法 2: 用 sstream 物件, 以字串流形式串接 stringstream sstr_fname; sstr_fname << "test_data_" << 100 << "_" << 20.7 ; cout << sstr_fname.str() << endl; 24
練習 2 1. 假設有一個包含 10 個元素的陣列 a[10], 嘗試在不需其他陣列的幫助下, 將這個陣列元素反轉 2. 嘗試在不需其他陣列的幫助下, 將陣列中 a[2] 至 a[7] 的元素反轉 3. 隨機產生兩個介於 0-9 的變數 r1 與 r2, 且此兩個變不可相同, 嘗試在不需其他陣列的幫助下, 將陣列中 a[r1] 至 a[r2] 的元素反轉 1 2 3 4 5 6 7 8 9 10 a[0] a[1] a[2] a[3] a[4] a[5] a[6] a[7] a[8] a[9] 1 2 3 7 6 5 4 8 9 10 a[0] a[1] a[2] a[3] a[4] a[5] a[6] a[7] a[8] a[9] 25
作業 TSP 問題 : 2-opt 延續練習 3, 隨機產生兩個介於 0-9 的變數 r1 與 r2, 且此兩個變不可相同, 嘗試在不需其他陣列的幫助下, 將陣列中 a[r1] 至 a[r2] 的元素反轉 但在作業中需選若選擇的 r1 與 r2 使得 r1-r2 > 10- (r1-r2), 則反轉 r1 與 r2 外的陣列範圍, 如圖所示 case1: r1-r2 <= 10- (r1-r2) 1 2 3 4 5 6 7 8 9 10 a[0] a[1] a[2] a[3] a[4] a[5] a[6] a[7] a[8] a[9] case2: r1-r2 > 10- (r1-r2) 1 2 3 7 6 5 4 8 9 10 a[0] a[1] a[2] a[3] a[4] a[5] a[6] a[7] a[8] a[9] 1 2 3 4 5 6 7 8 9 10 a[0] a[1] a[2] a[3] a[4] a[5] a[6] a[7] a[8] a[9] 2 1 10 4 5 6 7 8 9 3 a[0] a[1] a[2] a[3] a[4] a[5] a[6] a[7] a[8] a[9] 26