C語言約瑟夫環問題

2020-07-16 10:04:28
編號為 1,2,3,…,n 的 n 個人圍坐一圈,任選一個正整數 m 作為報數上限值,從第一個人開始按順時針方向報數,報數到 m 時停止,報數為 m 的人出列。從出列人的順時針方向的下一個人開始又從 1 重新報數,如此下去,直到所有人都全部出列為止。

演算法思想

每個人的編號存放在一個陣列 a 中,主函數中決定人數的個數以及報數的上限值 m,設計一個函數實現對應的操作。函數的形參有整型陣列 a、整數 n 和 m,n 用來接收傳遞的人數,m 用來接收報數上限,函數的返回值為空;函數體中輸出出列人的順序。

函數中利用迴圈存取陣列中 n 個元素,每次存取元素,設定內迴圈連續存取 m 個元素,元素存取的下標為 k,存取到第 m 個元素時,如果元素不是 0,此時輸出元素 a[k],再設定 a[k] 為 0,繼續存取後面的元素。

主函數中設定陣列 a,從鍵盤輸入 n 和 m,利用迴圈產生 n 的位置序號存放到陣列 a 中,呼叫函數實現相應的操作。

程式程式碼

#include <stdio.h>
#define N 100
int josef(int a[],int n,int m)
{
    int i,j,k=0;
    for(i=0;i<n;i++)
    {
        j=1;
        while(j<m)
        {
            while(a[k]==0)
            k=(k+1)%n;
            j++;
            k=(k+1)%n;
        }
        while(a[k]==0)
        k=(k+1)%n;
        printf("%d ",a[k]);
        a[k]=0;
    }
    return 0;
}

int main()
{
    int a[100];
    int i,j,m,n;
    printf("input n and m:");
    scanf("%d%d",&n,&m);
    for(i=0;i<n;i++)
        a[i]=i+1;
    printf("n output:n");
    josef(a,n,m);
    printf("n");
    return 0;
}

偵錯執行結果

15 個人圍坐在一起,報數上限為 4 時的出列順序如下所示:

input n and m:15 4

output:
4 8 12 1 6 11 2 9 15 10 5 3 7 14 13

100 個人圍坐在一 起,報數上限為 9 時的出列順序如下所示:

input n and m:100 9

output:
9 18 27 36 45 54 63 72 81 90 99 8 19 29 39 49 59 69 79 89 100 11 22 33 44 56 67
78 91 2 14 26 40 52 65 77 92 4 17 32 47 61 75 88 5 21 37 53 70 85 1 20 38 57 74
94 12 31 51 73 95 15 41 62 84 7 34 60 86 13 43 71 98 30 66 97 35 76 10 50 93 42
83 28 87 48 6 68 46 23 3 96 16 25 64 55 58 24 80 82

總結

① 程式由 main() 函數和 josef() 函陣列成,main() 函數呼叫 josef() 函數,用陣列名作為函數引數,在主函數和被呼叫函數中分別定義陣列。主函數執行到 josef(a,n,m) 語句時,將陣列 a 的首元素的地址傳遞給形引數組 a,程式轉去執行 josef(),形引數組 a 中的元素發生逆序排列,則實引數組 a 也隨之改變,當 josef() 執行完後,返回到主函數中繼續執行被調函數後面的語句。

② 範例中定義函數 josef() 解決問題的難點有兩個:一是如何求下一個出圈的人的位置;二是此人出圈後對這個人的位置如何處理。從第一個人開始報數,報到 m 時,此人出圈,設定變數 j,每次統計出圈的人數,當出圈人數到 m 時,重新開始統計。n 個人圍坐一圈,可看作環狀,設定 k 表示出圈人的下標,則出圈人的下標的計算可用“(k+l)%n”表示。對於第二個問題,首先將出圈人的位置列印輸出,然後將其位置元素設定為 0。

③ 陣列名作函數引數時,要求在被呼叫函數和呼叫函數中分別定義陣列,且形參和實參必須是型別相同的陣列。實參和形引數組是指向同一段地址空間的,當主函數執行時,這段空間由實引數組控制,當被呼叫函數執行時,這段空間由形引數組使用,被調函數執行結束後,該空間又交回給實引數組。

用陣列名作為函數引數時,形參與實參之間的傳遞方式為地址傳遞,因此,形引數組的改變會影響實引數組的內容。

C 編譯系統對形引數組的大小不做檢查,只是將實引數組的首地址傳給形引數組,所以形引數組可以不用指定大小。如範例中被呼叫函數的首部定義為 void josef(int a[], int n,int m),其中的整型陣列 a 的定義為 int a[],沒有給出陣列的具體大小。

④ 一維陣列名、多維陣列名都可以作為函數的引數,進行地址傳遞。