570 likes | 1.06k Views
Сложност на алгоритми. Кр. Манев, Бургас 09.2008. Задачи и алгоритми. З адача (в математически смисъл): „ Упражне-ние по математика, физика и др., което се разрешава чрез разсъждения и изчисления “ . (Тълковен речник на българския език).
E N D
Сложност на алгоритми Кр. Манев, Бургас 09.2008
Задачи и алгоритми • Задача (в математически смисъл): „Упражне-ние по математика, физика и др., което се разрешава чрез разсъждения и изчисления“. (Тълковен речник на българския език). • Дьорд Пойя : „Да имаме задача означава да търсим съзнателно някое действие, годно за постигането на една ясно схващана, но не и непосредствено достижима цел. Да се реши една задача означава да се намери това действие.“
Задачи и алгоритмиМасова задача • Дадено: МножествоX= {x1, x2, x3,…} от обекти с определени свойства (всяко xi може да приема множество стойности – параметър) и множествоD= {d1, d2, d3,…} от операции (действия). • Търси се: МножествоY= {y1, y2, y3,…} от обекти с определени свойства - резултат, което да се получи от X, с прилагане на операциите от D. • Решение: Не резултаът, а последователността от операции, с които сме го намерили
Задачи и алгоритмиПримери замасови задачи • Дадени са естествените числа iиj.С прилагане на аритметични операции (вкл. сравняване) да се намери НОД на iиj. • Дадени са естествените числа i, jиk>0.С при-лагане на аритметични операции (вкл. сравня-ване) да се провери дали k е НОД на iиj. са масови задачи. • Да се намери НОД на 12 и 30. • Да се провери дали 6 е НОД на 12 и 30. са екземпляри на тези масови задачи – получени са с фиксиране на някои/всички параметри.
Задачи и алгоритмиРешение намасова задача (МЗ) • Решение на МЗще наричаме строго определена крайна последователност от стъпки (процедура), която започвайки от зададените стойности на параметрите на произволен екземпляр на МЗ и прилагайки на всяка стъпка някоя от допустимите операции, намира резултата за този екземпляр на масовата задача - АЛГОРИТЪМ
Задачи и алгоритмиРазрешими и неразрешими МЗ • Не за всяка МЗ съществува алгоритъм, който да намира резултат за всеки екземпляр на задачата – неразрешима МЗ. • Разрешимите МЗ също не са безпроблем-ни – много често, първият попаднал ни алгоритъм за рещаване на МЗ е неудов-летворителен.
Сложност на задачи и алгоритми • Много е важно за тази теория, да сe различават понятията сложност на алгоритъм и сложност на задача. • Обикновено, същствуват много алгоритми за решаване на МЗ и всички те могат да имат различна сложност. • Сложност на МЗ бихме могли да нарчем сложността на най-добрия алгоритъм който я решава. Ако знаехме кой е той!!!
Сложност на алгоритми • За да можем да дефинираме понятието сложност на алгоритъм ще трябва да направим важни стъпки: • Да фиксираме множеството от възможни стойности на параметрите на МЗ. • Да фиксираме изчислителния формализъм – операции, начин на представяне на процедурите (синтаксис и семантика). • Да определим изключително важното за теорията понятие размер на входа.
Сложност на алгоритмиСтойности на параметрите • Ще фиксираме като множеството от възможни стойности на параметрите на МЗ стойности от множеството на целите числа. • Компютърът релно работи с подмоножество на целите числа, но то с годините се разширява – 8b, 16b, 32b, 64b, 128b... • Дробните числа в компютъра се моделират с двойка цели – мантиса и порядък. Представя-нето е неравномерно, аритметиката проблем-на: може да се случи (a*b)*c a* (b*c)
Сложност на алгоритмиИзчислителен формализъм • Машината с произволен достъп до паметта (МПД) е упростен модел на съвременния компютър • Всяка клетка на ВЛ, ИЛ и паметта с произволен достъп, както и Акумула-торът (AC) съдържат r-разряд-но цяло - r-разрядна МПД.
Сложност на алгоритмиИзчислителен формализъм • Програмната памет е със-тавена от клетки. Всяка клетка има свой адрес и може да съдържа по една команда. • Клетката PC (брояч на ко-мандите) съдържа адреса на текущо изпълняваната команда - r-разрядно положително цяло.
Сложност на алгоритмиИзчислителен формализъм • Всяка команда е съставена от КОД + АРГУМЕНТ (r-разряда) • Използват се 3 типа команди • Непосредствен операнд: CODE# Z– операцията се извъшва с числото Z • Спряк адрес: CODE A – операцията се извъшва с числотонамиращо се в клетка с адрес A - <A> • Скосвен адрес: CODE& A– операцията се извъшва с числотонамиращо се в клетка с адрес, който се намира в клетката с адрес A - <<A>>.
Сложност на алгоритмиИзчислителен формализъм • Да разгледаме за пример командите за събиране • Непосредствен операнд: ADD# 100– към съдържанието на АC се добавя 100. Означаваме <AC>:=<AC>+100 • Спряк адрес: ADD 100– към съдържанието на АC се добавя съдържанието на клетката с адрес 100 -<AC>:=<AC>+<100> • Скосвен адрес: ADD& 100– към съдържани-ето на АC се добавя съдържанието на клет-ката с адрес, който се намира в клетката с адрес 100 - <AC>:=<AC>+<<100>>. • При всяка от тези команди <PC>:=<PC>+1
Сложност на алгоритмиИзчислителен формализъм • Подобни са всички останали команди на МПД от „аритметичен“ тип: • Изваждане SUB# Z, SUB A, SUB& A • Умножение MUL# Z, MUL A, MUL& A • Деление DIV# Z, DIV A, DIV & A • Остатък при деление MOD# Z, MOD A, MOD & A • Побитова дизюнкция OR# Z, OR A, OR & A • Побитова конюнкция AND# Z, AND A, AND & A • Изместване наляво SHL# Z, SHL A, SHL & A • Изместване надясно SHR# Z, SHR A, SHR & A • и.т.н.
Сложност на алгоритмиИзчислителен формализъм • Команди за работа с Акумулатора: • LOAD# Z - <AC>:=Z, <PC>:=<PC>+1 • LOAD A - <AC>:=<A>, <PC>:=<PC>+1 • LOAD & A - <AC>:=<<A>>, <PC>:=<PC>+1 • STORE A - <A>:=<AC>, <PC>:=<PC>+1 • STORE& A - <<A>>:=<AC>, <PC>:=<PC>+1 • Команди за вход и изход • INPUT - <AC>:=<поредна клетка на ВЛ> <PC>:=<PC>+1 • OUTPUT – <поредна клетка на ИЛ>:=<AC> <PC>:=<PC>+1
Сложност на алгоритмиИзчислителен формализъм • Команди за преходи: • JMP B - <PC>:= B • JMPZ B – ако <AC> = 0, <PC>:=B, иначе <PC>:=<PC>+1 • JMPP B – ако <AC> > 0, <PC>:=B, иначе <PC>:=<PC>+1 • JMPN B – ако <AC> < 0, <PC>:=B, иначе <PC>:=<PC>+1 • Команда за прекратяване на работата: • STOP
Сложност на алгоритмиИзчислителен формализъм • Задача: да се състави програма за МПД, която въвежда от ВЛ положителни числа до срещане на 0 и запомня в паметта тези числа (нещо като масив) и броя им. • Първата работа при съставяне на програма за МПД е да си разпределим паметта: • В клетката с адрес 0 ще си организираме индекс на масива от числа, а като завършим четенето от него ще намерим и броя на числата • В клетката с адрес 1 ще поставим първото въведено число, в клетката с адрес 2 – второто, в клетката с адрес 3 –третото и т.н.
Сложност на алгоритмиИзчислителен формализъм 0) LOAD# 1// поставяме 1 в АС 1) STORE 0 // съхраняваме 1 в клетка 0 (индекс) 2) INPUT // въвеждаме поредно число в АС 3) JMPZ 9 // ако е въведена 0 - команда 9) 4) STORE& 0 // записваме числото в адреса от клетка 0 5) LOAD 0 // стойността на индекса - в АС 6) ADD# 1 // увеличаваме индекса с 1 7) STORE 0 // нова стойност на индекса в клетка 0 8) JMP 2 // връщаме се за ново четене 9) LOAD 0 // стойността на индекса в АС 10) SUB# 1 // намаляваме индекса с 1 11) STORE 0 // бройна числата в клетка 0 12) OUTPUT // извеждаме резултата 13) STOP // прекратяваме изпълнението
Сложност на алгоритмиРазмер на входа • Последната стъпка, която трябва да направим, преди да дефинираме сложност на алгоритъм, е да кажем как ще определяме размер на входа. • Определянето на размер на входа е много сложна задача, затова ще отложим засега окончателното определяне на тази стъпка. • За сега ще считаме, че размерът на входа е приблизително равен на броя на прочетените от ВЛ числа. За нашия пример нека да вземем за размер на входа броя N на положителните числа, които четем от лентата. Това, че изключваме прочетената 0 от рзмера на входа е несъществено.
Сложност на алгоритмиСложност по време • Сложност по време на един алгоритъм A ще наричаме сложността на програмата за МПД, с която е реализиран.Да означим с #броя на командите, изпълнени от МПД при работата й върху вход .Дефинираме два вида сложност по време на програми за МПД: • сложност по време в най-лошия случай: tA(N) = max с размер N{#} • сложност по време в средния случай: tA*(N) = с размер N{#}/ |{с размер N}| • По различни причини ще се занимаваме само със сложността в най-лошия случай.
Сложност на алгоритмиСложност по памет • Сложност по памет на един алгоритъм A ще наричаме сложността на програмата за МПД, с която е реализиран.Да означим с @броя на клетките памет, използвани от МПД при работата й върху вход .Дефинираме два вида сложност по памет на програми за МПД: • сложност по памет в най-лошия случай: sA(N) = max с размер N{@} • сложност по памет в средния случай: sA*(N) = с размер N{@}/ |{с размер N}| • По различни причини няма да се занимаваме сега със сложността по памет.
Сложност на алгоритмиСложност по време 0) LOAD# 1// 1 път 1) STORE 0 // 1 път 2) INPUT // N+1 пъти 3) JMPZ 9 // N+1 пъти 4) STORE& 0 // N пъти 5) LOAD 0 // N пъти 6) ADD# 1 // N пъти 7) STORE 0 // N пъти 8) JMP 2 // N пъти 9) LOAD 0 // 1 път 10) SUB# 1 // 1 път 11) STORE 0 // 1 път 12) OUTPUT // 1 път 13) STOP // 1 път
Сложност на алгоритмиСложност за примера • И така, за нашия пример получаваме: • за сложносттта по време в най-лошия случай: tA(N) = 7.N + 9 • за сложността по време в средния случай, тъй като всички случаи са еднакви: tA*(N) = tA(N) = 7.N + 9 • за сложносттта по памет и в най-лошия случайи в средния случай, тъй като всички случаи са еднакви: sA*(N) = sA(N) = N + 1
Сложност на алгоритмиПрограма на С • В състезателната дейност не използваме МПД и затова ще се наложи да пренесем представе-ната теория в друг изчислителен формализъм – език за програмиане. • По понятни причини ще използаме езика С, но пренасянето на теорията няма да се различава съществено, ако използаваме друг език за програмиране. • Основен принцип при пренасянето ще бъде следният: ще се опитаме да съпоставим на програмата на С такава сложност, каквато бихме получили, ако програмата беше написана на езика на МПД.
Сложност на алгоритмиПрограма на С • Сложност tе(N) на израз е в програма на С, както и сложността на съответния оператор е; ще дефинираме като броя на всички знаци на операции в израза, плюс броя на всички скоби (както “кръгли”, така и индексни), влючително и тези пропуснати заради приоритети. • Пример: (m[i+1])=(a+(b*C))- сложност 12 а всъщност е 10, но това е удовлетворително LOAD iMUL c ADD# 1STORE d ADD mLOAD a STORE tADD d LOAD bSTORE& t
Сложност на алгоритмиПрограма на С • Сложността на блока от оператори {oper 1;oper 2;…;oper K; } в програма на Сще дефинираме като tblck(N) = toper 1(N) + toper 2(N) + … + toper K(N). • Сложността на условния оператор if expr oper в програма на Сще дефинираме като tif(N) = texpr(N) + toper (N). • Сложността на условния оператор if expr oper 1 else oper 2 в програма на Сще дефинираме като tif_else(N) = texpr(N) + max {toper 1(N), toper 2(N)}
Сложност на алгоритмиПрограма на С • За да определим сложността на оператора за цикъл while expr oper първо определяме броя c(N) наитерациите. • Ако сложността toper не зависи от N, тогава twhile(N) = (c(N)+1)texpr(N) + c(N)toper. • Ако сложността toper зависи от N, да означим с ti(N) сложносттана i-тата итерация. Тогава twhile(N) = (c(N)+1)texpr(N) + t1(N) + t2(N)+…+ tc(N)(N) • За цикъла do operwhile expr постъпваме аналогично
Сложност на алгоритмиПрограма на С • За пример нека да определим сложността на алгоритъма “на мехурчето” за сортиране на Nчисла. За размер на входа да приемем N. int a[],N; bubble_sort() { int i,j,t; for(i=N-1;i>0;i--) for(j=1;j<=i;j++) if(a[j]>a[j+1]) {t=a[j];a[j]=a[j+1];a[j+1]=t;} }
Сложност на алгоритмиПрограма на С int a[],N,; bubble_sort() { int i,j,t; 3 2 3 for(i=N-1;i>0;i--) 3+N*5+i=1,…,N-1f(i) 2 3 3 for(j=1;j<=i;j++)2+(i+1)*6+i*30= 10 =36*i+8=f(i) 30 if(a[j]>a[j+1]) 59 6 20{t=a[j];a[j]=a[j+1];a[j+1]=t;} }
Сложност на алгоритмиПрограма на С И така за сложността на алгоритъма на мехурчето получаваме: tbubble_sort(N)= 3 +5N + i=1,2,…,N-1 f(i) = = 3 +5N + i=1,2,…,N-1 (36i + 8) = = 3 +5N+8(N – 1) +36 i=1,2,…,N-1 i = = 13N – 5 + 18N(N – 1) = = 18N2– 5N – 5
Сложност на алгоритмиИзвикване на програми • За да определим сложността на оператор, в който има извикване на функция f, трябва • да разгледаме съответната функция f като имплементация на специфичен алгоритъм; • да определим за този алгоритъм какво е размер N* на входаи да пресметнем сложността tf(N*); • Да се върнем в оператора и да изразим N* като функция на N: N* = g(N). • Тогава сложността на извикването ще бъде tf(g(N))
Сложност на алгоритмиИзвикване на програми • За пример нека да разгледаме версия на алгоритъма “на мехурчето” за сортиране на първите N* измежду зададени Nчисла. int a[],N; bubble_sort(int N*) { int i,j,t; for(i=N*-1;i>0;i--) for(j=1;j<=i;j++) if(a[j]>a[j+1]) {t=a[j];a[j]=a[j+1];a[j+1]=t;} } Сложността му е tbubble_sort(N*) = 18.(N*)2 – 15N* – 5
Сложност на алгоритмиИзвикване на програми • Да рагледаме следния алгоритъм. • int a[],N,; alabala() { int i; for(i=1;i<=N;i++) bubble_sort(int i)} Сложността му ще бъде talabala(N) = 2 + (N + 1).6 + i=1,2,…,N tbubble_sort(i) = = 6N + 8 + i=1,2,…,N (18.i2– 15i – 5) = = 6N + 8 + 3N(N + 1)(2N + 1) –7.5N(N + 1) – 5N= = 6N +8 + 6N3 + 9N2+ 3N – 7.5N 2 – 7.5N – 5N= =6N3 +1.5 N2 – 3.5N + 8
Сложност на алгоритмиРекурсивни извиквания • Да рагледаме следния алгоритъмза сортиране на масив, съдържащ рекурсивни извиквания: int a[],N; int main(){merge_sort(1,N);return 0;} merge_sort(int i,int j) { int k; if(i==j) return; k=(i+j)/2;merge_sort(i,k); merge_sort(k+1,j); merge(i,k,j);} Сложността му е: t(N)=t merge_sort(N)=3+4+2tmerge_sort (N)+tmerge (N),N > 1 t(1)= const - рекурентно отношение
Сложност на алгоритмиРекурсивни извиквания • Да рагледаме популярния алгоритъмза търсене на число в сортиран масив, съдържащ рекурсивно извикване: int a[],N,x; int main(){bin_search(1,N);return 0;} bin_search(int i,int j) { while(i<=j) {int k=(i+j)/2; if(x==a[k]) return k; if(x<a[k])bin_search(i,k-1); else bin_search(k+1,j);}} Сложността му се изразява с рекурентното отношение: t(1)= const t(N)=t bin_search(N)=3+4+6+6+tbin_search(N/2), N > 1