If you want something easy to implement and fast, try this:
Function Sum(x: Number, n: Integer) -> Number
P := PolySum(:x, n)
return P(x)
End
Function PolySum(x: Variable, n: Integer) -> Polynomial
C := Sum-Coefficients(n)
P := 0
For i from 1 to n + 1
P += C[i] * x^i
End
return P
End
Function Sum-Coefficients(n: Integer) -> Vector of Rationals
A := Create-Matrix(n)
R := Reduced-Row-Echelon-Form(A)
return last column of R
End
Function Create-Matrix(n: Integer) -> Matrix of Integers
A := New (n + 1) x (n + 2) Matrix of Integers
Fill A with 0s
Fill first row of A with 1s
For i from 2 to n + 1
For j from i to n + 1
A[i, j] := A[i-1, j] * (j - i + 2)
End
A[i, n+2] := A[i, n]
End
A[n+1, n+2] := A[n, n+2]
return A
End
Explanation
Our goal is to obtain a polynomial Q
such that Q(x) = sum i^n for i from 1 to x
. Knowing that Q(x) = Q(x - 1) + x^n
=> Q(x) - Q(x - 1) = x^n
, we can then make a system of equations like so:
d^0/dx^0( Q(x) - Q(x - 1) ) = d^0/dx^0( x^n )
d^1/dx^1( Q(x) - Q(x - 1) ) = d^1/dx^1( x^n )
d^2/dx^2( Q(x) - Q(x - 1) ) = d^2/dx^2( x^n )
... .
d^n/dx^n( Q(x) - Q(x - 1) ) = d^n/dx^n( x^n )
Assuming that Q(x) = a_1*x + a_2*x^2 + ... + a_(n+1)*x^(n+1)
, we will then have n+1
linear equations with unknowns a1, ..., a_(n+1)
, and it turns out the coefficient cj
multiplying the unknown aj
in equation i
follows the pattern (where (k)_p
= (k!)/(k - p)!
):
- if
j < i
, cj
= 0
- otherwise,
cj
= (j)_(i - 1)
and the independent value of the i
th equation is (n)_(i - 1)
. Explaining why gets a bit messy, but you can check the proof here.
The above algorithm is equivalent to solving this system of linear equations.
Plenty of implementations and further explanations can be found in https://github.com/fcard/PolySum. The main drawback of this algorithm is that it consumes a lot of memory, even my most memory efficient version uses almost 1gb
for n=3000
. But it's faster than both SymPy and Mathematica, so I assume it's okay. Compare to Schultz's method, which uses an alternate set of equations.
Examples
It's easy to apply this method by hand for small n
. The matrix for n=1
is
| (1)_0 (2)_0 (1)_0 | | 1 1 1 |
| 0 (2)_1 (1)_1 | = | 0 2 1 |
Applying a Gauss-Jordan elimination we then obtain
| 1 0 1/2 |
| 0 1 1/2 |
Result = {a1 = 1/2, a2 = 1/2}
=> Q(x) = x/2 + (x^2)/2
Note the matrix is always already in row echelon form, we just need to reduce it.
For n=2
:
| (1)_0 (2)_0 (3)_0 (2)_0 | | 1 1 1 1 |
| 0 (2)_1 (3)_1 (2)_1 | = | 0 2 3 2 |
| 0 0 (3)_2 (2)_2 | | 0 0 6 2 |
Applying a Gauss-Jordan elimination we then obtain
| 1 1 0 2/3 | | 1 0 0 1/6 |
| 0 2 0 1 | => | 0 1 0 1/2 |
| 0 0 1 1/3 | | 0 0 1 1/3 |
Result = {a1 = 1/6, a2 = 1/2, a3 = 1/3}
=> Q(x) = x/6 + (x^2)/2 + (x^3)/3
The key to the algorithm's speed is that it doesn't calculate a factorial for every element of the matrix, instead it knows that (k)_p
= (k)_(p-1) * (k - (p - 1))
, therefore A[i,j]
= (j)_(i-1)
= (j)_(i-2) * (j - (i - 2))
= A[i-1, j] * (j - (i - 2))
, so it uses the previous row to calculate the current one.
mod
s by a factor of 5 or 50 doesn't seem to significantly reduce the amount of computation. – Northmann
you can compute the sum directly in reasonable time. For largen
I think you can find such pairs of numbers, which add up to 1000000007 (for example 1 and 1000000006, 7 and 1000000000...). Those numbers equal 1 and (–1) mod 1000000007, respectively, so they cancel in summation – and so do their respective powers. You can skip them then, so you'll have to directly calculate no more than 1000000007 terms of the sum... – Northmanlog b
is at most 10 here. – Wouldben > 1000000007/2
there are some numbers between1
andn
which make sum1+n
equal 1000000007; so you have to compute no more than1000000007/2
i.e. 500000004 terms. Additionally(a**x) * (b**x)
equals(a*b) ** x
, so it's enough to computep**x
for primes only, then compute remaining terms by multipliying powers of appropriate primes. – Northman