逆约瑟夫问题
约瑟夫问题可以说十分经典,其没有公式解也是广为人知的~
目录
前言
一、约瑟夫问题与逆约瑟夫问题
1.约瑟夫问题
2.逆约瑟夫问题
二、思考与尝试(显然有很多失败)
问题分析
尝试一:递归/递推的尝试
尝试二:条件约束下的求解
尝试三:遍历
尝试四:可行性与最优性剪枝优化
尝试五:可行特解的寻找方法
三、总结评价
四、核心代码
1. x==s情况下,找特解
2. x!=s情况下,找特解(效率很低)
3. 遍历找特解(实话是,实际感觉花费时间很少,比上面好多了)
总结
前言
本博客源于一次作业。老师要求我们逆求约瑟夫问题,着实困扰许久。
一、约瑟夫问题与逆约瑟夫问题
1.约瑟夫问题
有这样一个游戏,n个同学围绕成一个环,分别标号1.2.3...n,并按顺序排列,显然1与2和n相邻。从第s个同学开始以此报数,当报数为m时,报数的同学离开环。剩下的接着从1开始报数,如上循环,直至只剩一人。最后一个人获胜。
约瑟夫问题的解法有很多,递归/模拟是两个大类。在这里的解法就不赘述了。
2.逆约瑟夫问题
假设你正在游玩约瑟夫游戏,从你开始报数,游戏规则与课上讲述一致,现在你想确保你是最后一个,即获胜的玩家。如果由你设置m(即每报几个数出列一人),你应该如何设置m来确保自己的胜利?
二、思考与尝试(显然有很多失败)
问题分析:
正常的约瑟夫问题,由人数n,报数长m,开始位次s,来推导出最终出列的人编号x。求解过程是自然、顺势而为的。而此约瑟夫进阶问题,则是通过最终出列人的编号、开始位次(固定从第一个开始)、人数n来求得报数长m。是逆着自然进行的操作,所以预测是困难的。为此我进行了一些尝试,试图得出解析解,但显然这与约瑟夫问题本身一样,解析解是不可能的。其他的一些尝试,虽然都历经了失败,但是对于问题本身有了更加深入的理解,对于朴素方法——枚举判断m,有了一些优化的考量。
尝试一:递归/递推的尝试
从正向的约瑟夫问题出发,约瑟夫问题可以被精妙地转化为递归问题,将复杂的情况转化为规模小的问题。逆约瑟夫问题同样是有着较大规模、较类似的子问题。因此尝试讲逆约瑟夫问题转化为递归问题。然而,在尝试中遇到了困难。
设
,为了求得m,考虑低复杂度的子问题,即当
或n小到问题可以求解,是必备的条件。此外,还需要问题能够往低阶过渡,即公式可以写成
的形式。经过思考,发现这两种条件都无法满足。
同时,由于m、n、x在同一问题的解中的唯一性,很难将类似于
往更高的
进行递推,尝试用递推也困难重重。
尝试二:条件约束下的求解
对于固定的n、x,都有m与之对应,那么n、x对于m有何等约束呢?
约瑟夫问题是一个环问题,对这一类问题,有一种解法普遍适用。那就是将环拆成链,把这条链复制后首位连接,这就达到了化环为链的效果。只是约瑟夫问题下,对于一段链上“踢出该成员”的操作,对于所有的子链上对应的成员,都要进行“踢出”操作。于是想到,用操作的长度的不同表示,来对m进行限制。
设共k+1段子链,那么操作长度length通过子链个数与位置x来计算有:
![]()
同时,从踢人的轮数z以及考虑到踢出不被计数因此实际跳过的人数c考虑有:
![]()
因此总有:
![]()
然而仔细观察结构,总有一组特解使上式成立:
![]()
由于c的不可确定性,采用对c、k、z遍历的方法,尝试找到满足使m为整数的解,发现只能得出以上特解。

