从n个数字的总和中找出最接近的n

时间:2016-09-23 21:12:43

标签: algorithm logic

当我获得第一个 n 数字的总和时,我正在尝试求解 n 的最接近值。 意思是如果我将总和设为60,那么我的 n 应该是10,因为前10个数字的总和是55,如果我包括11则总和将是66,超过我所需的总和。

int num=1, mysum = 0;  
int givensum=60;
while (mysum < givensum) {
    mysum += num;
    num++;
}
cout<<num-1;
return 0;

我能想到解决这个问题的另一种方法是求解二次方程式 n(n+1) / 2 = givensum并从中获取 n 。 有没有其他方法可以解决这个问题?

3 个答案:

答案 0 :(得分:5)

我认为没有比解决quadratic equation更好的方法了。这很简单,

n*(n+1)/2 = sum
n^2 + n - sum*2 = 0
assumin ax^2 + bx + c = 0
a = 1, b = 1, c = -2*sum

因为我们不需要否定答案:

n = ( -b + sqrt(b^2 - 4ac) ) / 2a

这是实施:

#include <iostream>
#include <cmath>
using namespace std;


int main()
{
    int sum = 60;
    int a = 1;
    int b = 1;
    int c = -sum*2;

    double delta = b*b - 4*a*c;
    if ( delta >= 0 ){
        double x1 = -b + sqrt(delta);
        //double x2 = -b - sqrt(delta); // we don't need the negative answer
        x1 /= 2*a;
        //x2 /= 2*a;
        cout << x1 << endl;
    }
    else {
        cout << "no result";
    }
}

结果是浮点数,如果您希望 n 元素之和小于或等于输入和,则应使用floor函数将其向下舍入。 / p>

考虑函数f(n) = n*(n+1)/2,它产生第一个 n 整数的总和。此功能严格增加。因此,当f(n)的值被赋予您时,您可以使用二进制搜索来查找 n

#include <iostream>
#include <cmath>
using namespace std;


int main()
{
    int sum = 61;
    int low = 1, high = sum, mid;
    while ( low < high ){
        mid = ceil ( (low+high)/2.0 );
        int s = mid*(mid+1)/2;
        if ( s > sum ){
            high = mid-1;
        } else if ( s < sum ) {
            low = mid;
        } else {
            low = mid;
            break;
        }
    }
    cout << low << endl;
}

答案 1 :(得分:1)

所以我们希望找到n

之类的整数(n+1)*n/2 <= y < (n+2)*(n+1)/2

求解二次方程f(x)=y,其中f(x)=(x+1)*x/2可以使用浮点运算,然后将n作为x的整数部分。

但我们并不真正需要浮点数,因为我们只需要结果的整数部分,我们也可以使用Newton-Raphson迭代https://en.wikipedia.org/wiki/Newton%27s_method

f(x)的衍生物是f'(x)=(x+(1/2))。不是一个好的整数,但我们都可以乘以2,并像这样写循环:(这是Smalltalk,但这并不重要):

Integer>>invSumFloor
    "Return the integer n such that (1 to: n) sum <= self < (1 to: n+1) sum"

    | guess delta y2 |
    y2 := self * 2.
    guess := 1 bitShift: y2 highBit + 1 // 2.
    [
        delta := ((guess + 1) * guess - y2) // (guess * 2 + 1).
        delta = 0 ]
            whileFalse: [ guess := guess - delta ].
    ^guess - 1

所以我们这样迭代:

x(n+1) = x(n) - (2*f(x(n))-2*y)/(2*f'(x(n)))

但是我们不是采用精确的除法,而是使用//,它是向下舍入到最接近的整数的商。

通常情况下,我们应该在最后阶段测试猜测是否被过高估计。

但是在这里,我们安排初步猜测是对结果的过高估计,但不要过高估计,以至于猜测总是会被高估。像这样我们可以在最后阶段简单地减去1。上面的实现使用粗略猜测第一位位置作为初始x值。

然后我们可以检查前一万个自然整数的实现:

(1 to: 10000) allSatisfy: [:y | 
    | n sum |
    n := y invSumFloor.
    sum := (1 to: n) sum.
    (sum <= y and: [y < (sum + n + 1)])].

回答true

Smalltalk的优点是你可以尝试这样的事情:

80 factorial invSumFloor.

得到类似的东西:

378337037695924775539900121166451777161332730835021256527654

你在这里看到Newton Raphson迅速融合(上例中有7次迭代)。这与最初的幼稚迭代非常不同。

答案 2 :(得分:0)

代码突破while后,mysum成为最接近givensum的总和。对于您给出的示例,while循环执行因为55 is less than 60,并且mysum变为66而num在最后一次循环执行之前变为12停止。完成此步骤后,由于66 is not less than 60while不会再次执行。因此,您应该mysum减少num-2

cout<<num-2;
相关问题