# How to evaluate this sum $\sum\limits_{i=1}^{n} \left \lfloor \sqrt{2} \cdot i \right \rfloor$

I have the following sum that I need to evaluate:

$$\sum\limits_{i=1}^{n} \left \lfloor \sqrt{2} \cdot i \right \rfloor$$

If the floor wasn't there it would have been too easy $\sqrt2 \sum\limits_{i=1}^{n} i$, but with the floor added I have no clue how to approach it. Is this sum solveable and if so how do I evaluate it?

• Did you see this related question? – Dietrich Burde Jan 13 '17 at 19:54
• What sort of answer are you expecting? – Nate Jan 13 '17 at 20:14
• @Nate I need to compute the value of the above expression but n can be very large $n\in \mathbb{N} \to \{0 < n <= 10^{100}\}$ which takes a lot of cpu processing time using loops, I was looking for a way to simplify the expression. – razzak Jan 13 '17 at 20:55
• Do you need an exact answer? If $n = 10^{100}$ is $0.70710678118654752440084436210484 \times 10^{200}$ good enough? – Nate Jan 13 '17 at 21:05
• By Beatty's theorem every positive integer is in exactly one of the sets $S_1=\{\lfloor n\sqrt2\rfloor\mid n>0\}$ or $S_2=\{\lfloor n(2+\sqrt2)\rfloor\mid n>0\}$. Wonder if we can use that to get a more precise result? Some kind of a recurrence formula could come out, but it's too late an hour for me to think straight... – Jyrki Lahtonen Jan 13 '17 at 22:14

As you said, if you ignored the floor and looked at the sum $\sum_{i=1}^n \sqrt{2} i = \sqrt{2} \sum_{i=1}^n i$ you could easily compute the answer as $\sqrt{2} \frac{n(n+1)}{2}$. Taking floors clearly lowers this, but this is an upper bound.

On the other hand we know $\lfloor \sqrt{2} i \rfloor > \sqrt{2}i - 1$. So we could get a lower bound by taking $\sum_{i=1}^n (\sqrt{2} i - 1) = \sqrt{2} \frac{n(n+1)}{2} - n$.

The point being that both of these bounds look like $\frac{\sqrt{2}}{2} n^2 + O(n)$. So for $n=10^{100}$ we can get about 100 digits of accuracy by the approximation $\frac{\sqrt{2}}{2} \times 10^{200}$.

• Upon a bit more thought I can show that $\sum\limits_{i=1}^{n} \left \lfloor \sqrt{2} \cdot i \right \rfloor = \sqrt{2} \frac{n(n+1)}{2} - n/2 + o(n)$, giving the next term. I'd need to think harder and look up some results though to say more about the $o(n)$ term in order to actually say anything about $n = 10^{100}$ though. – Nate Jan 13 '17 at 21:59

I've found a fast enough algorithm to compute the exact value for $n = 10^{100}$:

$$\sum_{i=1}^{10^{100}} i\cdot\sqrt 2 =$$

$$70710678118654752440084436210484903928483593768847403658833986899536623923105351942519376716382078638821760123411090095254685423841027253480565451739737157454059823250037671948325191776995310741236436$$

Here's the python code:

import math
import time

#
# Helpers for numbers in the form a + b*sqrt(2), encoded as a tuple (a,b)
#

# compare 2 numbers
def root2_cmp(x,y):
x_a,x_b = x
y_a,y_b = y

a = x_a - y_a
b = x_b - y_b

a_sign = cmp(a,0)
b_sign = cmp(b,0)

if a_sign == b_sign: return a_sign

if a_sign == 0: return b_sign
if b_sign == 0: return a_sign

# one of a_sign and b_sign is 1 and the other is -1
# compare a^2 and (b*sqrt(2))^2 = 2*b^2

aa = a*a
bb2 = b*b*2

# if a^2 > 2b^2, return a_sign otherwise the opposite sign
return cmp(aa,bb2)*a_sign

def root2_max(x,y):
return x if root2_cmp(x,y) > 0 else y

def root2_min(x,y):
return x if root2_cmp(x,y) < 0 else y

return tuple(x_i+y_i for x_i,y_i in zip(x,y))

def root2_sub(x,y):
return tuple(x_i-y_i for x_i,y_i in zip(x,y))

# Takes 2 sequence objects in the form (lower_bound, upper_bound, offset, sequence_data)
# Returns the same data for the concatenation of the sequences.

def seq_concat(a,b):
a_lower, a_upper, a_offset, a_seq_data = a
b_lower, b_upper, b_offset, b_seq_data = b

c_lower = root2_max(a_lower, root2_sub(b_lower, a_offset))
c_upper = root2_min(a_upper, root2_sub(b_upper, a_offset))

# sanity check - should never be concatenating sequences that can't go together
assert root2_cmp(c_upper, c_lower) > 0

