Как решать задачи ресурса "проект Эйлер"? Георгий Гуляев 3 июля 2026 г. На сайте https://projecteuler.net опубликовано на данный момент более 1000 задач и эта коллекция со временем продолжает пополнять- ся. Придумывают эти задачи математики со знаниями в области про- граммирования. В большинстве своем это задачи из теории чисел или комбинаторики, но встречаются и другие темы. Каждая задача имеет сложность в процентах от 0 до 40, в зависимо- сти от того как быстро ее решают пользователи ресурса. Решение, как правило, требует написания некоторого небольшого кода на каком-либо языке программирования, но для более сложных задач одного програм- мирования бывает недостаточно - требуются знания и умения в области математики. Решив задачу (введя правильный численный ответ), пользователь полу- чает доступ к закрытым материалам задачи и решениям других поль- зователей, а также может опубликовать свое решение. Авторы ресурса, естественно, не хотели бы, чтобы решения задач и ответы к ним публи- ковались где-либо еще. Разрешено только в целях обучения обсуждать идеи решения первых 100 задач. Поэтому, в данной статье для демонстрации поиска решения задач такого рода, мы не будем использовать задачи ресурса https://projecteuler. net , а рассмотрим свою оригинальную задачу. 1 Задача . На плоскости расположена прямоугольная таблица, в клетках которой записаны все натуральные числа снизу вверх по диагоналям в следующим порядке: Определим функцию f ( x, y ) = числу в столбце x и строке y таблицы, тогда f (1 , 1) = 1 , f (2 , 1) = 2 , f (1 , 2) = 3 , f (2 , 2) = 5 , f (5 , 3) = 24 , ... . 1. Вычислить f (123456789 , 987654321) . 2. Определим функцию s ( n ) = n X x =1 n X y =1 ( f ( x, y ) − xy ) Можно проверить, что s (123) = 75367635 , s (123456) = 77432320610578913280 . Вычислить s (1234567890) . В качестве ответа использовать 10 последних цифр результата вычисления. Решение . 1. Первое задание - относительно простое. Тут присутствуют чисто тех- нические сложности при написании функции f ( x, y ) . Если не применять никакой математики, то можно просто строить всю таблицу ( x, y ) до некоторого значения n и затем использовать ее для вычисления f ( x, y ) . Программа на языке программирования Julia: function table(n) l = [(1,1)] for i in 2:n (x,y) = l[end] if x==1 x=y+1 y=1 push!(l,(x,y)) else x-=1 y+=1 push!(l,(x,y)) end end l end julia> l = table(28) 28-element Vector{Tuple{Int64, Int64}}: (1, 1) (2, 1) (1, 2) (3, 1) (2, 2) (1, 3) (4, 1) (3, 2) (2, 3) (1, 4) (5, 1) (4, 2) (3, 3) (2, 4) (1, 5) (6, 1) (5, 2) (4, 3) (3, 4) (2, 5) (1, 6) (7, 1) (6, 2) (5, 3) (4, 4) (3, 5) (2, 6) (1, 7) julia> l[25] (4, 4) Тут мы вывели все координаты ( x, y ) для чисел от 1 до 28, например для 25 это (4 , 4) . Лучше, конечно, не строить таблицу в памяти, так как для больших n она будет занимать много места, а только пробегать по ней. Так появляется наша первая версия для функции f ( x, y ) : function f(x,y) i = 1; j = 1; r = 1 while i<x+y&&j<x+y if i==x&&j==y return r end if i==1 i=j+1 j=1 else i-=1 j+=1 end r+=1 end end julia> f(3,4) 19 julia> f(4,4) 25 Координаты ( x, y ) всегда попадают в квадрат со стороной менее x + y , поэтому действуют ограничения: i < x + y и j < x + y . Эта програм- ма очень неэффективная, с ее помощью решить первую задачу мы не сможем: julia> @time f(12345,54321) 2.457699 seconds 2222132101 f (123456 , 654321) - уже дождаться невозможно. Пришло время математики. Придется немного порассуждать, чтобы вы- вести общую формулу для f ( x, y ) . В таблице на картинке в условии зада- чи возьмем какое-нибудь число в первом ряду, например 16. Количество чисел до него можно вычислить как сумму 1 + 2 + 3 + 4 + 5 = 15. Это сумма арифметической прогрессии: 5(5+1) 2 , таким образом 16 = 5(5+1) 2 + 1 или 16 = 6(6 − 1) 2 + 1 , учитывая, что 16 - шестое число в первом ряду. В общем случае f ( x, 1) = x ( x − 1) 2 + 1 . Теперь, для того чтобы получить формулу для f ( x, y ) , рассмотрим частный случай: f(x+y-1,1) = (x+y-1)(x+y-2) 2 + 1 По построению таблицы, при b < a у нас справедлива формула f ( a, b ) = f ( a − 1 , b + 1) + 1 . Применяя ее y − 1 раз к формуле для f ( x + y − 1 , 1) , получаем f(x,y) = (x+y-1)(x+y-2) 2 + y Теперь мы можем решить первую задачу без проблем: f(x,y) = div((x+y-1)*(x+y-2),2) + y julia> @time f(123456789,987654321) 0.000000 seconds 617283948703703707 2. Теперь, когда у нас есть такая простая формула, кажется, что и со вто- рой частью задачи у нас не будет проблем, но не тут то было. Функцию для вычисления суммы составить несложно: function sm(n) f(x,y) = div((x+y-1)*(x+y-2),2) + y s = Int128(0) for i in 1:n, j in 1:n s+=f(i,j)-i*j end s end Здесь мы накапливаем сумму в переменной типа Int128, так как при больших n она может превзойти Int64. julia> sm(123) 75367635 julia> sm(12345) 7740880282734900 julia> @time sm(123456) 26.305627 seconds 77432320610578913280 Результаты вычисления sm (123) и sm (123456) совпали с приведенными в условии задачи, однако становится очевидным, что эта функция так- же недостаточно эффективна и не позволит нам за приемлемое время вычислить требуемое значение: sm (1234567890) . Опять обратимся к математике. Если существует возможность вычис- лить требуемую сумму быстрее, чем полный перебор всех слагаемых, то должно быть какое-то соотношение (формула, теорема), позволяющее вычислять такие суммы. Прежде всего, заметим, что найденную формулу для f ( x, y ) можно немно- го видоизменить, раскрывая скобки: f ( x, y ) = ( x + y − 1)( x + y − 2) 2 + y = ( x + y − 1)( x + y ) − 2( x + y − 1)) 2 + y = ( x + y )( x + y − 1)) 2 − x + 1 то есть f(x,y) = (x+y)(x+y-1) 2 − x + 1 Обратим внимание на то, что в формуле присутствуют так называе- мые треугольные числа вида: T n = n ( n − 1) 2 . Поиском в интернет нахо- дим следующие соотношения для них (смотри, например, статью из ви- кипедии https://ru.wikipedia.org/wiki/%D0%A2%D1%80%D0%B5%D1%83% D0%B3%D0%BE%D0%BB%D1%8C%D0%BD%D0%BE%D0%B5_%D1%87%D0%B8%D1%81%D0% BB%D0%BE ): T n + m = T n + T m + mn T 1 + T 2 + T 3 + ... + T n = 0 + 1 + 3 + 6 + ... + n ( n − 1) 2 = n 3 − n 6 Таким образом, на основании первого равенства, формулу для f ( x, y ) можно переписать в виде: f ( x, y ) = ( x + y )( x + y − 1) 2 − x + 1 = T x + y − x + 1 = T x + T y + xy − x + 1 Применяя теперь эту формулу для f ( x, y ) и второе равенство, получаем s ( n ) = n X x =1 n X y =1 ( f ( x, y ) − xy ) = n X x =1 n X y =1 ( T x + T y − x +1) = n X x =1 ( nT x + n 3 − n 6 − nx + n ) = n n 3 − n 6 + n n 3 − n 6 − n n ( n + 1) 2 + n 2 = 2 n ( n 3 − n ) − 3 n 2 ( n + 1) + 6 n 2 6 = 2 n 4 − 2 n 2 − 3 n 3 − 3 n 2 + 6 n 2 6 = n 2 (2 n 2 − 3 n + 1) 6 = n 2 (2 n − 1)( n − 1) 6 Таким образом, сумму s ( n ) можно вычислять, даже не зная всех ее сла- гаемых. Теперь, без проблем мгновенно получается требуемый результат: sm(n) = div(n^2*(2*n-1)*(n-1),6) julia> sm(123) 75367635 julia> sm(Int128(123456)) 77432320610578913280 julia> sm(Int128(1234567890)) 774352408386692628193094876226110850 Последние 10 цифр: 6226110850 Выводы . Для решения подобных задач бывает недостаточно умения просто программировать на каком-либо языке. Поиск эффективного ал- горитма может потребовать от вас некоторых знаний в области матема- тики и определенной математической культуры для проведения логиче- ских рассуждений и преобразований математических выражений.