Применение умножения матриц и быстрого возведения в степень и линейные последовательности в олимпиадных задачах по информатике. 2012.03.17 Общая рабочая тетрадь. НЕ МУДРИТЬ!!! - см. KISS. Умножение матриц Type arr=array[1..100,1..100] of integer; rec=record c:arr; nr,nc,z:longint; end; Function mul(a,b:rec):rec; Var t:rec; Begin if a.nc<>b.nr then begin mul.z:=-1; exit; end; t.n:=a.n; t.m:=b.m; for i:=1 to a.nr do for j:=1 to b.nc do t.c[i,j]:=0; for i:=1 to a.nr do for j:=1 to b.nc do for k:=1 to a.nc do t.c[i.j]:=t.c[i,j]+a.c[i,k]*b.c[k,j]; mul:=t; end; Возведение в степень вычисляем за O(log n) умножений матриц. Числа Фибоначчи и обобщения P - простое число около 10^9. f[0] = f[1] = 1; f[k] = ( f[k-1]+f[k-2] ) mod P. fib[n] = fib[n-1] X = Y 1 1 1 0 a b c d * fib[n-1] fib[n-2] * A B X = Aa+Bb Y = Ac+Bd f[k] = ( 3 * f[k-1]+f[k-2] ) mod P. f[k-1] = 1 * f[k-1] fib[n] = fib[n-1] 3 1 1 0 * fib[n-1] fib[n-2] f[0]=f[1]=f[2]=1 f[k]=f[k-1]+f[k-2]+f[k-3]; f[n] = 1 1 1 * f[n-1] f[n-1] 1 0 0 f[n-2] f[n-2] 0 1 0 f[n-3] Матрицы только квадратные. Ключевой момент - быстрое возведение матриц в степень, а в степень можно возводить только квадратные матрицы. Быстрое построение матриц f[n] = ?*f[n-1] ?*f[n-2] ?*f[n-3] * f[n-1] f[n-1] ?*f[n-1] ?*f[n-2] ?*f[n-3] f[n-2] f[n-2] ?*f[n-1] ?*f[n-2] ?*f[n-3] f[n-3] Прикладываем правый столбик к строкам и проставляем коэффиенты по формуле либо копируем, когда член нужен просто для вычисления предыдущего. f[n] = 5*f[n-1]+7*f[n-2]+3*f[n-3] f[n] = 5*f[n-1] 7*f[n-2] 3*f[n-3] * f[n-1] 1*f[n-1] 0*f[n-2] 0*f[n-3] f[n-2] f[n-2] 0*f[n-1] 1*f[n-2] 0*f[n-3] f[n-3] Пропущенные члены f[n]=f[n-1]+5*f[n-3] f[n] = 1 0 5 * f[n-1] f[n-1] 1 0 0 f[n-2] f[n-2] 0 1 0 f[n-3] Берем все пропущенные члены тоже!! НЕ МУДРИТЬ! Свободный член f[n]=2*f[n-1] + 3*f[n-2] + 5 f[n] = 2 3 5 f[n-1] 1 0 0 f[n-2] 1 0 0 1 1 Несколько последовательностей f[n]=f[n-1]+3*g[n-1] g[n]=2*f[n-1]+g[n-1] * f[n-1] f[n-1] f[n] = g[n] 1 3 2 1 * f[n-1] g[n-1] f[n]=5*f[n-1]+3*g[n-1]+2*g[n-2] g[n]=2*f[n-1]+g[n-1]+5*g[n-2] f[n] = * f[n-1] 5 3 2 g[n] 2*f[n-1] 1*g[n-1] 5*g[n-2] g[n-1] g[n-1] 0*f[n-1] 1*g[n-1] 0*g[n-2] g[n-2] Как нам получить g[n-1] из f[n-1], g[n-1] и g[n-2]? СКОПИРОВАТЬ! Двигаемся по одному шагу. f[n]=5*h[n-1]+g[n-2]+f[n-3]+1 g[n]=3*h[n-2]+9 h[n]=7*g[n-1]+3*g[n-2]+3*f[n-1]+4*h[n-1] Если есть свободный член, то в двух столбиках дописывает 1. f[n] = 0 0 1 0 1 5 0 1 f[n-1] 1 0 0 0 0 0 0 0 f[n-2] f[n-2] 0 1 0 0 0 0 0 0 f[n-3] g[n] 0 0 0 0 0 0 3 9 g[n-1] g[n-1] 0 0 0 1 0 0 0 0 g[n-2] h[n] 3 0 0 7 3 4 0 0 h[n-1] h[n-1] 0 0 0 0 0 1 0 0 h[n-2] 1 0 0 0 0 0 0 0 1 1 Зависимость от n-го члена f[n]=g[n-1]+2*g[n-2]+f[n-1] g[n]=3*g[n-2] + 3*f[n] Нужно выражать, быстрее это делать в самой матрице. * f[n-1] f[n] = 1 1 2 * f[n-1] g[n] 3 3 3+2*3 g[n-1] g[n-1] 0 1 0 g[n-2] ds f[n]=2*g[n]+2*g[n-1]+f[n-1] g[n]=3*f[n]+3*g[n-1] + 3*f[n-1] Возможно стоит просто решить систему линейных уравнений? Возможно противоречие и т.д. - это уже другая тема. В какую степень возводить? f[0] = f[1]=1 f[n] = f[n-1] + f[n-2] fib[n] = fib[n-1] 1 1 1 0 * fib[n-1] fib[n-2] В какую степень нужно возвести матрицу? Для n=k в какую степень нужно возвести матрицу M и где взять fib[n]? fib[n] = fib[n-1] S(n)=M^x * S(y) fib[n]=S(n)[z] (z=0 или 1) Чему равны x,y,z? x=n-1 y=1 z=0 S(1)= {fib[1], fib[0]} S(2)=M^1*S(1) ==== А если нужно посчитать fib[0]? Просто берем, если задано. S(n) - столбик из 2-х ел M = 1 1 1 0 ==== f[0]=f[1]=f[2]=1 f[n]=S(n)[0] * M^(n-2) * S(2) Ориентируемся по первым членам. Вычислительная сложность f[0] = f[1]=1 f[n] = f[n-1] + f[n-2] O(log2(n)*8+8)=O(log2(n)) В общем случае O(log_k(n))=O(log_n(n)). потому что log_k1(n)/log_k2(n)=const ==== f[n]=5*h[n-1]+g[n-2]+f[n-3]+1 g[n]=3*h[n-2]+9 h[n]=7*g[n-1]+3*g[n-2]+3*f[n-1]+4*h[n-1] O(log2(n)*8^3)=O(log2(n)) Если константа-размерность матрицы небольшая , то ею можно пренебречь. Арифметическая прогрессия s(n)=1+2+...+n Как посчитать? s=(n*n+n)/2 O(1) P-большое простое около 10^9 s(n)=(1+...+n) mod P s=(((((n mod P)*(n mod P)) mod P + n mod P) mod P) * ((2^(p-2)) mod P)) mod P 2^(p-2)=2^(-1)*mod P Мы сейчас не рассматриваем проблемы с переполением. O(log2(p)) K-большое не-простое число около 10^9 Проблема - не всегда существует обратное для непростого К. Пример: K=4 => 2^(4-2)=4 => 4 mod 4 = 0. 0*2 <> 1. s(n)=s(n-1)+f(n); - зависящее от n, но выражаемо. f(n)=f(n-1)+1 s(0)=0 f(0)=0 Преимущество матриц - используется только * и + - можно по любому не обязательно простому модулю работать. НО! Матрицы хорошо работают только для линейных соотношений. O(log2(n)) ======== s(n)=1+....+n p(n)=s(1)+....+s(n) s[n]=s[n-1]+f[n]; f[n]=f[n-1]+1=n p[n]=p[n-1]+s[n] f[n] = 1 0 0 1 * f[n-1] s[n] 1 1 0 1 s[n-1] p[n] 1 1 1 1 p[n-1] 1 0 0 0 1 1 Задача. Количество путей длины n на графе. Существует ли путь в ориентированном графе длины n из вершины 0 в вершину k-1? На входе матрица смежностей M. Kil=(M^n) [0][k-1]; Если Kil>0, то существует. Что делать в случае переполнения? заменить умножение на AND заменить сумму на OR. Почему мы можем заменить * и + на AND и OR? Мы умеем считать количество путей длины n. === Сколько существует путей длины <= n? Размерность матрицы k. v(i)[j] - количество путей длины j из 0 в i-ую вершину. v(0)[n] = 0 . 0 . 0 . 0 0 . v(0)[n-1] v(p,q)[n-1] v(q)[n-1] 0 v(k-1)[n] m(k-1,0) . .,, ,.. m(k-1,k-1) 0 v(k) a(k-1)[n] m(k-1,0) ... ... ... m(k-1,k-1) 1 a(k-1)[n-1] v(k-1)[n] - сколько путей длины ровно n. a(k-1)[n] - количесво путей длины <= n из 0-ой вешині в k-1-ую. a(k-1)[n] = a(k-1)[n-1] + v(k-1)[n]. => дописываем 1 паременную. Домашнее задание. Спросить у преподавателя по алгебре, когда в умножении матриц * и + можно заменить на другие операции (например на AND и OR). На какие другие операции можно заменять * и + ? (Если повезет, то вам прочитают лекцию ;) ) Задача. Пирамидка. Складываем пирамидку из шариков. Если 1 слой - то 1 шарик, если 2 слоя - то 4 шарика (3 шарика, сверху 1), если 3 слоя, то 10 (6+3+1). Сколько шариков в пирамидке высотой n? В n-ом слое L(n) прибавляется n апельсинок по отношению к предыдующему. L(n)=L(n-1)+n S(n)=S(n-1)+L(n) - всего апельсинок. => строим матрицу и возводим в степень. Сложность: O(log(n)). Домашнее задание. Выучить метод неопределенных коефициентов, решить им задачу про пирамидки за O(1). (Возможно поможет преподаватель по алгебре). Вывод: увидели линейное соотношение - применяем умножение матриц! Задачи на тему умножения матриц TopCoder: srm376_div1_500 srm377_div1_500 srm377_div1_950 srm403_div1_500 srm147_div1_1000 (not the best solution, but straight-forward) srm428_div1_500 Рекомендации * Больше практики, туров, соревнований. * Избавляйтесь от Паскаля, используйте C++, C#, Java - за одно и работу сразу найдете ;)