如果有两个数字n
和k
,请找到最大化剩余x
的{{1}},1 <= x <= k
。
例如,n = 20且k = 10,解是x = 7,因为余数20%7 = 6是最大值。
我的解决方案是:
n % x
但我的解决方案是int n, k;
cin >> n >> k;
int max = 0;
for(int i = 1; i <= k; ++i)
{
int xx = n - (n / i) * i; // or int xx = n % i;
if(max < xx)
max = xx;
}
cout << max << endl;
。有没有更有效的解决方案?
答案 0 :(得分:7)
不是渐近更快,而是更快,只需向后退,当你知道自己无法做得更好时停下来。
假设k
小于n
(否则只输出k
)。
int max = 0;
for(int i = k; i > 0 ; --i)
{
int xx = n - (n / i) * i; // or int xx = n % i;
if(max < xx)
max = xx;
if (i < max)
break; // all remaining values will be smaller than max, so break out!
}
cout << max << endl;
(这可以通过执行for循环来进一步改进i > max
,从而消除了一个条件语句,但我这样写它是为了使它更明显)
另外,请查看Garey和Johnson的计算机和难以处理的书,以确保这不是NP-Complete(我确信我记得那本看起来很像这本书的问题)。在投入太多精力试图找到更好的解决方案之前,我会这样做。
答案 1 :(得分:6)
此问题相当于在给定范围内查找函数f(x)=n%x
的最大值。让我们看看这个函数是怎么样的:
很明显,如果我们从x=k
开始,然后在有意义的情况下减少x
,我们可以尽快获得最大值(直到x=max+1
)。此图还显示,对于大于x
的{{1}},我们不需要按顺序减少sqrt(n)
。相反,我们可以立即跳到当地最大值之前。
x
Magic constant int maxmod(const int n, int k)
{
int max = 0;
while (k > max + 1 && k > 4.0 * std::sqrt(n))
{
max = std::max(max, n % k);
k = std::min(k - 1, 1 + n / (1 + n / k));
}
for (; k > max + 1; --k)
max = std::max(max, n % k);
return max;
}
允许通过减少第一个(昂贵的)循环的迭代次数来提高性能。
最坏情况时间复杂度可以估计为O(min(k,sqrt(n)))。但是对于足够大的4.0
,这个估计可能过于悲观:我们可以更快地找到最大值,如果k
明显大于k
,我们只需要1或2次迭代就可以找到它。
我做了一些测试来确定在最坏情况下需要多少次迭代才能得到sqrt(n)
的不同值:
n
增长率明显优于O(sqrt(n))。
答案 2 :(得分:2)
对于k&gt;这个问题很简单(取x = n + 1)。
对于k < n,考虑余数n%x的图表。对于所有n看起来都是一样的:剩余部分在n:n / 2,n / 3,n / 4的谐波下降到零,之后它们跳起来,然后平滑地向下一次谐波减小。
解决方案是k以下最右边的局部最大值。作为公式x = n//((n//k)+1)+1
(其中//
是整数除法)。
答案 3 :(得分:1)
漂亮的小谜题!
从两个微不足道的案例开始。
代表n < k
:任何x
s.t. n < x <= k
解决了问题。
n = k
:x = floor(k / 2) + 1
解决。
我的尝试。
代表n > k
:
x = n
while (x > k) {
x = ceil(n / 2)
}
^ ----没有用。
x = floor(float(n) / (floor(float(n) / k) + 1)) + 1
x = ceil(float(n) / (floor(float(n) / k) + 1)) - 1
^ ----&#34; 关闭&#34; (无论那意味着什么),但没有用。
我的骄傲让我提到我是第一个利用由k
1.
- 有界谐波
<强>解。强>
与其他答案一致,我只需检查n
小于k
的谐波(@ColonelPanic一致),限制当前的最大值(由@TheGreatContini提供)。这是两个世界中最好的,我已经使用0到10000000之间的随机整数进行了测试,并取得了成功。
int maximalModulus(int n, int k) {
if (n < k) {
return n;
}
else if (n == k) {
return n % (k / 2 + 1);
}
else {
int max = -1;
int i = (n / k) + 1;
int x = 1;
while (x > max + 1) {
x = (n / i) + 1;
if (n%x > max) {
max = n%x;
}
++i;
}
return max;
}
}
效果测试: http://cpp.sh/72q6
示例输出:
Average number of loops:
bruteForce: 516
theGreatContini: 242.8
evgenyKluev: 2.28
maximalModulus: 1.36 // My solution
答案 4 :(得分:1)
挥手
x
因素n
的值不能产生最大n%x
,因为如果x
是n
的因子,那么{{ 1}}。
因此,您希望采用一种避免考虑任何n%x=0
x
因子的程序。但这意味着您需要一种简单的方法来了解n
是否是一个因素。如果可能的话,您将能够进行简单的素数分解。
由于is not已知一种简单的方法来进行素数分解,因此无法通过“简单”的方法来解决您的问题(我认为您不会找到单一的公式,某种搜索将是必要的。)
也就是说,素数分解文献有一种巧妙的方法可以快速获得相对于天真搜索的因素,所以也许可以利用它来回答你的问题。
答案 5 :(得分:0)
我确实错了,但在我看来,这取决于是否n < k
。
我的意思是,如果n < k
,n%(n+1)
给你最大值,那么x = (n+1)
。
另一方面,您可以从j = k
开始,然后重新评估n%j
,直到它等于n
,因此x = j
是什么你正在寻找并且你会在最多k
步骤中得到它......太多了,是吗?
答案 6 :(得分:-1)
好的,我们想知道除数最大的除数;
让n
为要除数,i
为除数。
对于所有n
,i
除以i<n
时,我们很想找出最大余数。
我们知道,remainder = n - (n/i) * i //equivalent to n%i
如果我们观察上述等式以获得最大remainder
,则必须最小化 (n/i)*i
任何n/i
的{{1}}的最小值是i<n
。
请注意,1
代表n/i == 1
,当且仅当i<n
现在我们有i>n/2
。
大于i>n/2
的最小可能值为n/2
。
因此,给出最大余数的除数n/2+1
这是C ++中的代码
i = n/2+1
时间复杂度:O(1)