實驗3_C語言函式應用程式設計

顾谢耀202383290109發表於2024-10-29

任務一:

#include <stdio.h> 
char score_to_grade(int score); 
int main() { 
    int score; 
    char grade; 
    while(scanf("%d", &score) != EOF) { 
        grade = score_to_grade(score);
        printf("分數: %d, 等級: %c\n\n", score, grade); 
    }     
    return 0; 
} 

char score_to_grade(int score) { 
    char ans; 
    switch(score/10) { 
        case 10: 
        case 9: ans = 'A'; break; 
        case 8: ans = 'B'; break; 
        case 7: ans = 'C'; break; 
        case 6: ans = 'D'; break; 
        default: ans = 'E'; 
    } 
    return ans;
}

問題1:函式score_to_grade的功能是根據傳入的整數分數score,將其轉換為對應的等級字元並返回。形參型別是int,即接受一個整數引數。返回值型別是char,即返回一個字元。

問題2:有問題。問題在於當score/10的值為 9 或者 10 時,執行完ans = "A"後,沒有break語句,程式會繼續執行下一個case,同理對於其他的case也存在這個問題。這樣會導致最終返回的等級可能不是預期的結果。例如,如果score 95,理論上應該返回等級A,但由於沒有break,程式會繼續執行後續的case,直到遇到break或者整個switch語句結束,這樣可能會返回B、C、D或E等錯誤的等級。此外,ans被定義為字元型別變數,而在修改後的程式碼中,給ans賦值用了雙引號括起來的字串常量,這是不匹配的型別賦值,在 C 語言中會導致編譯錯誤。

任務2:

#include <stdio.h> 
int sum_digits(int n); // 函式宣告 
#include <stdio.h> 
int sum_digits(int n); // 函式宣告 
int main() { 
    int n; 
    int ans; 
    while(printf("Enter n: "), scanf("%d", &n) != EOF) { 
        ans = sum_digits(n); // 函式呼叫 
        printf("n = %d, ans = %d\n\n", n, ans); 
    } 
    return 0; 
} 
// 函式定義 
int sum_digits(int n) { 
    int ans = 0;
    while(n != 0) { 
        ans += n % 10; 
        n /= 10; 
    } 
    return ans; 
}

問題 1:函式sum_digits的功能是計算給定整數n的各位數字之和並返回。

問題 2:可以實現同等效果。

兩種實現方式背後的演算法思維區別如下:

(1)第一種實現方式是採用迴圈的演算法思維。透過不斷地取n的末位數字累加到ans中,然後將n除以 10 去掉末位數字,重複這個過程直到n變為 0。這種方式是一種迭代的過程,逐步處理整數的每一位數字。

(2)第二種實現方式是採用遞迴的演算法思維。如果輸入的整數n小於 10,直接返回n,因為一個一位數的各位數字之和就是它本身。如果n大於等於 10,則先遞迴地計算n/10的各位數字之和,再加上n的末位數字n%10。這種方式是將問題不斷分解為更小的子問題,直到子問題可以直接求解,然後逐步合併子問題的解得到最終結果。

任務3:

#include <stdio.h>
int power(int x, int n); // 函式宣告
int main() {
    int x, n;
    int ans;
    while(printf("Enter x and n: "), scanf("%d%d", &x, &n) != EOF) {
        ans = power(x, n); // 函式呼叫
        printf("n = %d, ans = %d\n\n", n, ans);
    }
    return 0;
}
// 函式定義
int power(int x, int n) {
    int t;
    if(n == 0)
    return 1;
    else if(n % 2)
    return x * power(x, n-1);
    else {
        t = power(x, n/2);
        return t*t;
    }
}

問題 1:函式power的功能是計算整數x的n次方並返回結果。

問題 2:函式power是遞迴函式。遞迴模式為:當n為 0 時,返回 1;當n為奇數時,返回x * power(x, n - 1);當n為偶數時,先計算t = power(x, n/2),然後返回t * t。

數學公式模型為

power(x, 0)= 1;

n為奇數時,power(x, n)= x* power(x, n -1);
n為偶數時,power(x, n)= power(x, n/2)* power(x, n/2)。

任務4:

#include <stdio.h>

int is_prime(int n) {
    if (n <= 1) return 0;
    for (int i = 2; i * i <= n; i++) {
        if (n % i == 0) return 0;
    }
    return 1;
}

int main() {
    int count = 0;
    printf("100 以內的孿生素數:\n");
    for (int i = 1; i < 100; i++) {
        if (is_prime(i) && is_prime(i + 2)) {
            printf("%d %d\n", i, i + 2);
            count++;
        }
    }
    printf("100 以內的孿生素數共有 %d 個\n", count);
    return 0;
}

