1 Комбинаторика. Общие понятия.
Комбинаторика (Комбинаторный анализ) — раздел математики, изучающий дискретные объекты, множества (сочетания, перестановки, размещения и перечисления элементов) и отношения на них (например, частичного порядка). Комбинаторика связана со многими другими областями математики — алгеброй, геометрией, теорией вероятностей и имеет широкий спектр применения в различных областях знаний (например в генетике, информатике, статистической физике).
Термин «комбинаторика» был введён в математический обиход Лейбницем, который в 1666 году опубликовал свой труд «Рассуждения о комбинаторном искусстве».
Иногда под комбинаторикой понимают более обширный раздел дискретной математики, включающий, в частности, теорию графов.
Для формулировки и решения комбинаторных задач используют различные модели комбинаторных конфигураций. Примерами комбинаторных конфигураций являются:
Размещением из n элементов по k называется упорядоченный набор из k различных элементов некоторого n-элементного множества;
Перестановкой из n элементов (например чисел 1,2,…,n) называется всякий упорядоченный набор из этих элементов. Перестановка также является размещением из n элементов по n;
Сочетанием из n по k называется набор k элементов, выбранных из данных n элементов. Наборы, отличающиеся только порядком следования элементов (но не составом), считаются одинаковыми, этим сочетания отличаются от размещений;
Композицией числа n называется всякое представление n в виде упорядоченной суммы целых положительных чисел;
Разбиением числа n называется всякое представление n в виде неупорядоченной суммы целых положительных чисел.
К разделам комбинаторики относят:
Перечислительная комбинаторика;
Структурная комбинаторика;
Экстремальная комбинаторика;
Теория Рамсея;
Вероятностная комбинаторика;
Топологическая комбинаторика;
Инфинитарная комбинаторика.
2. Перестановки.
Рисунок 1 - перестановки
Перестановкой называется упорядоченный набор элементов. Во многих алгоритмических задачах из этого каталога требуется найти наилучший способ упорядочения набора объектов. В качестве примеров можно назвать задачу коммивояжера (определение порядка посещения п городов, имеющего наименьшую стоимость),задачу уменьшения ширины ленты (упорядочивание вершин графа в линию таким образом, чтобы минимизировать длину самого длинного ребра) и задачу изоморфизма графа (упорядочивание вершины графа таким образом, чтобы он был идентичным другому графу). Любой алгоритм для предоставления точного решения таких задач должен создавать в процессе решения последовательность перестановок. Из п элементов можно создать п\ перестановок. С увеличением п количество перестановок возрастает так быстро, что не стоит надеяться сгенерировать все перестановки для п > 12, т. к. 12! = 479 001 600. Подобные числа должны охладить пыл любого человека, пытающегося решить задачу методом исчерпывающего перебора, и помочь объяснить важность генерирования случайных перестановок.
2.1 Лексикографический порядок
Лексикографический порядок означает, что элементы отсортированы, и часто является наиболее естественным способом генерирования комбинаторных объектов. Перестановки{1, 2, 3} располагаются в лексикографическом порядке таким образом: {}, {1}, {1, 2}, {1, 2, 3}, {1, 3}, {2}, {2, 3} и {3}. Но генерирование подмножеств в лексикографическом порядке оказывается на удивление трудной задачей. Поэтому не стоит использовать этот подход, если только у вас нет какой-либо важной причины для этого.
Ниже представлен код на языке C#, который решает поставленные нам задачи
//--------------------------------------------------------------------------------------генерация всех перестановок
private void button1_Click(object sender, EventArgs e)
{
int N;
//Ввод N с защитой от дурака
if (Int32.TryParse(textBox1.Text, out N) && N >= 1)
{
string s = System.String.Empty;
int i = 0, j = 0;
int[] a = new int[N];
for (; i < N; i++)
a[i] = i+1;
while (true)
{
s = GetString(a);
listBox1.Items.Add(s);
//Находим самый правый индекс, где a[i - 1] < a[i]
for (i = N - 1; i > 0 && a[i - 1] > a[i]; i--) ;
//Если i = 0 - все перестановки найдены - выходим
if (i == 0) break;
//Находим a[j] - наименьший элемент справа от a[i - 1] и больший его
for (j = N - 1; a[i - 1] > a[j]; j--) ;
//Меняем местами a[i - 1] и a[j]
Swap(ref a[i - 1], ref a[j]);
//реверс строки от позиции i до конца
for (int k = i, r = N - 1; r > k; k++, r--)
Swap(ref a[r], ref a[k]);
}
}
else
{
textBox1.Text = "";
var result = MessageBox.Show("Порядок группы перестановок - натуральное число N >= 1",
"Ошибка ввода N",
MessageBoxButtons.OK,
MessageBoxIcon.Error);
}
}
//---------------------------------------------------------------------------------------получение перестановки по номеру
private void button2_Click(object sender, EventArgs e)
{
int N;
long k;
//Ввод N и k с защитой от дурака
if (Int32.TryParse(textBox2.Text, out N) && N >= 1)
{
long[] Factorials = new long[N];
GetFactorialsArray(ref Factorials, N);
if (Int64.TryParse(textBox3.Text, out k) && k >= 1 && k <= Factorials[N - 1])
{
bool[] used = new bool[N];
for (int i = 0; i < N; i++)
used[i] = false;
int[] a = new int[N];
k--;
for (int i = 0; i < N - 1; i++)
{
int x = Convert.ToInt32(k / Factorials[N - 2 - i]);
k %= Factorials[N - 2 - i];
int count = -1, j = 0;
for ( ; count != x; j++) //пока не отсчитаем х
if (!used[j]) count++; //считаем неиспользованные числа
a[i] = j;
used[j-1] = true;
}
for (int i = 0; i < N; i++)
if (!used[i])
{
a[N - 1] = i + 1;
}
string s = GetString (a);
textBox4.Text = s;
}
else
{
textBox3.Text = "";
var result = MessageBox.Show("Номер перестановки - натуральное число 1 <= k <= N!",
"Ошибка ввода k",
MessageBoxButtons.OK,
MessageBoxIcon.Error);
}
}
else
{
textBox2.Text = "";
var result = MessageBox.Show("Порядок группы перестановок - натуральное число N >= 1",
"Ошибка ввода N",
MessageBoxButtons.OK,
MessageBoxIcon.Error);
}
}
//-------------------------------------------------------------------------------------- получение номера по перестановке
private void button3_Click(object sender, EventArgs e)
{
string s = textBox5.Text;
string[] words = s.Split(default(string[]), StringSplitOptions.RemoveEmptyEntries);
int N = words.Length;
int[] a = new int[N];
for (int i = 0; i < N; i++)
a[i] = Convert.ToInt32(words[i]);
//если введенная последовательность является перестановкой
if (CheckSequence(a))
{
long[] Factorials = new long[N];
GetFactorialsArray(ref Factorials, N);
long k = 0; //здесь будет номер
//массив использованных/неиспользованных чисел
bool[] used = new bool[N];
for (int i = 0; i < N; i++)
used[i] = false;
for (int i = 0; i < N - 1; i++)
{
int x = 0;
for (int j = 0; (j + 1) != a[i]; j++) //до позиции данного числа в массиве
if (!used[j]) x++; //считаем неиспользованные числа
k += x * Factorials[N - 2 - i];
used[a[i] - 1] = true; //помечаем число как использованное
}
k++;
textBox6.Text = k.ToString();
}
//если ввод неправильный
else
{
textBox5.Text = "";
var result = MessageBox.Show("Перестановка - массив уникальных чисел от 1 до N, введенных через пробел в произвольном порядке",
"Ошибка ввода перестановки",
MessageBoxButtons.OK,
MessageBoxIcon.Error);
}
}
//----------------------------------------Clean All
Рисунок 2 – Итог работы программы.