因此,如此考虑,实际上也是无意义的。
尝试三:遍历
为此,从以上两次尝试,可以发现,正面解决逆约瑟夫问题,不亚于反面解决约瑟夫问题。其实,最朴素最简单的一类想法,就是遍历。我们已经解决了在给定n、s、m求x的问题,在解决给定n、s=1、x求m的问题时,可以通过遍历m的可能取值,将得出来的x与给定的x进行比较。如果相等,就说明此时的m是合理的,或者说此时的m就是答案。
下面对这种方法进行复杂度分析。
考虑最好情况,即m第一次取值就是答案,此时复杂度为一次约瑟夫问题求解的复杂度,O(n2)。最坏的情况下,可能要取n次,复杂度为O(n3)。平均复杂度也为O(n3)。由于遍历的方法是基于多次的约瑟夫问题求解,时间复杂度比较高。这也是为什么一开始不从遍历枚举这种最朴素直接可行的方法着手的原因。
从约瑟夫问题本身过程考虑,结合逆约瑟夫问题的要求,考虑进一步优化的方法。
尝试四:可行性与最优性剪枝优化
注意到逆约瑟夫问题的要求,即最后一名出队的同学是给定的x。这意味着如若x不是最后一个出队,那么此时进行评测的m也不是符合要求的答案。对逆约瑟夫问题的求解,就好比是一棵具有n个枝的树,某一些枝不可能得到答案,就直接将其抛弃。因此我们可以对原约瑟夫问题进行可行性剪枝,来实现对逆约瑟夫问题的优化。此外,如若已经找到可行的m使得问题得解,就不需要继续进行后续m+1、m+2……的评判了。认定第一个m就是最优答案,不去考虑剩下的答案,即为最优性剪枝。
具体剪枝策略:
对于每一位出列的人,同时记录出列的总的数量。如若x出列且出列的人总数不为n,即还有人未出列、x不是最后一个出列,此时就直接终止当前约瑟夫问题求解过程,并转入对m+1的约瑟夫问题解的判定。此外,找到一个答案,直接退出求解序列。
程序求解

运行编写好的程序,随机输入人数n、开始位次s、获胜位置x,求解m。
这里随机取n=1234 s=24 x=51 程序“Anti-Josephus.exe”解得m=54
然后用约瑟夫问题程序“Josephus.exe”进行检验:输入n=1234,m=54,s=24,可以看到,最后一个出列的是51,与答案相等。其他几次测试也都如此。
当然,对于某些特定的n、s、x,不存在m使得其成立。
![]()
(实际上,由于人会退出,所以m>n也是合理的,因为m=m%n不成立。因此,由无穷的m对应有限的n,几乎可以说任何一个位置都由多个m作为解。这里的程序在尝试的时候,下意识认为m<=n,是有问题的。不过,之后的尝试过程中都修正过来了)
尝试五:可行特解的寻找方法
本来已经提交作业。课上,老师说已经交的部分作业他看过了,绝大多数都是遍历。于是他做了一些提示。给同学们的思路开拓作用很大。事实上,寻求特解这一思路,之前竟一直没有出现在我的脑海中。
上课教员给予了一种新的思路,或者说对题目的含义有了更为精确的理解:第x位次的人为获胜,需要找到m使得他获胜。由于约瑟夫问题跳过了已经点名过的人,或者说,已经点名过的人已经被踢出序列。因此,并不意味着km+b=b的成立。所以,可以说,m的值域远远超过人数n。
然而最后一名获胜者只有一个,只有n种可能。这就意味着,有多个不同的m使得第x位的人获胜。而我们只需要求出一个,这一个可以是平凡普通解,也可以是特解。寻求通解的算法/公式,难度很高,上述也进行了多次尝试。然鹅,寻求特解,意味着我们可以用一些规律,找到那些富有某种特征的解。
考察游戏进行到最后只剩两个人的情况。若由第x个位置的人开始报数,那么需要报2/4/6…即2的倍数次,即可获胜。若由另一个人开始报数,那么需要报1/3/5…即奇数次,位置x的人即可获胜。为了方便求解,我们考虑从x位置的人首先报数的情况。之所以认为选择x开始报数这种情况优于另一种,是因为:可以将小规模的问题推广,即,不论多少人,都从首先x开始报数,而每一种规模的问题,都有近似的解法。
约定:①设Ni为倒数第i轮,一共进行n轮。 ②每轮都由x位置首先开始报数。③表示X,Y,Z...的最小公倍数。
i=n时,那么x第n轮出局,有
![]()
i=n-1时,x在下一轮出局,为满足约定②,
![]()
i=n-2时,还剩三个人,为下一轮满足约定②,
![]()
……
i=2时,还有n-1人,为下一轮满足约定②,
![]()
i=1时,还有n人,然而第一轮明确规定从s开始报数,为下一轮满足约定②,
![]()
或者写成
![]()
综合上述n个约束:
![]()
![]()
只要满足上式两个式子,m就是满足题目含义的一个解。(自然有更多的解不满足条件,但这里只寻求找到解,或者说,找到特解的情况。)
令人欣喜的是:题目中要求从第x人开始报,![]()
那么上式就化为
![]()
![]()
两式合并为(实际上,这种策略下的最小解即
):
![]()
得到这个公式,如果“不顾一切”的话,我们甚至可以O(1)地得到一个特解m地值——n!
然而如果要将m带入程序进行验算,草率地令m=n!常常会导致数太大而程序崩溃(例如C++,只用基本类型,不考虑高精度等算法) 。因此,我们需要进行筛选,找到1~n的(最小)公倍数。
值得庆幸的是,我们已经有了最小公倍数的求解算法。不过直接应用还是可能面临时间复杂度太高的问题(n>=25时已经开始凸显)。

