Хотя подход генерации и тестирования прост для понимания, он также не очень эффективен, если вы хотите использовать его для больших чисел. Вместо этого мы можем использовать принцип включения-исключения .
Идея состоит в том, чтобы сначала сложить слишком много чисел, посмотрев на коэффициенты 3, 5 и 7 по отдельности. Затем мы вычитаем те, которые мы сосчитали дважды, то есть, кратные 3 * 5, 3 * 7 и 5 * 7. Но теперь мы вычли слишком много и нам нужно снова добавить кратные 3 * 5 * 7.
Начнем с нахождения суммы всех целых чисел 1..n
, кратных k
. Сначала мы узнаем, сколько их, m = n / k
, округляется в меньшую сторону благодаря целочисленному делению. Теперь нам нужно просто подытожить последовательность k + 2*k + 3*k + ... + m*k
. Мы выделяем k
и получаем k * (1 + 2 + ... + m)
.
Это хорошо известный арифметический ряд, который, как нам известно, суммируется с k * m * (m + 1)/2
(см. номер треугольника ).
private long n = 9999;
private long multiples(long k) {
long m = n / k;
return k * m * (m + 1) / 2:
}
Теперь мы просто используем включение-исключение, чтобы получить окончательную сумму:
long sum = multiples(3) + multiples(5) + multiples(7)
- multiples(3*5) - multiples(3*7) - multiples(5*7)
+ multiples(3*5*7);
Это будет намного лучше масштабироваться до большего n
, чем просто зацикливание всех значений, но остерегайтесь переполнения и при необходимости измените на BigInteger
с.