逆约瑟夫问题
约瑟夫问题可以说十分经典,其没有公式解也是广为人知的~
目录
前言
一、约瑟夫问题与逆约瑟夫问题
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)向用户发出情况警报信息并进一步解释警报或向用户提问&…...
[特殊字符] 智能合约中的数据是如何在区块链中保持一致的?
🧠 智能合约中的数据是如何在区块链中保持一致的? 为什么所有区块链节点都能得出相同结果?合约调用这么复杂,状态真能保持一致吗?本篇带你从底层视角理解“状态一致性”的真相。 一、智能合约的数据存储在哪里…...

业务系统对接大模型的基础方案:架构设计与关键步骤
业务系统对接大模型:架构设计与关键步骤 在当今数字化转型的浪潮中,大语言模型(LLM)已成为企业提升业务效率和创新能力的关键技术之一。将大模型集成到业务系统中,不仅可以优化用户体验,还能为业务决策提供…...

Appium+python自动化(十六)- ADB命令
简介 Android 调试桥(adb)是多种用途的工具,该工具可以帮助你你管理设备或模拟器 的状态。 adb ( Android Debug Bridge)是一个通用命令行工具,其允许您与模拟器实例或连接的 Android 设备进行通信。它可为各种设备操作提供便利,如安装和调试…...

AI Agent与Agentic AI:原理、应用、挑战与未来展望
文章目录 一、引言二、AI Agent与Agentic AI的兴起2.1 技术契机与生态成熟2.2 Agent的定义与特征2.3 Agent的发展历程 三、AI Agent的核心技术栈解密3.1 感知模块代码示例:使用Python和OpenCV进行图像识别 3.2 认知与决策模块代码示例:使用OpenAI GPT-3进…...
基于Uniapp开发HarmonyOS 5.0旅游应用技术实践
一、技术选型背景 1.跨平台优势 Uniapp采用Vue.js框架,支持"一次开发,多端部署",可同步生成HarmonyOS、iOS、Android等多平台应用。 2.鸿蒙特性融合 HarmonyOS 5.0的分布式能力与原子化服务,为旅游应用带来…...
【论文笔记】若干矿井粉尘检测算法概述
总的来说,传统机器学习、传统机器学习与深度学习的结合、LSTM等算法所需要的数据集来源于矿井传感器测量的粉尘浓度,通过建立回归模型来预测未来矿井的粉尘浓度。传统机器学习算法性能易受数据中极端值的影响。YOLO等计算机视觉算法所需要的数据集来源于…...

ServerTrust 并非唯一
NSURLAuthenticationMethodServerTrust 只是 authenticationMethod 的冰山一角 要理解 NSURLAuthenticationMethodServerTrust, 首先要明白它只是 authenticationMethod 的选项之一, 并非唯一 1 先厘清概念 点说明authenticationMethodURLAuthenticationChallenge.protectionS…...

深入解析C++中的extern关键字:跨文件共享变量与函数的终极指南
🚀 C extern 关键字深度解析:跨文件编程的终极指南 📅 更新时间:2025年6月5日 🏷️ 标签:C | extern关键字 | 多文件编程 | 链接与声明 | 现代C 文章目录 前言🔥一、extern 是什么?&…...
Swagger和OpenApi的前世今生
Swagger与OpenAPI的关系演进是API标准化进程中的重要篇章,二者共同塑造了现代RESTful API的开发范式。 本期就扒一扒其技术演进的关键节点与核心逻辑: 🔄 一、起源与初创期:Swagger的诞生(2010-2014) 核心…...
基于matlab策略迭代和值迭代法的动态规划
经典的基于策略迭代和值迭代法的动态规划matlab代码,实现机器人的最优运输 Dynamic-Programming-master/Environment.pdf , 104724 Dynamic-Programming-master/README.md , 506 Dynamic-Programming-master/generalizedPolicyIteration.m , 1970 Dynamic-Programm…...