在小于O(N)的时间复杂度内找到总和

3

问题: 在O(n)的时间复杂度内找到一个数字K,使得1,2,3...K的总和恰好是1,2,3...N总和的一半。

数学: 我知道序列1,2,3....N的总和为N(N+1)/2

因此,我们的任务是找到一个K,使得: K(K+1) = 1/2 * (N)(N+1)/2,如果存在这样的K。

伪代码:

sum1 = n(n+1)/2
sum2 = 0

for(i=1;i<n;i++)
{
    sum2 += i;
    if(sum2 == sum1)
    {
        index = i
        break;
    }
}

问题:目前解决方案的时间复杂度为O(n),但我需要更好的解决方案,比如O(n),O(log(n))...


由于Codechef中的某个问题涉及以下技巧并具有严格的限制条件。 - Alpha
3
你停止做数学题的时间太早了。解出K(K+1) = 1/2 * (N)(N+1)/2关于K的方程,然后你就能在常数时间内得到结果。 - 463035818_is_not_a_number
1
你需要解决二次方程式 - Jarod42
我的猜测是解决方案的时间复杂度为O(K)...如果我错了就把我烧在柴堆上吧 :) - BitTickler
2
算法及其大O符号与编程语言无关,请不要将其与任何编程语言相关联。 - Ulrich Eckhardt
显示剩余12条评论
4个回答

3

您的方程式接近正确,但是您在K方面省略了除以2。实际上,您需要

K * (K + 1) / 2 = N * (N + 1) / (2 * 2)

或者

2 * K * (K + 1) = N * (N + 1)

将这个值代入 Wolfram Alpha 中可以得到实数解:

K = 1/2 * (-sqrt(2N^2 + 2N + 1) - 1)
K = 1/2 * (sqrt(2N^2 + 2N + 1) - 1)

由于您可能不想要负值,所以第二个方程是您要寻找的。这应该是一个O(1)的解决方案。


但是如何检查(2N^2 + 2N + 1)是否为完全平方数(是否有办法判断浮点值是否为整数,然后输出K)? - Alpha
1
我不确定N是否已经给定。 - user1196549
@YvesDaoust 在 OP 的伪代码中,只有 N 是给定的。而“在序列 1、2、3、...、N 中找到 K”对我来说就像是 N 已经给定了。我不确定还有什么其他解决方法... - scohe001
1
@scohe001:通过寻找一个适用的(K,N)对来解决问题。如果已知N,那么这个问题就太容易了... - user1196549
@scohe001: 这是其反面。如果N已知,答案就变成了一句枯燥的“cout <<“答案是”<<”,然后是二次根公式。如果不知道,并且你真的想设计一个以非穷举的方式进行搜索(忽略K=2,N=3符合条件),那么真正的挑战就来了。 - user1196549
显示剩余2条评论

2
其他答案展示了方程的解析解:
k * (k + 1) = n * (n + 1) / 2            其中n已知
然而,问题是OP需要k为整数,但并非每个选定的n都存在这样的值。
我们可以使用整数算术将牛顿法调整为解决此方程。
sum_n =  n * (n + 1) / 2
k = n
repeat indefinitely         // It usually needs only a few iterations, it's O(log(n))
    f_k = k * (k + 1)
    if  f_k == sum_n
        k is the solution, exit
    if  f_k < sum_n
        there's no k, exit 
    k_n = (f_k - sum_n) / (2 * k + 1)   // Newton step: f(k)/f'(k) 
    if  k_n == 0
        k_n = 1   // Avoid inifinite loop
    k = k - k_n;

这里有一个C++实现。
我们可以使用问题中发布的算法来找到所有满足方程式的(n, k)对,其中 0 < knN
n = 1                        // This algorithm compares 2 * k * (k + 1) and n * (n + 1)
sum_n = 1                    // It finds all the pairs (n, k) where 0 < n ≤ N in O(N)
sum_2k = 1
for every n <= N             // Note that n / k → sqrt(2) when n → ∞
    while  sum_n < sum_2k
        n = n + 1            // This inner loop requires a couple of iterations,
        sum_n = sum_n + n    // at most.
   
    if ( sum_n == sum_2k )
        print n and k
   
    k = k + 1
    sum_2k = sum_2k + 2 * k