任務5:

#include <stdio.h>
#include <stdlib.h>

void hanoi(unsigned int n, char from, char temp, char to, int *count);
void moveplate(unsigned int n, char from, char to, int *count);

int main() {
    unsigned int n;
    while (scanf("%u", &n)!= EOF) {
        int count = 0;
        hanoi(n, 'A', 'B', 'C', &count);
        printf("一共移動了%d次。\n", count);
    }
    system("pause");
    return 0;
}

void hanoi(unsigned int n, char from, char temp, char to, int *count) {
    if (n == 1) {
        moveplate(n, from, to, count);
    } else {
        hanoi(n - 1, from, to, temp, count);
        moveplate(n, from, to, count);
        hanoi(n - 1, temp, from, to, count);
    }
}

void moveplate(unsigned int n, char from, char to, int *count) {
    printf("%u:%c-->%c\n", n, from, to);
    (*count)++;
}

任務6:

迭代

#include <stdio.h>

int func_iterative(int n, int m) {
    if (m > n || m < 0) return 0;
    if (m == 0 || m == n) return 1;

    int result = 1;
    for (int i = 1; i <= m; i++) {
        result = result * (n - (m - i)) / i;
    }
    return result;
}
int main() {
    int n, m;
    int ans;
    while (scanf("%d%d", &n, &m)!= EOF) {
        ans = func_iterative(n, m);
        printf("n = %d, m = %d, ans = %d\n\n", n, m, ans);
    }
    return 0;
}

遞迴

#include <stdio.h>

int func_recursive(int n, int m) {
    if (m > n || m < 0) return 0;
    if (m == 0 || m == n) return 1;
    return func_recursive(n - 1, m - 1) + func_recursive(n - 1, m);
}

int main() {
    int n, m;
    int ans;
    while (scanf("%d%d", &n, &m)!= EOF) {
        ans = func_recursive(n, m);
        printf("n = %d, m = %d, ans = %d\n\n", n, m, ans);
    }
    return 0;
}#include <stdio.h>
#include <stdlib.h>

int print_charman(int n);

int main() {
    int n;
    printf("Enter n: ");
    scanf("%d", &n);
    print_charman(n); 
    return 0;
}

任務7:

int print_charman(int n){
    for(int t=0;t<n;t++){
        int i=(n-t)*2-1;
        int j=t;
        for(j;j>0;j--){
            printf("      ");
        }
        for(i;i>0;i--){
            printf(" O    ");        
            }
        printf("\n");
        j=t;
        for(j;j>0;j--){
            printf("      ");
        }
        i=(n-t)*2-1;
        for(i;i>0;i--){
            printf("<H>   ");        
            }
        printf("\n");
        j=t;
        for(j;j>0;j--){
            printf("      ");
        }
        i=(n-t)*2-1;
        for(i;i>0;i--){
            printf("I I   ");        
            }
        printf("\n");
        }
    return 0; 
}

實驗總結

知識歸納:

1.函式宣告與定義:我學會了如何正確地宣告和定義函式,明確函式的輸入引數型別和返回值型別,確保在主函式中能夠正確呼叫。理解了函式宣告是為了讓編譯器提前知道函式的存在,而函式定義則是具體實現函式功能的程式碼部分。

2.迴圈結構:for迴圈和while迴圈的靈活運用。在列印字元小人陣列的程式中,使用for迴圈控制行數和列數,精確地輸出特定格式的圖形。透過迴圈的條件判斷,如迴圈變數的起始值、終止值和步長的設定,實現不同的輸出效果。

3.遞迴與迭代:遞迴方式:理解了遞迴的概念和實現方法。在計算組合數和漢諾塔問題中,遞迴函式透過不斷呼叫自身來解決問題,將複雜問題分解為更小的子問題。但也需要注意遞迴的深度,避免棧溢位。

迭代方式:掌握了迭代的方法,透過迴圈和變數的更新來逐步計算結果。與遞迴相比,迭代通常更高效,尤其是在處理大規模資料時。

體會感受:

在解決不同問題時,需要根據問題的特點選擇合適的演算法。例如,計算組合數可以使用遞迴或迭代方式,兩種方法各有優缺點。遞迴方式程式碼簡潔,但可能會導致棧溢位;迭代方式效率高,但程式碼相對複雜一些。在列印字元小人陣列的問題中,透過分析圖形的規律,選擇合適的迴圈結構和條件判斷來實現輸出。透過遞迴方式解決問題,讓我深刻體會到了程式設計的簡潔之美。遞迴函式能夠將複雜問題分解為簡單的子問題,使程式碼更加清晰易懂。但同時也意識到遞迴的侷限性,如棧空間的限制和效能問題。

相關文章