Как суммировать последовательность?
как я могу суммировать следующую последовательность:
⌊n/1⌋ + ⌊n/2⌋ + ⌊n/3⌋ + ... + ⌊n/n⌋
Это просто O (n) решение на C++:
#include <iostream>
int main()
{
int n;
std::cin>>n;
unsigned long long res=0;
for (int i=1;i<=n;i++)
{
res+= n/i;
}
std::cout<<res<<std::endl;
return 0;
}
знаете ли вы лучшее решение, чем это? Я имею в виду O(1) или O(log(n)). Спасибо за ваше время :) и решения
изменить: Спасибо за все ваши ответы. Если кому-то нужно решение O (sqrt(n)): Python:
import math
def seq_sum(n):
sqrtn = int(math.sqrt(n))
return sum(n // k for k in range(1, sqrtn + 1)) * 2 - sqrtn ** 2
n = int(input())
print(seq_sum(n))
C++:
#include <iostream>
#include <cmath>
int main()
{
int n;
std::cin>>n;
int sqrtn = (int)(std::sqrt(n));
long long res2 = 0;
for (int i=1;i<=sqrtn;i++)
{
res2 +=2*(n/i);
}
res2 -= sqrtn*sqrtn;
std::cout<<res2<<std::endl;
return 0;
}
5 ответов
это Дирихле делитель summatory функции D(х). Используя следующую формулу (источник)
здесь
дает следующее O(sqrt(n))
psuedo-код (который является допустимым Python):
def seq_sum(n):
sqrtn = int(math.sqrt(n))
return sum(n // k for k in range(1, sqrtn + 1)) * 2 - sqrtn ** 2
Примечания:
- на
//
оператор в Python-целое число, то есть усечение, деление. -
math.sqrt()
используется в качестве иллюстрации. Строго говоря, это должно использовать алгоритм точного целочисленного квадратного корня вместо математики с плавающей запятой.
из статьи Википедии о функции summatory делитель,
здесь . Это должно обеспечить
решение времени.
EDIT: целочисленная проблема квадратного корня также может быть решена в квадратном корне или даже логарифмическом времени - на всякий случай, если это не очевидно.
проект Polymath описывает алгоритм вычисления этой функции во времени O (n^(1/3 + o (1)), см. раздел 2.1 на стр. 8-9 из:
http://arxiv.org/abs/1009.3956
алгоритм предполагает разрезание области на достаточно тонкие интервалы и оценка значение на каждом, где интервалы выбраны достаточно тонкими, чтобы оценка была точной при округлении до ближайшего целого числа. Таким образом, вы вычисляете до некоторого диапазона непосредственно (они предлагают 100n^(1/3), но вы можете изменить это с некоторой осторожностью), а затем сделать все остальное в этих тонких ломтиках.
посмотреть запись OEIS для получения дополнительной информации об этой последовательности.
Edit: теперь я вижу, что Kerrek SB упоминает этот алгоритм в комментариях. Справедливости ради, однако, я добавил комментарий к OEIS 5 лет назад, поэтому я не чувствую себя плохо за публикацию " его " ответа. :)
Я должен также упомянуть, что алгоритм O(1) невозможен, так как ответ вокруг N log n и, следовательно, даже писать требуется время > log n.
давайте разделим все числа {1, 2, 3, ..., n}
на 2 группы: меньше или равно sqrt(n)
и больше sqrt(n)
. Для первой группы мы можем вычислить сумму с помощью простой итерации. Для второй группы, мы можем использовать следующее наблюдение: если a > sqrt(n)
, чем n / a < sqrt(n)
. Вот почему мы можем перебирать значение [n / i] = d
(от 1
to sqrt(n)
) и вычислить количество таких i
это [n / i] = d
. Его можно найти в O(1)
для фиксированного d
воспользовавшись тем, что [n / i] = d
означает i * d <= n and i * (d + 1) > n
, что дает [n / (d + 1)] < i <= [n / d]
.
первая и вторая группы обрабатываются в O(sqrt(n))
, что дает O(sqrt(n))
раз в общей сложности.
большие n
используйте формулу:
здесь
( является трансцендентным числом.)
посмотреть константа Эйлера-Машерони статьи для получения дополнительной информации.