c_sequence = seq_data_concat(a_seq_data, b_seq_data)

return c_lower, c_upper, c_offset, c_sequence

# Takes 2 sequence data objects in the form (length, slope, y-intercept)
# Returns the same data for the concatenation of the sequences.
def seq_data_concat(a,b):
a_len, a_slope, a_intercept = a
b_len, b_slope, b_intercept = b

c_len = a_len + b_len
c_slope = a_slope + b_slope
c_intercept = a_slope*b_len + a_intercept + b_intercept

return (c_len, c_slope, c_intercept)

# Compute the sum for i from 1 to n of floor(i*sqrt(2))
def root2_floor_sum(n):
seqs = [
#  lower  upper  offset  seq_data (length, slope, y-intercept)
( (0,0), (2,-1), (-1,1), (1,1,1) ),
( (2,-1), (1,0), (-2,1), (1,2,2) )
]

prev_seq = None
cur_seq_data = (0,0,0)
cur_offset = (0,0)

# while current sequence length is below n
while cur_seq_data[0] < n:
# remove too-big sequences
max_len = n - cur_seq_data[0]
seqs = filter(lambda (lb,ub,off,seq_data): seq_data[0] <= max_len, seqs)

matching_seqs = filter(lambda (lb,ub,off,seq_data): root2_cmp(cur_offset, lb) >= 0 and root2_cmp(cur_offset, ub) < 0, seqs)
next_seq = max(matching_seqs, key=lambda (lb,ub,off,seq_data): seq_data[0])

if prev_seq != None:

next_lb, next_ub, next_off, next_seq_data = next_seq

cur_seq_data = seq_data_concat(cur_seq_data, next_seq_data)

prev_seq = next_seq

return cur_seq_data[2]

def verify_up_to(n):
# verify the algorithm works for small n
expected = 0
root2 = math.sqrt(2)
for i in range(1,n+1):
expected += int(math.floor(root2*i))
assert root2_floor_sum(i) == expected

verify_up_to(1000)

start_time = time.time()
result = root2_floor_sum(10**100)
end_time = time.time()

print result
print 'computed in',int((end_time - start_time)*1000),'ms'


Here's the output I get for the above program:

70710678118654752440084436210484903928483593768847403658833986899536623923105351942519376716382078638821760123411090095254685423841027253480565451739737157454059823250037671948325191776995310741236436
computed in 889 ms


## Explanation of the algorithm

First, consider the sequence of differences of successive terms in this sum $a_n = \lfloor n\sqrt 2 \rfloor - \lfloor(n-1) \sqrt 2 \rfloor$.

This sequence is all $1$s and $2$s: $1,1,2,1,2,1,1,2,1, ...$

Given this, we can compute the sum in question with $\sum_{i=1}^n i \cdot a_{n-i+1}$. But this is obviously too slow with $n = 10^{100}$. So rather than computing the sequence directly, we want to compute some info about it that still lets us compute the sum.

So we will only keep track of the following for the sequence $a$:

• The length of the sequence $len(a)$

• The function $f_a(k) = \sum_{i=1}^{len(a)} (i+k) \cdot a_{len(a)-i+1}$. Then $f_a(0)$ is the sum described above. You can think of this as being the above sum, but with $a$ having $k$ zeros appended to it. Because $f_a$ is a finite sum of linear functions, it is linear. Therefore it is uniquely defined by its slope, $slope(a)$ and $y$-intercept, $intercept(a)$

Now suppose we have the above info for sequences $a$ and $b$. We can compute the same info for the concatenation of these sequences, $c$.

• $len(c) = len(a) + len(b)$

• $f_c(k) = f_a(k + len(b)) + f_b(k)$, giving us

• $slope(c) = slope(a) + slope(b)$
• $intercept(c) = slope(a) \cdot len(b) + intercept(a) + intercept(b)$

So this means that we can build the needed info for the whole sequence by combining info of smaller sequences.

Next, we need the fact that this sequence repeats itself a lot (I mean look at it).

Imagine a line drawn from $(n,\sqrt n)$ to $(m,\sqrt m)$ with $m > n$. Between each time this passes through a vertical line, it passes through either $1$ or $2$ horizontal lines. These $1$s and $2$s are exactly the sequence of differences of terms we defined above at indices $n$ through $m$. Because of the irrationality of $\sqrt 2$, we can move this line around slightly and still get the same sequences. If we've computed the sequence up to some $k$ and find that the fractional part of the $k$th term in the original sum, $frac(m*sqrt(2))$ is close to the fractional part of the $n$th term, we can translate the line above to $(k,\sqrt k)$ to $(k+m-n, \sqrt {k+m-n})$ and this won't change the sequence associated with it.

So the algorithm works by building bigger and bigger lines described above by appending them together, remembering the info described above about the sequences associated with the lines (length, slope, intercept).