記事ディレクトリ
ジョセフ問題とは何ですか?
ジョセフ問題は古典的な数学の問題であり、その一般形式は次のように説明できます。
- n 人 (1 から n までの番号が付けられています) が円卓の周りに座ります。
- 最初の人から数え始めて、m に報告する人が出てきます。
- 次に、デキューされた人の次の人から再び数え始め、m に報告した人が再びデキューされます。
- 最後の人が残るまでこのパターンを繰り返します。要求されるのは当選者の番号です。
解決
- 列挙する
列挙法を使用してすべての可能な計算をリストすると、時間と空間の複雑さが非常に大きくなるため、より効率的なアルゴリズムを見つける必要があります。
- 循環チェーンチーム
-
保管構造の観点から、チームはシーケンシャルチームとチェーンチームに分けられます。
シーケンシャル キューは1 次元配列を使用してキュー要素を固定容量で連続的に格納しますが、
チェーン キューの容量は事前に見積もることができず、動的に変更できます。チェーン チームでは、ヘッド ノードを設定します。ヘッド ポインターは常にヘッド ノードを指し、テール ポインターはキューの末尾要素を指します。 -
n 人をリンク リストで結び、循環行列に似た方法を使用してその数を報告します。
-
具体的には、現在のノードから番号が1つずつ通知されるたびに、番号がmに達した時点でそのノードを削除し、次のノードから再度番号が通知される。
-
このプロセスは n-1 回繰り返され、最後に残ったノードが勝者となります。
注: 効率を向上させるために、実装プロセスで採用する必要があるいくつかのトリックがあります。
- たとえば、ポインタを使用して現在のラウンドの最後のノードを記録し、不必要な走査を減らすことができます。
- さらに、リンク リストはノードを削除するときにノードの先行ノードを見つける必要があるため、一方向リンク リストの代わりに二重リンク リストを使用する必要があります。
コード
- C言語の実装:
#include<stdio.h>
#include<stdlib.h>
typedef struct LNode{
//链式队列的结点
int data;
struct LNode * next;
}LinkList;
int n = 41, m = 3;
//1、初始化循环单链表
LinkList *init_LinkList(LinkList *L){
int i = 1;
L = (LinkList*)malloc(sizeof(LinkList));
LinkList *p=L;
LinkList *pNew;
scanf("%d", &n);
if(n != 0) {
while(i <= n) {
pNew = (LinkList *)malloc(sizeof(LinkList));
pNew->data = i++;
p->next = pNew;
p = pNew;
}
pNew->next=L->next;
}
free(L); //删除头结点
return pNew->next; //返回第一个数的指针
}
void del_sque(int n,LinkList *p){
LinkList * t;
while(p!=p->next) {
for(int i=1;i<m-1;i++)
p=p->next;
printf("%d->",p->next->data);
t=p->next;
p->next=t->next;
free(t);
p=p->next;
}
printf("%d\n",p->data);
return;
}
int main(){
LinkList *Head = NULL;
Head = init_LinkList(Head); //创建无头结点的循环链表
del_sque(n,Head); //求解约瑟夫问题
return 0;
}
- Python の実装:
class ListNode:
def __init__(self, value):
self.value = value
self.next = None
self.prev = None
def josephus(n, m):
# 构建初始链表
head = ListNode(1)
cur = head
for i in range(2, n+1):
new_node = ListNode(i)
cur.next = new_node
new_node.prev = cur
cur = new_node
tail = cur # 记录末尾节点
# 进行删除操作,直至只剩1个节点
cur = head
while cur != cur.next:
# 找到m-1个节点
for _ in range(m-1):
cur = cur.next
# 删除当前节点
prev, next = cur.prev, cur.next
if prev:
prev.next = next
if next:
next.prev = prev
# 更新cur指针和tail指针
cur = next
tail = prev
return tail.value # 返回唯一留下的节点编号
# 测试代码
print(josephus(7, 3)) # 输出4