1、约瑟夫环问题:N个人围成一圈,从第一个开始报数,第M个将被淘汰,下一个重新从1开始报数,继续淘汰第M个,直到最后剩下一个。例如N=5,M=2,淘汰的顺序是:2,4,1, 5,最后获胜的是3
2、linux内核list.h:linux-5.12/drivers/gpu/drm/nouveau/include/nvif/list.h
3、代码清单:
.
├── include
│ └── list.h
├── log
│ ├── log_level_time.c
│ └── log_level_time.h
└── main.c
4、log文件夹下的log_level_time.c、log_level_time.h为自定义log打印,详情见上一篇博文"C语言实现带时间log打印及log级别设置"
5、代码解释
头文件及数据结构
1 #include <unistd.h>
2 #include <stdlib.h>
3 #include "include/list.h"
4 #include "log/log_level_time.h"
5
6 struct jos_s {
7 struct list_head list;
8 int man_num;
9 };
10
定义变量
11 int main(int argc, char* argv[])
12 {
13 int opt;
14 int cnt_all, key;
15 int i;
16 int cnt_off = 0;
17 struct jos_s *jos;
18 struct list_head head;
19
使用main函数参数进行log初始化,比如-v 1打印DEBUG 以上级别,-v -1打印WARN以上级别,不设置则默认打印INFO以上级别
20 log_init(argc, argv);
21
main函数参数传入M和N值
22 while((opt = getopt(argc, argv, ":c:k:")) != -1) {
23 switch (opt) {
24 case 'c':
25 cnt_all = atoi(optarg);
26 break;
27 case 'k':
28 key = atoi(optarg);
29 break;
30 default:
31 break;
32 }
33 }
34
35 LOG_INFO("cnt_all = %d, key = %d", cnt_all, key);
36
根据参数申请内存
37 jos = malloc(cnt_all * sizeof(struct jos_s));
38 if (jos == NULL) {
39 LOG_ERROR("malloc error");
40 return -1;
41 }
42
使用list.h的接口,建立约瑟夫环。其中INIT_LIST_HEAD初始化链表头。list_add_tail在链表尾部添加元素,并且新添加的最后一个元素指向链表头,即环形链表。
43 INIT_LIST_HEAD(&head);
44
45 for(i = 0; i < cnt_all; i++) {
46 jos[i].man_num = i + 1;
47 list_add_tail(&jos[i].list, &head);
48 }
49
模拟游戏规则,进行循环报数淘汰,直到剩下最后一个胜利者。其中list_for_each_entry_safe,为遍历链表接口。
50 struct jos_s *pos;
51 struct jos_s *n;
52
53 while (head.next != head.prev) {
54 list_for_each_entry_safe(pos, n, &head, list) {
55 cnt_off++;
56 if (cnt_off == key) {
57 cnt_off = 0;
58 LOG_DEBUG("del %d", pos->man_num);
59 list_del(&pos->list);
60 }
61 }
62 }
63
输出最后获胜者。其中list_entry,根据list找到struct。
64 pos = list_entry(head.next, struct jos_s, list);
65 LOG_INFO("the winner is: %d", pos->man_num);
66
67 free(jos);
68 return 0;
69 }
6、结果演示
十人,淘汰数是5,获胜者是3
$ ./jos -c 10 -k 5
[2021-05-24 00:59:14.567] [ main.c: 35] [INFO ] cnt_all = 10, key = 5
[2021-05-24 00:59:14.569] [ main.c: 65] [INFO ] the winner is: 3
加入参数-v 1打印DEBUG信息,输出淘汰顺序
$ ./jos -c 10 -k 5 -v 1
[2021-05-24 01:01:18.904] [ main.c: 35] [INFO ] cnt_all = 10, key = 5
[2021-05-24 01:01:18.907] [ main.c: 58] [DEBUG] del 5
[2021-05-24 01:01:18.908] [ main.c: 58] [DEBUG] del 10
[2021-05-24 01:01:18.912] [ main.c: 58] [DEBUG] del 6
[2021-05-24 01:01:18.914] [ main.c: 58] [DEBUG] del 2
[2021-05-24 01:01:19.028] [ main.c: 58] [DEBUG] del 9
[2021-05-24 01:01:19.028] [ main.c: 58] [DEBUG] del 8
[2021-05-24 01:01:19.028] [ main.c: 58] [DEBUG] del 1
[2021-05-24 01:01:19.028] [ main.c: 58] [DEBUG] del 4
[2021-05-24 01:01:19.028] [ main.c: 58] [DEBUG] del 7
[2021-05-24 01:01:19.028] [ main.c: 65] [INFO ] the winner is: 3
一亿人,淘汰数是5,最后获胜者是35852477
./jos -c 100000000 -k 5
[2021-05-23 15:20:37.278] [ main.c: 35] [INFO ] cnt_all = 100000000, key = 5
[2021-05-23 16:41:10.463] [ main.c: 65] [INFO ] the winner is: 35852477
7、list.h代码可从linux内核源码获取,本文使用的linux5.12源码的linux-5.12/drivers/gpu/drm/nouveau/include/nvif/list.h
------谢谢阅读------