反素数c++
先上代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll n;
ll p[]={2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53};
int maxd,maxval;
void dfs(int pl,ll tmp,int num,int up){
if((num>maxd)||(num==maxd&&maxval>tmp)){
maxd=num;
maxval=tmp;
}
if(pl==16) return;
for(int i=1;i<=up;i++){
if(tmp*p[pl]>n) return;
tmp*=p[pl];
dfs(pl+1,tmp,num*(i+1),i);
}
}
int main(){
cin>>n;
dfs(0,1,1,63);
cout<<maxval<<endl;
return 0;
}
代码解析:寻找不超过n的反素数
这段代码是用来寻找不超过给定正整数n的反素数(antiprime number)。反素数是指在一个特定范围内拥有最多因数的数中最小的那个。
反素数的定义和性质
反素数具有以下性质:
-
它是范围内因数个数最多的数
-
如果有多个数具有相同的最大因数个数,则选择其中最小的一个
-
反素数的质因数分解中,质数是从小到大排列的,且指数是单调不增的
代码结构分析
1. 全局变量和预处理
typedef long long ll; ll n; ll p[]={2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53}; int maxd, maxval;
-
p[]
数组存储了前16个质数(2到53) -
maxd
记录当前找到的最大因数个数 -
maxval
记录对应的数值
2. 深度优先搜索函数dfs
void dfs(int pl, ll tmp, int num, int up) {if((num>maxd)||(num==maxd&&maxval>tmp)) {maxd = num;maxval = tmp;}if(pl == 16) return;for(int i=1; i<=up; i++) {if(tmp*p[pl] > n) return;tmp *= p[pl];dfs(pl+1, tmp, num*(i+1), i);} }
参数说明:
-
pl
:当前考虑的质数在p[]中的索引 -
tmp
:当前生成的数值 -
num
:当前数值的因数个数 -
up
:当前质数的最大可能指数(保证指数单调不增)
函数逻辑:
-
检查当前数值是否比已记录的最优解更好(因数更多,或因数相同但数值更小)
-
如果已经考虑完所有质数(16个),则返回
-
尝试为当前质数分配指数i(从1到up)
-
确保乘积不超过n
-
递归处理下一个质数,更新数值、因数个数,并限制下一个质数的指数不超过当前i
-
3. 主函数
cpp
复制
下载
int main() {cin >> n;dfs(0, 1, 1, 63);cout << maxval << endl;return 0; }
-
读取输入的n
-
从第一个质数(索引0)开始搜索,初始数值为1,因数个数为1,初始指数上限设为63(足够大)
-
输出找到的反素数
算法原理
-
因数个数计算:一个数的因数个数等于其质因数分解各指数加1的乘积。例如,12=2²×3¹,因数个数为(2+1)×(1+1)=6。
-
贪心策略:为了找到因数最多且数值最小的数,我们:
-
优先使用较小的质数
-
确保质数的指数是单调不增的(更大的质数指数不超过前面的)
-
-
剪枝优化:
-
当当前乘积超过n时停止继续尝试
-
限制后续质数的指数不超过前一个质数的指数
-
示例分析
假设n=10:
-
搜索过程会尝试以下组合:
-
2^1 = 2 (因数2)
-
2^2 = 4 (因数3)
-
2^3 = 8 (因数4)
-
3^1 = 3 (因数2)
-
2^1×3^1 = 6 (因数4)
-
其他组合会超过10
-
-
最终找到6和8都有4个因数,选择较小的6
复杂度分析
-
时间复杂度:O(2^k),其中k是质数个数(这里k=16),但由于剪枝和指数限制,实际运行很快
-
空间复杂度:O(k)递归深度
这段代码高效地利用了反素数的数学性质和DFS剪枝策略,能够在合理时间内找到不超过n的最大反素数。
进行递归时要注意手动模拟理解代码,例如n=18时,一次进行这样的搜索
1. 2^1 (因数2)
2. 2^1*3^1(因数4)
3. 2^2 (因数3)
4. 2^2*3^1(因数6)
5. 2^3 (因数4)
如此得到答案12
自己这样模拟一遍便能理解代码是如何操作并进行筛选出最终答案了