序文
ジョセフの指輪は古くからある興味深い問題であり、人々の間の生死を賭けた争いが関係しており、人々の長期的な思考と探求を呼び起こしてきました。この問題はさまざまな方法で解決できますが、それぞれに独自の長所と短所があります。
配列を使用してジョセフ リングを実装すると、人員の順序を簡単かつ直観的に表すことができますが、配列サイズの静的な制限とデータ コピーの低い操作効率の影響を受けます。ただし、単一リンク リストの実装を使用すると、実行時にジョセフ リングのサイズを動的に調整し、ポインターの更新を通じてノードを削除できるため、効率が向上します。
さらに、ジョセフ リング問題は数式で解く方が効率的で、データ構造を構築したり走査したりする必要がなく、最後の生存者の数は単純な数学的計算で取得できます。このアプローチは、より大きな問題にうまく機能し、非常に効率的です。
各実装には独自の利点があり、適切なものを選択するのは実際のニーズによって異なります。いずれにせよ、ジョセフ リング問題を解決するには、数学とアルゴリズムへの興味と探求を刺激しながら、数学的思考とプログラミング スキルが必要です。
ヨセフの指輪の歴史的背景
ヨセフス問題は、古代ユダヤ人の歴史家フラウィウス・ヨセフスにちなんで名付けられた古代の数学の問題です。伝説によると、ヨセフスはユダヤ人の将軍で、ローマ軍の包囲下でユダヤ人の要塞に閉じ込められました。
伝統的なヨセフスの記述によると、彼と39人の同胞は洞窟に閉じ込められました。最後に残った者として、彼らはローマ人の捕虜になるよりは自殺したほうがいいと決心しました。そこで彼らは輪になって立ち、一人から数え、指定された数まで数えるごとにその人を殺し、次の人から数え始めることにしました。これは、一人だけが残り、彼だけが生き残るまで続きます。
この問題の中心は、どのポジションが最後に生き残るかを決定することです。ジョセフスは、彼自身の回想と経験に基づいて解決策を提案します。彼の説明によると、彼は7番目の位置に立っていました、つまり、円の中で7人目まで数えたとき、彼は最後の生存者になるでしょう。
配列メソッドで解決する
C 言語を使用してジョセフ リングを実現する場合、配列を使用して人員の順序を表し、ループと条件文を通じて殺害プロセスをシミュレートできます。詳細な説明は次のとおりです。
#include <stdio.h>
#define MAX_SIZE 100
// 约瑟夫环函数
int josephus(int n, int k)
{
int circle[MAX_SIZE]; // 用数组表示约瑟夫环
int i, index, count;
// 初始化约瑟夫环
for (i = 0; i < n; ++i)
{
circle[i] = i + 1;
}
index = 0; // 从第一个人开始
count = 0; // 计数器
// 开始杀人循环,直到只剩下一个人
while (n > 1)
{
count++;
// 数到第k个人就杀掉他
if (count == k)
{
// 打印被杀的人的编号
printf("杀死第 %d 个人\n", circle[index]);
// 将被杀的人从约瑟夫环中移除
for (i = index; i < n - 1; ++i)
{
circle[i] = circle[i + 1];
}
count = 0; // 重置计数器
n--; // 约瑟夫环的人数减一
}
index++; // 移向下一个人
// 当到达约瑟夫环的末尾时,回到开始位置
if (index == n)
{
index = 0;
}
}
// 返回最后幸存者的编号
return circle[0];
}
int main()
{
int n, k;
int survivor;
printf("请输入约瑟夫环的人数n:");
scanf("%d", &n);
printf("请输入每次数的数字k:");
scanf("%d", &k);
survivor = josephus(n, k);
printf("最后幸存者的编号是:%d\n", survivor);
return 0;
}
circle
このコードでは、最初にジョセフ リングを表す配列を宣言します。サイズは次MAX_SIZE
のとおりです。次に、ループを使用してジョセフのリング内の人々を初期化し、1 から n まで順番にランク付けします。
次に、変数index
とcount
変数を使用して殺害プロセスをシミュレートします。index
現在カウントされている人の位置と、count
すでにカウントされている人の数を表示します。
キル ループでは、最初にカウンターをインクリメントしcount
、次に k 人目に到達したかどうかを確認します。k 人目が数えられたら、殺された人の番号を出力し、その人をジョセフリングから外します。
その人を削除した後、その人を後ろに移動して空席を埋め、ジョセフリングの人数を 1 人減らす必要があります。
最後に、残り 1 人になるとループは終了し、最後の生存者の番号を返します。
main 関数では、ユーザーが入力したジョセフ リングの番号 n と各カウントの番号 k を受け取り、josephus
最後の生存者の番号を計算して出力する関数を呼び出します。
C言語シングルリンクリストソリューション
C 言語を使用してジョセフ リングを実現する場合、単結合リストを使用して人員の順序を表現し、ループと条件文を通じて人を殺すプロセスをシミュレートすることもできます。詳細な説明は次のとおりです。
#include <stdio.h>
#include <stdlib.h>
// 定义单链表节点结构
typedef struct Node
{
int data;
struct Node *next;
} Node;
// 创建一个单链表节点
Node *createNode(int data)
{
Node *newNode = (Node *)malloc(sizeof(Node));
newNode->data = data;
newNode->next = NULL;
return newNode;
}
// 创建约瑟夫环
Node *createJosephusCircle(int n)
{
Node *head = createNode(1);
Node *prev = head;
int i;
for (i = 2; i <= n; ++i)
{
Node *newNode = createNode(i);
prev->next = newNode;
prev = newNode;
}
prev->next = head; // 将末尾节点指向头节点形成循环
return head;
}
// 模拟杀人过程
int josephus(int n, int k)
{
Node *head = createJosephusCircle(n);
Node *current = head;
Node *prev = NULL;
int count = 0;
while (head->next != head)
{
count++;
if (count == k)
{
printf("杀死第 %d 个人\n", current->data);
prev->next = current->next;
free(current);
current = prev->next;
count = 0;
}
else
{
prev = current;
current = current->next;
}
}
int survivor = head->data;
free(head);
return survivor;
}
int main()
{
int n, k;
int survivor;
printf("请输入约瑟夫环的人数n:");
scanf("%d", &n);
printf("请输入每次数的数字k:");
scanf("%d", &k);
survivor = josephus(n, k);
printf("最后幸存者的编号是:%d\n", survivor);
return 0;
}
このコードでは、まず単一のリンク リスト ノード構造を定義します。この構造には、従業員番号を格納するフィールドと次のノードへのポインタがNode
含まれています。data
next
次に、createNode
新しい単一リンク リスト ノードを作成し、そのノードへのポインタを返す関数を定義します。
次に、createJosephusCircle
ジョセフ リングを作成する関数を作成しました。ノードを1からnまで順番に作成し、next
ポインタを使って接続します。最後に、next
エンド ノードのポインタをヘッド ノードに向けて、ループを形成します。
次に、josephus
殺害プロセスをシミュレートする関数を作成しました。ポインタを使用してcurrent
現在の人数を追跡し、prev
ポインタを使用して前のノードを記録して、人を殺害したときにリンク リストを更新できるようにします。変数を使用してcount
数え、k 人目が数えられると、殺された人の番号を出力し、リンクされたリストから削除します。
最後に、ヘッド ノードの番号を最後の生き残りとして取得し、メモリを解放します。
main 関数では、ユーザーが入力したジョセフ リングの番号 n と各カウントの番号 k を受け取り、josephus
最後の生存者の番号を計算して出力する関数を呼び出します。
単連結リストを使用してジョセフリングを実装するC言語コードの詳細説明です。
数式(再帰的方法)
数式を使用して、ジョセフの指輪の最後の生存者の数を計算します。これは再帰的または反復的に実行できます。反復アプローチを使用した詳細な説明は次のとおりです。
#include <stdio.h>
int josephus(int n, int k)
{
int survivor = 0;
int i;
// 从n=1的情况开始递推计算
for (i = 2; i <= n; ++i)
{
survivor = (survivor + k) % i;
}
// 因为编号从1开始,所以加1得到幸存者的编号
survivor += 1;
return survivor;
}
int main()
{
int n, k;
int survivor;
printf("请输入约瑟夫环的人数n:");
scanf("%d", &n);
printf("请输入每次数的数字k:");
scanf("%d", &k);
survivor = josephus(n, k);
printf("最后幸存者的编号是:%d\n", survivor);
return 0;
}
このコードでは、josephus
ジョセフ リング内の人数と各カウントの数をパラメーター n と k で表す関数を定義します。
反復することで、n=2 から開始し、各人が殺された後の次の人の数を計算します。survivor
計算中に生存者の数を追跡するために変数を使用します。初期値は 0 です。
反復の各ラウンドで、survivor
k を加算し、次に殺害される人の数を取得するために、人の総数 i を法として計算します。このようにして、最後の人だけが残るまで、次に殺される人を順番に見つけます。
最後に、survivor
ジョセフの指輪の人員番号付けスキームと一致するように 1 を追加し、最後の生存者の番号を返します。
main 関数では、ユーザーが入力したジョセフ リングの番号 n と各カウントの番号 k を受け取り、josephus
最後の生存者の番号を計算して出力する関数を呼び出します。
ジョセフリングを実現するためのC言語コードを数式を使って詳しく解説します。
これら 3 つの方法の長所と短所、および最適化スキームをまとめます。
以下に、ジョセフ リングの 3 つの実装の長所と短所、および考えられる最適化ソリューションをまとめます。
1. 配列の実装:
利点:
- シンプルかつ直観的で、理解と実装が簡単です。
- 保存速度とアクセス速度は高速で、配列インデックスを直接使用するためポインター操作は必要ありません。
欠点:
- 配列のサイズは静的であり、コンパイル時に決定する必要があるため、ジョセフ リングのサイズが制限されます。
- 人物を削除すると次の人物を前に進める必要があり、データのコピー作業が多くなり効率が悪い。
最適化可能なソリューション:
- 実行時に必要に応じてジョセフ リングのサイズを変更できるように、動的配列または動的にリンクされたリストを使用します。
2. 単一リンクリストの実装:
利点:
- 動的なメモリ割り当て。ジョセフ リングのサイズは実行時に動的に調整できます。
- ノードの削除操作ではポインタを更新するだけで済み、より効率的です。
欠点:
- ノード アクセスはリンク リストをトラバースすることによって実現する必要がありますが、これは配列のランダム アクセスよりも効率が低くなります。
- リンク リスト ノードには、ポインタ情報を保存するために追加のメモリ スペースが必要です。
最適化可能なソリューション:
- 二重リンクリストを使用すると、ノードのアクセス効率が向上します。
3. 数式の実現:
メリット:
- ジョセフ リングのデータ構造を構築してトラバースする必要はなく、計算は数式に直接基づいて行われるため、非常に効率的です。
- ジョセフ リングのサイズに制限されず、非常に大きな問題に適しています。
欠点:
- 最後に生き残った人の番号だけは取得できますが、殺された人の順序は取得できません。
- 一部の特殊な k 値では、周期法則が発生する場合があります。
最適化可能なソリューション:
- 数式の計算プロセスは、すでに最適解であるため、単純な最適化では改善できません。
一般に、適切な実装の選択は状況によって異なります。ジョセフ リングのサイズが小さく、完全な殺害命令を取得することが要件である場合は、配列または単一リンク リストを選択して実装できます。ジョセフリングのサイズが大きい場合や、最後の生存者の数だけを取得したい場合には、数式が最適解となります。さらに、特定の要件に応じて、最適化スキームを組み合わせて、実装の効率と柔軟性を向上させることもできます。
ジョセフリング問題の魅力は、数学、論理、プログラミングを組み合わせており、問題を解く過程で思考力を発揮するだけでなく、数学やコンピュータサイエンスの魅力を体験できることです。この問題は、精神を訓練する機会であると同時に、数学とアルゴリズムへの素晴らしい旅でもあります。学術研究でも日常生活でも、ジョセフ リング問題は私たちに楽しみとインスピレーションをもたらします。