Автор: Пользователь скрыл имя, 02 Марта 2013 в 19:41, дипломная работа
Целью выпускной работы является разработка алгоритмического подхода к изучению элементов комбинаторики методов их перебора.
Задачи исследования:
1) изучить литературу по теме выпускной работы;
2) провести сравнительный анализ элементов комбинаторики и правил их построения, описания алгоритмов перебора сочетаний, перестановок и размещений;
3) описать методику решения численных задач дискретного анализа;
4) подготовить рекомендации по усовершенствованию логики изложения некоторых вопросов комбинаторики.
Введение
1. Комбинаторные задачи в школьном курсе математики 9
1.1. Сочетание по m элементов из n 9
1.2. Перестановки из элементов n 11
1.3. Размещение m элементов по n позициям 14
2. Алгоритмы перебора 18
2.1. Перебор сочетаний, перестановки, размещения. 18
3. Большие числа 25
3.1. Упаковка натурального числа в виде массива 25
3.2. Сравнение больших чисел 29
3.3. Сложение и вычитание двух больших чисел 36
4. Комбинаторные алгоритмы. Сочетание 40
4.1. Генерация сочетания по его номеру 40
4.2. Сочетания с повторениями 47
5. Комбинаторные алгоритмы. Перестановки 50
5.1. Число перестановок из n элементов 50
5.2 Определение номера перестановки. Определение перестановки по номеру 52
5.3. Перестановки с повторениями 54
6. Множества и алгоритмы перебора 56
6.1. Подмножество. Перечисление подмножеств 56
6.2. Нумерация подмножеств 59
6.3. Скобочные последовательности и их нумерация 64
ЗАКЛЮЧЕНИЕ 70
ЛИТЕРАТУРА 72
Рис.3.7. Технические характеристики
Для того, чтобы исключить возможное влияние операционной системы на время выполнения вычисления, был проведен численный эксперимент, результаты которого представлены в таблице 1.
Таблица 3. Время вычисления факториала 1000
Порядковый номер вычисления |
Время вычисления, сек |
1 |
2,446 |
2 |
2,448 |
3 |
2,426 |
4 |
2,451 |
5 |
2,441 |
6 |
2,442 |
7 |
2,442 |
8 |
2,443 |
Среднее время вычисления |
2,442 |
Таким образом, программа вычисляет факториал 1000 в среднем за 2,442 секунды.
Пожалуй, самой сложной для реализации, является операция деления и нахождение остатка от деления двух больших чисел. Методы соответствующие данным операциям были названы delenie (BigInteger, BigInteger) и ostasok_delenie (BigInteger, BigInteger) соответсвенно. В основе лежит принцип деления «столбиком». Пример работы алгоритма приведен на рисунке 3.8.
Рис. 3.8. Операция деления и нахождение остатка от деления
Ход алгоритма следующий: сравниваем делитель с делимым, прибавляя поразрядно по одной цифре к делителю в случае, если получившийся делитель меньше делимого, при этом в частное записываем 0. На рисунке 8 видно этот этап: 2<7985, в частное записываем 0, затем 21<7985, в частное записываем 0, и так далее пока не поменяется знак неравенства 21367>7985. После этого запускается цикл по нахождению следующей цифры частного. На каждом шаге делитель прибавляется на величину равную самому делителю, пока он не станет больше либо равен нашему промежуточному делимому, т.е. 21367. Шаг цикла, на котором выполнится данное условие, и будет искомой цифрой для частного. Затем вычитаем из промежуточного делимого полученное в ходе цикла число и получаем промежуточный остаток. Так как он точно меньше делителя (в связи с предыдущими условиями), добавляем к нему следующую не задействованную цифру делимого и переходим к первому шагу алгоритма. Алгоритм считается выполненным, если получается остаток, меньший делителя и не осталось ни одной незадействованной цифры делимого. В зависимости от задачи, метод возвращает либо частное, либо остаток от деления.
Для удобства пользователей был введен еще один метод vishislenie(), который предоставляет возможность выполнять вышеперечисленные арифметические операции с двумя или одним большим числом путем простого ввода необходимого для вычисления выражения. Пример работы данного метода приведен на рисунке 3.9.
факториал большой число перемножение
Рис. 3.9. Режим вычисления выражений
Программа сложения и вычитания больших чисел
program PascalGuru;
{сложение + вычитание БОЛЬШИХ чисел в формате STRING}
Function CompLong(s1,s2:string):integer
var
a,len1,len2,i:integer;
b:boolean;
begin
a:=0;
b:=true;
len1:=length(s1);
len2:=length(s2);
if len1>len2 then begin a:= 1; b:=false; end;
if len1<len2 then begin a:=-1; b:=false; end;
if b then
for i:=1 to len1 do
begin
if Ord(s1[i])-48>Ord(s2[i])-48 then begin a:= 1; break; end;
if s1[i]<s2[i] then begin a:=-1; break; end;
end;
CompLong:=a;
end;
{--- функция вычитания ---}
function minus(s1,s2:string):string ;
var i,len,c,x:integer;
a,b:array[1..1000] of integer;
rez:string;
begin
c:=0;
len:=length(s2);
for i:=1 to len do
b[len-i+1]:=Ord(s2[i])-48;
len:=length(s1);
for i:=1 to len do
a[len-i+1]:=Ord(s1[i])-48;
if Complong(s1,s2)<0 then begin
Write('-');
len:=length(s2);
for i:=1 to len do begin
x:=a[i];
a[i]:=b[i];
b[i]:=x;
end;
end;
for i:=1 to len do
begin
c:=c+a[i]-b[i]+10;
a[i]:= c mod 10; {результат будет храниться в массиве a}
if c < 10 then c:=-1 else c:=0;
end;
while (a[len]=0) and (len>1) do len:=len-1;
rez:='';
for i:=len downto 1 do
begin
str(a[i],s1);
rez:=rez+s1;
end;
minus:=rez;
end;
{--- конец функции вычитания --- }
{функция сложения}
function plus(s1,s2:string):string ;
var a,b:array[1..100] of integer;
len,i,c:integer;
rez:string;
begin
c:=0;
len:=length(s1);
for i:=1 to len do
a[len-i+1]:=Ord(s1[i])-48;
len:=length(s2);
for i:=1 to len do
b[len-i+1]:=Ord(s2[i])-48;
if length(s1)>length(s2) then len:=length(s1)
else len:=length(s2);
for i:=1 to len do
begin
c:=c+a[i]+b[i]; {переменная c будет в дальнейшем использоваться для переноса числа в следующий ряд}
a[i]:=c mod 10; {результат сложения запишем в массив а}
c:=c div 10;
end;
if c>0 then begin
len:=len+1;
a[len]:=c;
end;
rez:='';
for i:=len downto 1 do
begin
str(a[i],s1);
rez:=rez+s1;
end;
plus:=rez;
end;
{--- конец функции сложения --- }
var c1,c2:string;
znak:char;
begin
writeln(plus('
writeln(minus('526','126') );
end.
Алгоритм
Идея алгоритма состоит в том, чтобы каким-нибудь образом пронумеровать все сочетания из n по k, то есть поставить в соответствие каждому числу i из диапазона 0≤i<C(n,k) (где C(n,k) – это число сочетаний из n по k) заранее определённое сочетание.
Итак, сначала определим отношение порядка на множестве сочетаний из n по k. Для наглядности будем записывать сочетания в формате «список из bool». Наше отношение порядка будет лексикографическим.
Имеем два сочетания, a=(a1, a2, … , an-1, an) и b=(b1, b2, … , bn-1, bn), ai и bi (1≤i≤n) принимают значения 0 или 1, причём суммы a1, a2, … , an и b1, b2, … , bn равны k. Сочетание a предшествует сочетанию b (a<b), если a1=b1, a2=b2, … am-1=bm-1, am<bm для некоторого m: 1≤m≤n.
Для наглядности расположим, например, все сочетания из 7 по 3 в указанном порядке.
(0, 0, 0, 0, 1, 1, 1) (0, 0, 0, 1, 0, 1, 1) (0, 0, 0, 1, 1, 0, 1) (0, 0, 0, 1, 1, 1, 0) (0, 0, 1, 0, 0, 1, 1) (0, 0, 1, 0, 1, 0, 1) (0, 0, 1, 0, 1, 1, 0) (0, 0, 1, 1, 0, 0, 1) (0, 0, 1, 1, 0, 1, 0) (0, 0, 1, 1, 1, 0, 0) (0, 1, 0, 0, 0, 1, 1) (0, 1, 0, 0, 1, 0, 1) (0, 1, 0, 0, 1, 1, 0) (0, 1, 0, 1, 0, 0, 1) (0, 1, 0, 1, 0, 1, 0) (0, 1, 0, 1, 1, 0, 0) (0, 1, 1, 0, 0, 0, 1) (0, 1, 1, 0, 0, 1, 0) (0, 1, 1, 0, 1, 0, 0) (0, 1, 1, 1, 0, 0, 0) (1, 0, 0, 0, 0, 1, 1) (1, 0, 0, 0, 1, 0, 1) (1, 0, 0, 0, 1, 1, 0) (1, 0, 0, 1, 0, 0, 1) (1, 0, 0, 1, 0, 1, 0) (1, 0, 0, 1, 1, 0, 0) (1, 0, 1, 0, 0, 0, 1) (1, 0, 1, 0, 0, 1, 0) (1, 0, 1, 0, 1, 0, 0) (1, 0, 1, 1, 0, 0, 0) (1, 1, 0, 0, 0, 0, 1) (1, 1, 0, 0, 0, 1, 0) (1, 1, 0, 0, 1, 0, 0) (1, 1, 0, 1, 0, 0, 0) (1, 1, 1, 0, 0, 0, 0) |
Если внимательно посмотреть на эти сочетания, то в их порядке легко проследить рекуррентную зависимость.
Рассмотрим первый элемент сочетаний. Сначала идут все сочетания, в которых он равен 0, потом – все, в которых он равен 1. Причём порядок следования сочетаний в обеих этих подгруппах повторяется – в каждой подгруппе сначала идут сочетания, в которых второй элемент – 0, а потом – в которых он равен 1 и т.д.
Количество сочетаний, в которых первый элемент равен нулю, равно числу сочетаний из n-1 по k, а количество сочетаний, в которых первый элемент равен единице, равно числу сочетаний из n-1 по k-1 (ответ на вопрос, почему так происходит, описан в примечании ко второму свойству биномиального коэффициента). Итак, если номер сочетания меньше числа сочетаний из n-1 по k, то первый элемент множества не входит в данное сочетание, иначе – входит.
Обобщая вышесказанное, приходим к следующему алгоритму.
Входные данные: CombNumber – номер сочетания, n, k – размер входного множества и размер сочетания, Src – входное множество (последовательность), из которого выбирается сочетание, Dest – выходное множество, в которое в результате работы алгоритма будет записано сочетание.
Выходные данные: сочетание, записанное в Dest.
Оценка временной сложности
Данный алгоритм в значительной степени использует вычисление биномиального коэффициента, поэтому его эффективность зависит от эффективности вычисления биномиального коэффициента. Максимальное количество итераций равно n (минимальное - k). Кроме того, используется k операций присваивания. Есть также незначительные операции типа инкремента и вычитания целых чисел. Их также не более n.
Предположим, что в качестве алгоритма вычисления биномиального коэффициента используется первый описанный в статье алгоритм (использующий дополнительную память). Его временная сложность равна O(nk), если требуемое значение C(n,k) не вычислялось до момента вызова, и O(1), если оно уже находится в кэше.
Тогда при вычислении C(n,k), исходя из алгоритма, все остальные требуемые значения биномиального коэффициента также вычисляются. То есть, по сути, биномиальный коэффициент необходимо вычислить всего один раз, во все последующие вызовы значения извлекаются из кэша.
Таким образом, временная
сложность всего алгоритма
Забегая вперёд, скажем, что при наличии заполненного кэша описываемый алгоритм более эффективен, чем алгоритм генерации сочетания методом случайной перестановки. То есть, если он вызывается многократно (например, хотя бы 5-10 раз), можно получить выигрыш в производительности (заполнение кэша окупается). Если же необходимо сгенерировать всего одно сочетание, то, конечно, лучше применить алгоритм генерации случайного сочетания методом случайной перестановки.
Реализация
Описание функций
Функций, относящихся к данному алгоритму, достаточно много, но не следует пугаться – все они однотипные.
Описание функций, реализующих алгоритм генерации сочетания по его номеру
namespace Combinatorics { // Функция, генерирующая сочетание в формате "список из bool" по его // порядковому номеру template<typename Integer, class RandomAccessIterator, class GetBinomialCoefficientFunc> void CombinationFromNumberBool( RandomAccessIterator First, RandomAccessIterator Last, GetBinomialCoefficientFunc& GetBinomialCoefficient);
template<typename Integer, class RandomAccessIterator> void CombinationFromNumberBool( RandomAccessIterator First, RandomAccessIterator Last);
// Функция, генерирующая сочетание в формате "список из bool" по его // порядковому номеру // В отличие от предыдущей функции, на итератор не накладывается // требование быть
итератором произвольного template<typename Integer, class OutputIterator, class GetBinomialCoefficientFunc> void CombinationFromNumberBool( Integer n, Integer k, OutputIterator Dest, GetBinomialCoefficientFunc& GetBinomialCoefficient);
template<typename Integer, class OutputIterator> void CombinationFromNumberBool( Integer n, Integer k, OutputIterator Dest);
// Функция, генерирующая сочетание в формате "подмножество" по его // порядковому номеру template<typename Integer, class RandomAccessIterator, class OutputIterator, class GetBinomialCoefficientFunc> void CombinationFromNumber(Integer CombNumber, Integer k, RandomAccessIterator First, RandomAccessIterator Last, OutputIterator Dest, GetBinomialCoefficientFunc& GetBinomialCoefficient);
template<typename Integer, class RandomAccessIterator, class OutputIterator> void CombinationFromNumber(Integer CombNumber, Integer k, RandomAccessIterator First, RandomAccessIterator Last, OutputIterator Dest);
// Функция, генерирующая сочетание в формате "подмножество" по его // порядковому номеру // В отличие от
предыдущей функции, на // требование быть
итератором произвольного template<typename Integer, class InputIterator, class OutputIterator, class GetBinomialCoefficientFunc> void CombinationFromNumber(Integer CombNumber, Integer n, Integer k, InputIterator Src, OutputIterator Dest, GetBinomialCoefficientFunc& GetBinomialCoefficient);
template<typename Integer, class InputIterator, class OutputIterator> void CombinationFromNumber(Integer CombNumber, Integer n, Integer k, InputIterator Src, OutputIterator Dest);
// Функция, генерирующая случайное сочетание в формате "список из bool" // по его порядковому номеру template<typename Integer, class RandomAccessIterator, class GetBinomialCoefficientFunc, class RandomNumberGenerator> void RandomCombinationFromNumberBoo RandomAccessIterator First, RandomAccessIterator Last, RandomNumberGenerator& RandomFunc, GetBinomialCoefficientFunc& GetBinomialCoefficient);
template<typename Integer, class RandomAccessIterator> void RandomCombinationFromNumberBoo RandomAccessIterator First, RandomAccessIterator Last);
// Функция, генерирующая случайное сочетание в формате "список из bool" // по его порядковому номеру // В отличие от предыдущей функции, на итератор не накладывается // требование быть
итератором произвольного template<typename Integer, class OutputIterator, class GetBinomialCoefficientFunc, class RandomNumberGenerator> void RandomCombinationFromNumberBoo OutputIterator Dest, RandomNumberGenerator& RandomFunc, GetBinomialCoefficientFunc& GetBinomialCoefficient);
template<typename Integer, class OutputIterator> void RandomCombinationFromNumberBoo OutputIterator Dest);
// Функция, генерирующая случайное сочетание в формате "подмножество" // по его порядковому номеру template<typename Integer, class RandomAccessIterator, class OutputIterator, class GetBinomialCoefficientFunc, class RandomNumberGenerator> void RandomCombinationFromNumber( RandomAccessIterator First, RandomAccessIterator Last, OutputIterator Dest, RandomNumberGenerator& RandomFunc, GetBinomialCoefficientFunc& GetBinomialCoefficient);
template<typename Integer, class RandomAccessIterator, class OutputIterator> void RandomCombinationFromNumber( RandomAccessIterator First, RandomAccessIterator Last, OutputIterator Dest);
// Функция, генерирующая случайное сочетание в формате "подмножество" // по его порядковому номеру // В отличие от
предыдущей функции, на // требование быть
итератором произвольного template<typename Integer, class InputIterator, class OutputIterator, class GetBinomialCoefficientFunc, class RandomNumberGenerator> void RandomCombinationFromNumber( InputIterator Src, OutputIterator Dest, RandomNumberGenerator& RandomFunc, GetBinomialCoefficientFunc& GetBinomialCoefficient);
template<typename Integer, class InputIterator, class OutputIterator> void RandomCombinationFromNumber( InputIterator Src, OutputIterator Dest); } // namespace Combinatorics |
Информация о работе Комбинаторные алгоритмы решения школьных задач по информатике