|
Разделы
Главная
Сапромат
Моделирование
Взаимодействие
Методы
Инновации
Индукция
Исследования
Факторизация
Частоты
Популярное
Как составляется проект слаботочных сетей?
Как защитить объект?
Слаботочные системы в проекте «Умный дом»
Какой дом надежнее: каркасный или брусовой?
Как правильно создавать слаботочные системы?
Что такое энергоэффективные дома?
|
Главная » Математика 1 ... 4 5 6 7 8 9 10 ... 28 глубину. Процедура поиска с возвращением для нахождения всех решений формально представлена в алгоритме 4.1. Алгоритм 4.1. Общий алгоритм поиска с возвращениями Sy =Ау, {Выделитькандидатов] к= 1; {Длиш частичного решения) while к>0 do begin while Sk * 0 do begin ak £ Sk, {Расширшт частичное решение } Sk = Sk- {ak}; {Удалитьвыбранного кандидата} if{aha2,..., ak) - решение then Сохранить решение; k = k+ 1; {Расшири ть частичное решение) с end; k = k- 1: {Вернуться, уменьшить частичное решение) end. Поиск с возвращением приводит к алгоритмам экспоненциальной сложности, так как из предположения, что все решения имеют длину не более , исследованию подлежат приблизительно п Y]Ak\ элементов. В предположении, что все .i \ = констан- п ты, получаем экспоненциальную сложность = С . Нужно помнить, что поиск с возвращением представляет собой только общий метод. Непосредственное его применение обычно ведет к алгоритмам, время работы которых недопустимо велико. Поэтому, чтобы метод был полезен, к нему нужно относиться как к схеме, с которой следует подходить к задаче. Схема должна быть хорошо приспособлена (часто это требует большой изобретательности) к конкретной задаче, так чтобы в результате алгоритм годился для практического использования. 4.2. Перестановки различных элементов Перестановки множества различных элементов относятся к часто порождаемым комбинаторным объектам. Без ограничения общности можно полагать, что элементами множества являются целые числа от 1 до п, т.е. рассматриваются перестановки целых чисел 1, п. Порождение перестановок на основе метода поиска с возвращениями выполняется в алгоритме 4.2. Заметим, что в процессе приспосабливания общего алгоритма 4.1 поиска с возвращениями к задаче порождения перестановок мы не вычисляли и не хранили явно множества о,. В этом случае легче и достаточно хранить только наименьшее значение из т.е. и следующее значение вычислять по мере необходимости. Проверка условия Sk -р 0 соответствует условию sk< /;, поскольку алгоритм устроен так, что перебор значений элемента выполняется в порядке их возрастания. Поэтому неравенство > п соответствует пустому множеству кандидатов = 0. Алгоритм 4.2. Порождение перестановок методом поиска с возвращением = 1; {Первыйкандидат} k = 1; {ДлиHi частичного решения) whilek>0 do begin whilesk<ndo begin ak = sk; {Расшир итъ частичное решение } sk=sk+l; {Удалитьвыбранного кандидата) while sk<n and not flag(sk)do sk=sk+ 1; ifk=n then Перестановка (%, a2,..., a ) - решение; else begin k = k + i, {Расширит! частичное решение) ** = i; while andnetfiagis, do =sk+\; end; end; k = k - 1; {Вернут ься, уменьшить частичное решение) end; Function flag(sk) {Slouch элемента ske перестановке (aaj,--, а^-О) flag= TRUE; / = 1; while i < k andflag do begin ifa, = sk then flag = FALSE; i= i+ 1; end. Программа на языке Pascal реализации рассмотренного метода генерации перестановок приводится в алгоритме 4.3. Следует отметить, что в программе размерность (длина) и перестановок читается из файла и порождаемые перестановки также сохраняются в файле. Попутно программа вычисляет время порождения всех перестановок с точностью до сотых долей секунды, которое сохраняется в конце файла сгенерированных перестановок. Алгоритм 4.3. Программа на Pascal е порождения перестановок методом поиска с возвращением Program Start Bac:kTrack; {Порождение перестановок} uses CRT,DOS; Const max n=20; Type Vector=array[l..max n] of Longing-Function Flag(Var a:Vector; sk:LongInt; k:Integer): Boolean; ( Поиск элемента sk в перестановке a[l],a[2],...,a[k-ll } Var i :Integer; yes :Boolean; begin; yes:=TP,UE; i:=l; while (i<k) and yes do begin if a[i]=sk then yes:=FALSE; i:=i+l; end; Flag:=yes; end;{Flag) Procedure BackTrack (var f-.Text; Var aiVector; n: Integer) ; { Генерация перестановок a[1],a[2]a[n] } Const m :LongInt=0; {Количество перестановок) Var s :Vector; i,k :Integer; begin; for i:=l to n do [1] И)п s[l]:=l; k:=l; while k>0 do begin while s[k]<=n do begin a[k]:=s[k]; repeat {Поиск следующего кандидата на место а [к.! ( s[к]:=s[к]+1; until or if k=n then begin {Перестановка найдена) т:=т+1; Write(f,m,) ); for i:=l to n do Write(f,a[iJ, ); WriteLn (f); else begin{Поиск первого кандидата на место а[к+1П k:=k+l; s[k]:=1; while (s[k]<=n) and Not Flag (a, s [k] , k) do s[k]:=s[k]+l; end; end; k:=k-l; end; end;{BackTrack} Var {Main} f .-Text; (Текстовый файл) Hour,Minute,Second,SeclOO,rHour,rMinute,rSecond, rSeclOO :Word; delta :LongInt; begin(Main) Assignif,BkTrack.in) Reset if) ; {Ф<шл открыт для чтения? Readl.n (f, n) ; {Ввод длины перестановки) Close(f); Assign(f,BkTrack.out); открыт для GetTime(Hour,Minute,Second,SeclOO); BackTrack (f,a,r.); GetTime(rHour,rMinute,rSecond, rSeclOO); delta:=rHour-Hour; delta:=delta* 60 + rMinute-Minute; delta:=delta* 60+rSecond-Second; delta:=delta*100+rSecl00-SeclOO; WritfeLn(f,Время счета=,delta div 100,., delta mod 100, сек'); Close(f); end{Main}. 4.3. Эффективное порождение перестановок Последовательность я! перестановок на множестве {1, 2;..., }, в которой соседние перестановки различаются так мало, как только возможно, - лучшее, на что можно надеяться с точки зрения мини- мизации объема работы, необходимого для порождения перестановок. Для того чтобы такое различие было минимально возможным, любая перестановка в нашей последовательности должна отличаться от предшествующей ей транспозицией двух соседних элементов. Такую последовательность перестановок легко построить рекурсивно. Для = 1 единственная перестановка {1} удовлетворяет нашим требованиям. мы имеем последовательность перестановоктг), л2, я3,.. на множестве {I, 2,..., п) в которой последовательные перестановки различаются только транспозицией смежных элементов. Расширим каждую из этих (и - 1)! перестановок, вставляя элемент п на каждое из я возможных мест. Порядок порождаемых таким образом перестановок будет следующим: (1234 1243 132JJ 1423 [4123 Г4132 .1432 1342 [1324 (3124 J3142 3412 [4312 (4321 3213421 4 3241 [3214 2314 , 12341 2431 [4231 (4213 21312143 [2134 Данную последовательность перестановок можно порождать итеративно, получая каждую перестановку из предшествующей ей и небольшого количества добавочной информации. Этодела-ется с помощью трех векторов: текущей перестановки я = л2,..., я ), обратной к ней перестановкир = (Pi,p2,-,P ) и записи направления в котором сдвигается каждый элемент / (-1, если он сдвигается влево; если вправо; и 0, если не сдвигается). Элемент сдвигается до тех пор, пока не достигнет элемента, большего, чем он сам; в этом случае сдвиг прекращается, В этот момент направление сдвига данного элемента изменяется на противоположное и передвигается следующий меньший его элемент, который можно сдвинуть. Поскольку хранится перестановка, обратная к я, то в л легко найти место следующего меньшего элемента. Алгоритм 4.4 представляет собой реализацию рассмотренного метода. Корректность алгоритма доказывается индукцией по п. Алгоритм порождения всех л! перестановок линеен, сложность его определяется как л! + о{п\). Алгоритм 4.4 - один из наиболее эффективных алгоритмов для порождения перестановок. Рабочая программа на языке Pascal реализации эффективного метода генерации перестановок приводится в алгоритме 4.5. В качестве примера в алгоритме 4.6 приводится программа реализации эффективного метода генерации перестановок, написанная на языке Си. Алгоритм 4.4. Метод эффективного порождения перестановок fori= 1 tondolj z* <А=0; т0 = = т = n + 1; {Меткиграницы) Print л =Crcj,5T2,...,7tn); т -гк while т *\do*\ while я- . л >mdo\rm р, ~ш [т =т-1 р. - />, ; {Изменить р=п ,nPtr tdrr -т) Аиоритм 4.5. Программа на Pascalе порождения перестановок эффективным методом Program Start Effect; (Эффективная генерация перестановок} uses CRT,DOS; Const max n=20; { n<=max n } Type Vector=array [0. .max n+l] of Integer; Var f :Text; { Генерация перестановок z[l], z[2], .../ z[n] } Procedure Var z:Vector; n:Integer ); Const k :LongInt=0; {Количество перестановок} p,d :Vector; pm,dm,zpm :Integer; i,m,w :Integer; begin; for i:=l to n do begin z[i]:=i; p[i]:=i; d{i]:=-l; end; d[l]:=0; m:=n+l; z [0 J ?- [n+1] :-mi while mol do begin { Печать перестановки } k:=k+l; Write(f,k,) ) ; for i:=l to n do Write{f,z[i}, V; WriteLn(f); m: =n; while z[p[m]+d[m] ]>m do begin d[m] :=-d[m] ; m:=m-l; end; pm:=p[m]; dm:=pm+d[m]; w:=z[pm]; z [pm] : =z [dm] ; z[dm]:=w; zpm:=z[pm]; w:=p[zpm]; p[zpm]:=pm; p[m]:=w; end; end;{Effect} Var {Main} n :Integer; {Длина перестановки} Hour,Minute,Second,SeclOO :Word; rHour,rMinute,rSecond,rSeclOO :Word; delta -.Lonqlnt; begin Assign(f,Effect.in); Reset(f); {Файх открыт для чтения} RoadLn(f,n); {Чтение длины перестановки} Close (f); Assign(f,Effect.out); открыт для GetTime(Hour,Minute,Second,SeclOO) ; Effect(z,n); GetTime(rHour,rMinute,rSecond,rSeclOO); delta:=rHour-Hour; delta:=delta*60+rMinute-Minute; delta:=delta* 60 + rSecond-Second; delta:=delta*10O+rSecl0O-SeclO0; WriteLn(f,Время счета=,delta div 100,., delta mod 100, сек'); Close(f); end. Алгоритм 4.6. Программа на Си порождения перестановок эффективным методом #include <stdio.h> ttinclude<stdlib.h> #include <time.h> #include <dos.h> void main ( ) { Генерация перестановок z[l], z[2], z[n] int n; int *z,*p,*d; FILE *f; struct dostima t t,tnew; long delta; unsigned long k; int pm, dm, zpm; int i,m, w; dos gettime (&t); f=fopen( primer.in , rt ); fscanf(f, %d ,&n) ; z=(int*)malloc( (n+2)*sizeof(int)) ; Перестановка p=(int*)malloc( (n+2)*sizeof(int)) ; Обратная d= (int*)malloc( (n+2)*sizeof(int)) ; Смещение fclose(f) ; f=fopen ( primer .out , wt ) ; for( i=i; i<-n; i++ ){ z (i]=p[i]-i; ..Mil- \; ; d[l]=0; z[0]=z[n+l]=m=n+l; k=0; while ( m!=l ) { Печать перестановки k++; fprintf(f, \n%ld) ,k); for( i-1; i<=n; i++ ) fprintf (f, %d ,z [i]) ; m=n; while ( z[p[m]+d[m] ]>m ){ d [m] =-d [m] ; m-; ) pm=p [m] ; dm=pm+d[m]; w=z[pm]; z[pm]=z[dm]; z[dm]=w; zpm=z[pm]; w=p[zpm]; p[zpm]=pm; p[m]=w; free(z); free(p); free(d); dos gettime (Stnew); delta=tnew.hour; delta-=t .hour; delta*=60; delta+=tnew.minute; delta-=t.minute; delta*=60; delta+=tnew.second; delta-=t.second; delta*=100; delta+=tnew.hsecond; delta-=t.hsecond; fprintf(f, ХпВремя счета %ld.%ld сек , (long) (delta/100), (long) (delta%100)); fclose(f); 4.4. Порождение подмножеств множества Порождение подмножеств множества {аьа2,..., ан} эквивалентно порождению /(-разрядных двоичных наборов ah принадлежащих подмножеству, если и только если /- й разряд равен единице. Таким образом, задача порождения всех подмножеств множества сводится к задаче порождения всех возможных двоичных последовательностей длины п. Очевидно, что наиболее прямым способом порождения всех двоичных наборов длины п является счет в системе счисления с основанием 2, как показано в алгоритме 4.7. Перевод этого алгоритма на язык подмножеств множества {а а- ..., ап) осуществляется согласно алгоритму 4.8, где добавлен фиктивный элемент апЛ. Алгоритм 4,7. Счет в системе счисления с основанием 2 для порождения всех п-разрядных наборов for i= О ton do b = 0; i=0; while 1 do whileb. =\do\bi °\ bt =1 Алгоритм 4.8. Порождение подмножеств счетом в двоичной системе счисления while апЛ eS do Print(S), =0; while a, eSdo \, . ~ [1=1+1 4.4. Порождение подмножеств множества. Задача Счастливый билет . Дано п (п>2) произвольных цифр: аь а2,..., ап, где а, е {1, 2,..., 9}, и произвольное целое число т. Написать программу, которая расставляла бы между каждой парой цифр аь а2,..., ап, записанных именно в таком порядке, знаки +,- так, чтобы значением получившегося выражения было число т. Например, если аи а2,..., ап соответственно 1, 2,..., 9 и т = 5, то подойдет следующая расстановка знаков: 1-2+3-4+5-6+7-8+9. Если требуемая расстановка невозможна, то сообщить об этом. Исходные данные вводятся из текстового файла, имеющего следующую структуру: Первая строка - целое число п. Вторая строка - целое число т. Третья строка - цифры аь а2,..., ап через пробелы. Результаты расчетов сохранить в текстовом файле. Пример файла исходных данных: 14 71 65884752345789 Пример файла выходных данных: 6+5+8+8+4+7+5+2+3+4-5+7+8+9 = 71 6+5+8+8+4+7+5-2-3+4+5+7+8+9 = 71 6+5+8+8+4+7-5+2+3+4+5+7+8+9 = 71 6-5+8+8+4+7+5+2+3+4+5+7+8+9 = 71 Время счета = 0.60 с. Ясно, что для решения данной задачи достаточно выполнить полный перебор всех возможных вариантов расстановки знаков ± между каждой парой цифр а{, а2,..., ап и выбрать те расстановки знаков ± , которые удовлетворяют условию равенства суммы величине т. Всего позиций для расстановки + равно п - 1, а значит для полного перебора необходимо проверить 2 1двоич-ных наборов, если будет соответствовать 1, а - 0. Программа решения задачи счастливый билет представлена алгоритмом 4.9. Процедура SubSet (подмножество) этой программы реализует рассмотренный выше алгоритм 4.7 счета в системе счисления с основанием 2 для порождения всех (п - 1)-разрядных наборов. Порожденные двоичные наборы используются в процедуре Summa (сумма) для формирования суммы, соответствующей данному набору знаков где соответствует 1, а - 0. 1 ... 4 5 6 7 8 9 10 ... 28 |
|