所以,权衡公倍数大小与算法复杂度,给出恰当的解法,是进一步考虑的问题。在此不做讨论。
三、总结评价:
尝试了多种思路,企图找到“捷径”,压缩时间复杂度,可惜失败了。
从朴素的遍历思想出发,能够保证解出问题(m的值或无解),但是时间复杂度令人担忧。
因此,为了尽量压缩时间复杂度,对逆约瑟夫问题的子问题,即单个约瑟夫问题,进行了可行性剪枝与最优性剪枝,尽可能的节省时间开支。
在教员进行提点之后,进行了尝试五,并写出了相应代码。虽然当x!=s的时候效率低下,但对于题目要求s=x而言,速度还是很可观的。不过由于c++类型大小限制,一般n取20左右,已经是运行极限了。(受long long/int类型大小限制。当然,可以通过高精度等算法来改进,但是时间效率依然堪忧)
最后,希望通过拓宽眼界、拓展能力,掌握更多数据结构与算法知识,找到解决逆约瑟夫问题的更好方法。
四、核心代码
1. x==s情况下,找特解
//s==x下的代码
int findM(int n, int s)
{typedef unsigned long long ull; ull m1=n;for(int i=n-1;i>=2;--i) {if(m1%i==0)continue;ull a1=m1,a2=i;ull c = 0;while(c = a1 % a2){a1 = a2;a2 = c;}m1=m1*i/a2;}//cout<<m1<<endl;return m1;
}
2. x!=s情况下,找特解(效率很低)
#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
using namespace std;
typedef long long ll;//求最大公约数
inline ll gcd(ll a,ll b) {while(b^=a^=b^=a%=b);return a;
}
//求a,b的最小公倍数
ll lcm(ll a,ll b);
int main()
{ll m1;ll n,s,x;cout<<"输入n\\s\\x:"; //题目要求,即输入x==scin>>n>>s>>x;m1=n-1;for(int i=n-2;i>=2;--i) //m0即lcm(1,2,3...n-1){if(m1%i==0)continue;m1=lcm(m1,i);}ll m2=x-s;while(1){if(m2>m1&&m2%m1==0){cout<<"m的一个特解是:"<<m2<<endl;break;}m2+=n;}return 0;
}
ll lcm(ll a,ll b)
{return a*b/__gcd(max(a,b),min(a,b));
}
3. 遍历找特解(实话是,实际感觉花费时间很少,比上面好多了)
调用了自己编写的Array类,进行约瑟夫问题的模拟。实际上就是将约瑟夫问题迭代多次,修改过来的。
#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include"Array.h"
#include"Array.cpp"
using namespace std;void Josephus(Array<int>& P,int n,int m,int x,bool& key,int s)
{int k=1;for(int i=0;i<n;++i){P.Insert(k,i);k++;}int s1=s;for(int j=n;j>=1;j--){s1=(s1+m-1)%j;if(s1==0)s1=j;if(P.Getnode(s1-1)==x) //剪枝{if(j!=1)return; //可行性剪枝else key=true; //最优性剪枝}int w=P.Getnode(s1-1);P.Remove(s1-1);P.Insert(w,n-1);}
}Array<int> a;
int n,s,x;
int main()
{cout<<"人数n,从第s位开始遍历,获胜位置x:";cin>>n>>s>>x;bool key=false;int m_ans=0;while(!key) //如果没找到答案,继续评判下一个m{m_ans++;if(m_ans>n)break;Josephus(a,n,m_ans,x,key,s);a.clear(); //清空a的元素,为下一轮做准备}if(key)cout<<m_ans; //如果找到答案,输出它else cout<<"No answer!"; //如果没有找到答案,说明无解return 0;
}
总结
还是比较新颖/拓宽思路的。
读者需要什么Array.h/Array.cpp文件/第一份函数代码的完整程序代码 都可以在评论区或者私信我(我一般不看私信,社区通知太多了也不点掉。。)
相关文章:
逆约瑟夫问题
约瑟夫问题可以说十分经典,其没有公式解也是广为人知的~ 目录 前言 一、约瑟夫问题与逆约瑟夫问题 1.约瑟夫问题 2.逆约瑟夫问题 二、思考与尝试(显然有很多失败) 问题分析 尝试一:递归/递推的尝试 尝试二:条件…...
MySQL之三大日志(更新中)
MySQL之三大日志(更新中) MySQL日志记录着数据库运行过程中的各种信息,包括:错误日志、普通查询日志、慢查询日志、二进制日志、中继日志、事务日志等。 综合上一篇《MySQL之"幻读"问题》涉及到事务,本文主…...
如何使用EvilTree在文件中搜索正则或关键字匹配的内容
关于EvilTree EvilTree是一款功能强大的文件内容搜索工具,该工具基于经典的“tree”命令实现其功能,本质上来说它就是“tree”命令的一个独立Python 3重制版。但EvilTree还增加了在文件中搜索用户提供的关键字或正则表达式的额外功能,而且还…...
北京移动CM311-5s-ZG_GK6323V100C_2+8_免拆一键卡刷固件包
北京移动CM311-5s-ZG_GK6323V100C_28_免拆一键卡刷固件包 特点: 1、适用于对应型号的电视盒子刷机; 2、开放原厂固件屏蔽的市场安装和u盘安装apk; 3、修改dns,三网通用; 4、大量精简内置的没用的软件,…...
JavaScript(1)
JavaScript简介 JavaScript是一门跨平台、面向对象的脚本语言,用来控制网页行为的,它能使网页可以交互。 JavaScript引入方式 1、内部脚本 将js代码定义在HTML页面中,在HTML中,JavaScript代码必须位于<script>与</scrip…...
阿里云云原生每月动态 | 聚焦实战,面向开发者的系列课程全新上线
作者:云原生内容小组 云原生是企业数字创新的最短路径。 《阿里云云原生每月动态》,从趋势热点、产品新功能、服务客户、开源与开发者动态等方面,为企业提供数字化的路径与指南。 本栏目每月更新。 趋势热点 《云原生实战指南》白皮书发布 …...
Goby 征文大擂台,超值盲盒等你来!
001 Goby 技术征文正式启动 Goby 致力于做最好的网络安全工具。为了促进师傅们知识共享和技术交流,现发起关于 Goby 的技术文章征集活动! 欢迎所有师傅们参加,分享您的使用经验或挖洞窍门等,帮助其他人更好地了解和利用 Goby。 …...
NLP - langid 语种识别
文章目录一、关于 langid二、基本使用Normalization多个语言中选择一个三、训练模型1、需要2、工具是3、过程4、代码调用自定义模型一、关于 langid https://github.com/saffsd/langid.py 用于检测语言 二、基本使用 import langidlangid.classify("This is a test"…...
liquibase学习和使用
文章目录liquibase学习介绍数据库更新日志和数据库更新日志锁定相关概念changelogchangeset的属性preconditionsql样例Contextssql样例Labelsql样例文件格式sql样例其他格式用的时候在补充跟踪表DATABASECHANGELOGLOCK (数据库更改日志锁定表)DATABASECH…...
redhawk:Low Power Analysis
1.rush current与switch cell 在standby状态下为了控制leakage power我们选择power gating的设计方式,使用power switch cell关闭block/power domain的电源。 power switch的基本介绍可见: 低功耗设计-Power Switch power switch的table中有四种状态,…...
24- 深度学习的模型保存和加载 (TensorFlow系列) (深度学习)
知识要点 keras 保存成hdf5文件, 1.保存模型和参数, 2.只保存参数 1.保存模型和参数 save_modelcallback ModelCheckpoint2. 只保存参数 save_weightscallback ModelCheckpoint save_weights_only True 保存模型: 案例数据: Fashion-MNIST总共有十个类别的图像model.save_w…...
【Echarts图例点击事件】自定义Echarts图例legend点击事件(已解决)
目录先睹为快(效果)1、实现Echarts多条曲线2、点击echarts触发接口请求2.1 先默认隐藏部分数据2.2 自定义legend图例点击事件3、源码下载地址(解压即用)**【写在前面】**这下我又不得不说了,还是客户现场使用时想查询一…...
uniapp-首页配置
为了获取到后台服务器发来的数据,需要配置相应的网络地址。位置在main.js入口文件中。 import { $http } from escook/request-miniprogramuni.$http $http // 配置请求根路径 $http.baseUrl https://api-hmugo-web.itheima.net// 请求开始之前做一些事情 $http.…...
支持DDR5,超频更简单,小雕够给力,技嘉B760M小雕WIFI主板上手
目前13代酷睿已经全员集结了,其中全新的i5 13490F应该依然会备受欢迎,当然了,刚上市不久的13代酷睿价格方面还不是很有吸引力,好在12代酷睿在新一代主板上面依然可用,所以预算有限的朋友,完全可用继续使用1…...
fengMap 自定义dom 偏离实际位置;缩放时飘出地图所在区域
目录 一、问题 二、原因及解决方法 三、总结 一、问题 1.前人写了一份代码,很奇怪。使用 new fengmap.FMCompositeMarker添加的复合覆盖物位置是正常的,缩放的时候也是正常的,仍然处于地图内部;但是new fengmap.FMDomMarker添加…...
TryHackMe-黑我杯
黑我杯 相信我们大家在TryHackMe的日积月累都学到了不少东西,从纯萌新到oscp再到更高 我很高兴能将国内各thm玩家聚集到一起,构建一个更好的学习环境和氛围 本次娱乐分两场: Offensive Pentesting — 中等难度Junior Penetration — 容易难…...
【JAVA程序设计】【C00109】基于SSM(非maven)的员工工资管理系统
基于SSM(非maven)的员工工资管理系统项目简介项目获取开发环境项目技术运行截图项目简介 基于ssm框架非maven开发的企业工资管理系统共分为二个角色:系统管理员、员工 管理员角色包含以下功能: 系统后台登陆、管理员管理、员工信…...
《计算机原理》——HelloWorld.cpp如何运行的
学校《计算机原理》开课啦!特此开辟专栏,将一些知识作为笔记,记录下来。 前言 本篇博客知识点来源于educoder的相关题目 1. 相关知识 1.1 计算机语言 计算机语言是人与计算机之间通讯的语言,计算机语言包括编写计算机程序的字符…...
【面试题】在JS循环中使用await会怎么样?
前言这个问题是这样产生的?某天,在学习异步的知识遇到这样一道题:使用Promise的方式,每隔一秒输出数组中一个值const arr [1, 2, 3] arr.reduce((pre, cur) > {return pre.then(() > {returnnewPromise((resolve, rejec…...
Qt QMessageBox详解
文章目录一.QMessageBox介绍枚举属性函数二.QMessageBox的用法1.导入QMessage库2.弹窗提示3.提供选项的弹窗提示4.作为提示,报警,报错提示窗口一.QMessageBox介绍 文本消息显示框(message box)向用户发出情况警报信息并进一步解释警报或向用户提问&…...
Cursor实现用excel数据填充word模版的方法
cursor主页:https://www.cursor.com/ 任务目标:把excel格式的数据里的单元格,按照某一个固定模版填充到word中 文章目录 注意事项逐步生成程序1. 确定格式2. 调试程序 注意事项 直接给一个excel文件和最终呈现的word文件的示例,…...
如何在看板中体现优先级变化
在看板中有效体现优先级变化的关键措施包括:采用颜色或标签标识优先级、设置任务排序规则、使用独立的优先级列或泳道、结合自动化规则同步优先级变化、建立定期的优先级审查流程。其中,设置任务排序规则尤其重要,因为它让看板视觉上直观地体…...
LeetCode - 394. 字符串解码
题目 394. 字符串解码 - 力扣(LeetCode) 思路 使用两个栈:一个存储重复次数,一个存储字符串 遍历输入字符串: 数字处理:遇到数字时,累积计算重复次数左括号处理:保存当前状态&a…...
MVC 数据库
MVC 数据库 引言 在软件开发领域,Model-View-Controller(MVC)是一种流行的软件架构模式,它将应用程序分为三个核心组件:模型(Model)、视图(View)和控制器(Controller)。这种模式有助于提高代码的可维护性和可扩展性。本文将深入探讨MVC架构与数据库之间的关系,以…...
Axios请求超时重发机制
Axios 超时重新请求实现方案 在 Axios 中实现超时重新请求可以通过以下几种方式: 1. 使用拦截器实现自动重试 import axios from axios;// 创建axios实例 const instance axios.create();// 设置超时时间 instance.defaults.timeout 5000;// 最大重试次数 cons…...
c#开发AI模型对话
AI模型 前面已经介绍了一般AI模型本地部署,直接调用现成的模型数据。这里主要讲述讲接口集成到我们自己的程序中使用方式。 微软提供了ML.NET来开发和使用AI模型,但是目前国内可能使用不多,至少实践例子很少看见。开发训练模型就不介绍了&am…...
有限自动机到正规文法转换器v1.0
1 项目简介 这是一个功能强大的有限自动机(Finite Automaton, FA)到正规文法(Regular Grammar)转换器,它配备了一个直观且完整的图形用户界面,使用户能够轻松地进行操作和观察。该程序基于编译原理中的经典…...
服务器--宝塔命令
一、宝塔面板安装命令 ⚠️ 必须使用 root 用户 或 sudo 权限执行! sudo su - 1. CentOS 系统: yum install -y wget && wget -O install.sh http://download.bt.cn/install/install_6.0.sh && sh install.sh2. Ubuntu / Debian 系统…...
Docker 本地安装 mysql 数据库
Docker: Accelerated Container Application Development 下载对应操作系统版本的 docker ;并安装。 基础操作不再赘述。 打开 macOS 终端,开始 docker 安装mysql之旅 第一步 docker search mysql 》〉docker search mysql NAME DE…...
Mysql中select查询语句的执行过程
目录 1、介绍 1.1、组件介绍 1.2、Sql执行顺序 2、执行流程 2.1. 连接与认证 2.2. 查询缓存 2.3. 语法解析(Parser) 2.4、执行sql 1. 预处理(Preprocessor) 2. 查询优化器(Optimizer) 3. 执行器…...