这里有一个C++实现,可以找到第一对满足 N < 200,000,000 的数对:
           N           K           K * (K + 1)
----------------------------------------------
           3           2                     6
          20          14                   210
         119          84                  7140
         696         492                242556
        4059        2870               8239770
       23660       16730             279909630
      137903       97512            9508687656
      803760      568344          323015470680
     4684659     3312554        10973017315470
    27304196    19306982       372759573255306
   159140519   112529340     12662852473364940

当然,对于太大的值来说,这变得不切实际,并最终溢出。
此外,有一种更好的方法来找到所有这些配对(你是否注意到了最后一位数字序列中的规律?)。
我们可以从操纵这个丢番图方程开始。
2k(k + 1) = n(n + 1)
引入变量 u = n + 1 → n = u - 1,v = k + 1 → k = v - 1
2(v - 1)v = (u - 1)u 2(v2 - v) = u2 + u 2(4v2 - 4v) = 4u2 + 4u 2(4v2 - 4v) + 2 = 4u2 - 4u + 2 2(4v2 - 4v + 1) = (4u2 - 4u + 1) + 1 2(2v - 1)2 = (2u - 1)2 + 1
代入变量 x = 2u - 1 → u = (x + 1)/2,y = 2v - 1 → v = (y + 1)/2
2y2 = x2 + 1 x2 - 2y2 = -1
这是关于2的负裴蜀定理
通过观察,很容易找到它的基本解,x1=1和y1=1。这些对应于n=k=0的原二次不定方程的解,但不是原问题的解(我忽略了0项的求和)。
一旦这些已知,我们可以用两个简单的递推关系计算出所有其他解:
xi+1 = xi + 2yi
yi+1 = yi + xi
请注意,我们需要“跳过”偶数的y,因为它们会导致非整数解。因此我们可以直接使用这些解。 $x_{i+2} = 3x_i + 4y_i$ 可以翻译为 $u_{i+1} = 3u_i + 4v_i - 3$,$n_{i+1} = 3n_i + 4k_i + 3$; $y_{i+2} = 2x_i + 3y_i$ 可以翻译为 $v_{i+1} = 2u_i + 3v_i - 2$,$k_{i+1} = 2n_i + 3k_i + 2$。
                    n                         k
-----------------------------------------------
3* 0 + 4* 0 + 3 =   3      2* 0 + 3* 0 + 2 =  2  
3* 3 + 4* 2 + 3 =  20      2* 3 + 3* 2 + 2 = 14  
3*20 + 4*14 + 3 = 119      2*20 + 3*14 + 2 = 84  
...

0

看起来这个问题要求解决丢番图方程。

2K(K+1) = N(N+1).

通过检查,K=2N=3 是一个解决方案!


请注意,从技术上讲,这是一个O(1)问题,因为N具有有限的值且不变(如果不存在解决方案,则对N的依赖甚至是无意义的)。

-1

你所面临的条件是1到N的和是1到K的两倍

因此,你有N(N+1) = 2K(K+1)K^2 + K - (N^2 + N) / 2 = 0

这意味着K = (-1 +/- sqrt(1 + 2(N^2 + N)))/2

这是O(1)


但是如何检查(2N^2 + 2N + 1)是否为完全平方数(是否有办法判断浮点值是否为整数,然后输出K)? - Alpha
1
@Alpha 这是一个相当简单的 O(1) 检查:https://dev59.com/WXI_5IYBdhLWcg3wDOhC#1549960 - scohe001
@Alpha,你不需要检查完美数,而是需要将它四舍五入。这样就可以了。 - kvantour
1
@kvantour 由于OP在问题定义中提到了“恰好是总和的一半”,我猜测打印没有这样的K存在是一个正确的解决方案(而不仅仅是四舍五入)。 - scohe001

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接