求满足组合数C(n,k)≤x的最大n的高效计算优化方案
针对k=1/2/3的组合数逆问题优化方案
给定k∈{1,2,3}、x∈[0, C(n',k)](n'为已知常数如1000),要求快速找到满足C(n,k)≤x的最大n且n≤n'。针对原迭代方案最坏需n'-k次循环的问题,可利用k值极小的特性,直接通过数学公式计算近似解,再做1次验证调整,实现几乎无循环的高效求解:
分情况推导
k=1的情况
组合数C(n,1)=n,因此最大n直接取min((long long)x, n'),无需额外计算。
k=2的情况
由C(n,2)=n(n-1)/2 ≤x,整理得二次不等式:
n² -n -2x ≤0
通过求根公式得到正根:n = (1 + sqrt(1 + 8*x)) / 2
取该值的整数部分floor(n),再与n'取最小值即可。若担心浮点精度误差,可额外判断下一个整数是否满足条件(实际因x范围限制,精度足够时无需此步骤)。
k=3的情况
由C(n,3)=n(n-1)(n-2)/6 ≤x,整理得三次不等式:
n³ -3n² +2n -6x ≤0
先通过立方根近似估算n的初始值:n ≈ cbrt(6*x) + 1
取整数部分后,只需验证该值及相邻的1-2个整数(最多1次调整),即可得到满足条件的最大n,再与n'取最小值。
优化后的C++模板代码
#include <cmath> #include <algorithm> template<int k> long long find_max_n(long long x, long long n_prime) { if (x < 0) return -1; // 非法输入处理 switch(k) { case 1: return std::min(x, n_prime); case 2: if (x == 0) return 0; // C(0,2)=0,C(1,2)=0 double sqrt_val = sqrt(1 + 8.0 * x); long long n = static_cast<long long>((1 + sqrt_val) / 2); // 仅需最多1次循环修正精度问题 while (n*(n-1)/2 <= x) n++; n--; return std::min(n, n_prime); case 3: if (x == 0) return 0; // C(0,3)=C(1,3)=C(2,3)=0 double cbrt_val = cbrt(6.0 * x); long long n = static_cast<long long>(cbrt_val) + 1; // 仅需最多1次循环修正近似值 while ((n*(n-1)*(n-2))/6 <= x) n++; n--; return std::min(n, n_prime); default: return -1; // 仅支持k=1,2,3 } }
代码说明
- 针对k=2和k=3的情况,仅需最多1次循环调整(初始近似值与真实解差距极小),远优于原最坏n'-k次循环的方案;
- 利用数学公式直接估算,避免了迭代计算组合数的大量重复运算;
- 加入边界情况处理(如x=0时的结果),保证鲁棒性。
内容的提问来源于stack exchange,提问作者PingFloyd
相关产品推荐
相关产品推荐

