<?xml version='1.0' encoding="utf-8"?>
      <rss version='2.0'>
      <channel>
      <title>Форум на Исходниках.RU</title>
      <link>https://forum.sources.ru</link>
      <description>Форум на Исходниках.RU</description>
      <generator>Форум на Исходниках.RU</generator>
  	
      <item>
        <guid isPermaLink='true'>https://forum.sources.ru/index.php?showtopic=57737&amp;view=findpost&amp;p=386447</guid>
        <pubDate>Tue, 22 Jun 2004 13:20:02 +0000</pubDate>
        <title>Комбинаторика</title>
        <link>https://forum.sources.ru/index.php?showtopic=57737&amp;view=findpost&amp;p=386447</link>
        <description><![CDATA[tserega: <span class="tag-color tag-color-named" data-value="blue" style="color: blue"><span class='tag-size' data-value='21' style='font-size:21pt;'><div class='tag-align-center'>Комбинаторика</div></span></span><br>
<br>
Иногда встречаются задания (особенно их любят в университетах &quot;злобные&quot; преподаватели) типа &quot;найти число сочетаний из 15 по 7&quot; или, что еще хуже, &quot;получить все перестановки из 6 элементов&quot;.<br>
<br>
<span class='tag-size' data-value='14' style='font-size:14pt;'><span class="tag-color tag-color-named" data-value="purple" style="color: purple">Все подмножества данного множества</span></span><br>
Допустим, что у нас есть множество S, состоящее из элементов a<sub class='tag-sub'>1</sub>, a<sub class='tag-sub'>2</sub>, ..., a<sub class='tag-sub'>N</sub>, т.е. S = {a<sub class='tag-sub'>N</sub>, a<sub class='tag-sub'>2</sub>, ..., a<sub class='tag-sub'>N</sub>} Для простоты можно считать, что a<sub class='tag-sub'>1</sub>, .. a<sub class='tag-sub'>N</sub> - это различные целые числа от 1 до N. Подмножеством данного множества S называется множество S&#39;, которое содержит некоторые элементы из S (не обязательно все). У множества из N элементов будет ровно 2<sup class='tag-sup'>N</sup> различных подмножеств. Получить их все можно как минимум тремя различными способами.<br>
<span class='tag-u'>1-й способ</span><br>
Для примера возьмем N = 3. Запишем все числа от 0 до 2<sup class='tag-sup'>N</sup> - 1 = 7 в двоичной системе счисления:<br>
0 - 000<br>
1 - 001<br>
2 - 010<br>
3 - 011<br>
4 - 100<br>
5 - 101<br>
6 - 110<br>
7 - 111<br>
Если на <em class='tag-i'>i</em>-той позиции в двоичной записи стоит 1, то <em class='tag-i'>i</em>-тый элемент входит в подмножество, иначе - не входит. Поэтому данный алгоритм можно реализовать так:<br>
<div class='tag-code'><span class='pre_code'></span><div class='code  code_collapsed ' title='Подсветка синтаксиса доступна зарегистрированным участникам Форума.' style=''><div><div><ol type="1"><div class="code_line">var</div><div class="code_line">&nbsp;&nbsp;N: Longint;</div><div class="code_line">&nbsp;&nbsp;Count: Longint;</div><div class="code_line">&nbsp;&nbsp;I: Longint;</div><div class="code_line">&nbsp;</div><div class="code_line">procedure PrintSet(X: Longint);</div><div class="code_line">var P: Integer;</div><div class="code_line">begin</div><div class="code_line">&nbsp;&nbsp;write(&#39;{ &#39;);</div><div class="code_line">&nbsp;&nbsp;P := N;</div><div class="code_line">&nbsp;&nbsp;while X &#60;&#62; 0 do</div><div class="code_line">&nbsp;&nbsp;begin</div><div class="code_line">&nbsp;&nbsp; &nbsp;if X mod 2 = 1 then write(P, &#39; &#39;);</div><div class="code_line">&nbsp;&nbsp; &nbsp;Dec(P);</div><div class="code_line">&nbsp;&nbsp; &nbsp;X := X div 2;</div><div class="code_line">&nbsp;&nbsp;end;</div><div class="code_line">&nbsp;&nbsp;writeln(&#39;}&#39;);</div><div class="code_line">end;</div><div class="code_line">&nbsp;</div><div class="code_line">begin</div><div class="code_line">&nbsp;&nbsp;N := 5;</div><div class="code_line">&nbsp;&nbsp;Count := 1 shl N; { 2^N }</div><div class="code_line">&nbsp;&nbsp;for I := 0 to Count - 1 do</div><div class="code_line">&nbsp;&nbsp; &nbsp;PrintSet(I);</div><div class="code_line">end.</div></ol></div></div></div></div><script>preloadCodeButtons('1');</script><br>
<span class='tag-u'>2-й способ</span><br>
Допустим, что у нас уже есть множество S, причем про <em class='tag-i'>N</em> элементов мы уже знаем, входят они в множество или нет. Из него можно получить еще 2 множества - то, которое содержит (<em class='tag-i'>N + 1</em>)-й элемент, и то, которое (<em class='tag-i'>N + 1</em>)-й элемент не содержит. Получается рекурсивный алгоритм:<br>
<div class='tag-code'><span class='pre_code'></span><div class='code  code_collapsed ' title='Подсветка синтаксиса доступна зарегистрированным участникам Форума.' style=''><div><div><ol type="1"><div class="code_line">function IntToStr(X: Longint): String;</div><div class="code_line">var S: string;</div><div class="code_line">begin</div><div class="code_line">&nbsp;&nbsp;Str(X, S);</div><div class="code_line">&nbsp;&nbsp;IntToStr := S;</div><div class="code_line">end;</div><div class="code_line">&nbsp;</div><div class="code_line">procedure PrintSet(N: Integer; S: string);</div><div class="code_line">begin</div><div class="code_line">&nbsp;&nbsp;if (N = 0) then writeln(&#39;{ &#39;, S, &#39;}&#39;)</div><div class="code_line">&nbsp;&nbsp;else</div><div class="code_line">&nbsp;&nbsp;begin</div><div class="code_line">&nbsp;&nbsp; &nbsp;PrintSet(N - 1, S + &#39; &#39; + IntToStr(N));</div><div class="code_line">&nbsp;&nbsp; &nbsp;PrintSet(N - 1, S);</div><div class="code_line">&nbsp;&nbsp;end;</div><div class="code_line">end;</div><div class="code_line">&nbsp;</div><div class="code_line">begin</div><div class="code_line">&nbsp;&nbsp;PrintSet(5, &#39;&#39;);</div><div class="code_line">end.</div></ol></div></div></div></div><br>
<br>
<span class='tag-u'>3-й способ</span><br>
На самом деле, это вариация первого способа, но такой метод очень полезный.<br>
Пусть у нас уже есть какое-то подмножество. Подумаем, как из него получить следующее (все подмножества будем записывать в определенном порядке). Для определенности заведем массив, в котором будем хранить, какие элементы входят в данное подмножество.<br>
Начнем генерировать подмножества с пустого (т.е. в множестве нет ни одного элемента).<br>
Допустим, что N = 5 и текущее подмножество {1, 2, 5} или в битовом представлении (1, 1, 0, 0, 1). Найдем первый 0 слева (если такого элемента не найдем, значит у нас &quot;полное&quot; множество, которое будет последним). Заменим этот 0 на 1, а все единицы слева - на нули.<br>
<div class='tag-code'><span class='pre_code'></span><div class='code  code_collapsed ' title='Подсветка синтаксиса доступна зарегистрированным участникам Форума.' style=''><div><div><ol type="1"><div class="code_line">const maxN = 100; { Максимальное число элементов в множестве }</div><div class="code_line">var</div><div class="code_line">&nbsp;&nbsp;Bits: Array[1 .. maxN] Of Byte; { Массив битов }</div><div class="code_line">&nbsp;&nbsp;N: Longint;</div><div class="code_line">&nbsp;&nbsp;I: Longint;</div><div class="code_line">begin</div><div class="code_line">&nbsp;&nbsp;ReadLn(N); { Считываем, какого размера множество }</div><div class="code_line">&nbsp;&nbsp;For I := 1 To N + 1 Do Bits[I] := 0; { Сначала у нас пустое подмножество }</div><div class="code_line">&nbsp;&nbsp;While (Bits[N + 1] = 0) Do { Пока не все множество состоит из 1 }</div><div class="code_line">&nbsp;&nbsp;Begin</div><div class="code_line">&nbsp;&nbsp; &nbsp;Write(&#39;{ &#39;); { Выводим текущее подмножество }</div><div class="code_line">&nbsp;&nbsp; &nbsp;For I := 1 To N Do</div><div class="code_line">&nbsp;&nbsp; &nbsp; &nbsp;If Bits[I] = 1 Then Write(I, &#39; &#39;);</div><div class="code_line">&nbsp;&nbsp; &nbsp;Writeln(&#39;}&#39;);</div><div class="code_line">&nbsp;&nbsp; &nbsp;I := 1; { Ищем следующее подмножество }</div><div class="code_line">&nbsp;&nbsp; &nbsp;While (Bits[I] = 1) Do</div><div class="code_line">&nbsp;&nbsp; &nbsp;Begin</div><div class="code_line">&nbsp;&nbsp; &nbsp; &nbsp;Bits[I]:=0;</div><div class="code_line">&nbsp;&nbsp; &nbsp; &nbsp;Inc(I);</div><div class="code_line">&nbsp;&nbsp; &nbsp;End;</div><div class="code_line">&nbsp;&nbsp; &nbsp;Bits[I]:=1;</div><div class="code_line">&nbsp;&nbsp;End;</div><div class="code_line">End.</div></ol></div></div></div></div><br>
<br>
<span class="tag-color tag-color-named" data-value="purple" style="color: purple"><span class='tag-size' data-value='14' style='font-size:14pt;'>Перестановки</span></span><br>
Как не сложно догадаться, что общее число перестановок из N элементов равно N&#33; (действительно, на первое место можем поставить любое из N чисел, на второе - любое из оставшихся N-1 и т.д.). Применим подход, как при генерации подмножеств.<br>
<span class='tag-u'>1-й способ</span><br>
Выберем порядок - по алфавиту (лексикографический) - т.е. первая перестановка - 1 2 3 .. N, а последняя - N N-1 ... 2 1<br>
Рассмотрим пример. Пусть N = 5 и текущая перестановка 1 4 3 5 2. Какая будет следующая перестановка?<br>
Будем двигаться с конца. Найдем самое &quot;правое&quot; число (оно будет на i-ой позиции), которое больше предыдущего (другими словами надо найти самый длинный возрастающий &quot;хвост&quot;). Если мы такого числа не нашли, то у нас уже получена перестановка N N-1 ... 2 1 - это последняя перестановка. Теперь найдем самое &quot;правое&quot; число, большее i-того (допустим, оно на j-той позиции). Поменяем их местами. Осталось перевернуть хвост от <em class='tag-i'>i+1</em>-й позиции до конца. И будет получена новая перестановка.<br>
1 4 3 5 2 - было в начале<br>
1 3 5 <span class='tag-u'>3 2</span> - меняем 3 и 5, затем подчеркнутое надо перевернуть<br>
1 3 5 2 3<br>
<div class='tag-code'><span class='pre_code'></span><div class='code  code_collapsed ' title='Подсветка синтаксиса доступна зарегистрированным участникам Форума.' style=''><div><div><ol type="1"><div class="code_line">const maxN = 10; { Максимальная длина перестановки }</div><div class="code_line">var</div><div class="code_line">&nbsp;&nbsp;Mas: Array[1 .. maxN] Of Byte; { Массив с перестановкой }</div><div class="code_line">&nbsp;&nbsp;N: Longint;</div><div class="code_line">&nbsp;&nbsp;I, J, K: Longint;</div><div class="code_line">&nbsp;</div><div class="code_line">Procedure WritePerm; { Вывод перестановки на экран }</div><div class="code_line">var I: Longint;</div><div class="code_line">begin</div><div class="code_line">&nbsp;&nbsp;For I := 1 To N Do Write(Mas[I], &#39; &#39;);</div><div class="code_line">&nbsp;&nbsp;Writeln;</div><div class="code_line">end;</div><div class="code_line">&nbsp;</div><div class="code_line">Procedure _Swap(I, K: Longint); { Меняет два элемента с индексами I и K }</div><div class="code_line">var X: Byte;</div><div class="code_line">begin</div><div class="code_line">&nbsp;&nbsp;X := Mas[I];</div><div class="code_line">&nbsp;&nbsp;Mas[I] := Mas[K];</div><div class="code_line">&nbsp;&nbsp;Mas[K] := X;</div><div class="code_line">end;</div><div class="code_line">&nbsp;</div><div class="code_line">begin</div><div class="code_line">&nbsp;&nbsp;ReadLn(N);</div><div class="code_line">&nbsp;&nbsp;For I := 1 To N Do Mas[I] := I; { Заполняем массив числами от 1 до N }</div><div class="code_line">&nbsp;&nbsp;While True Do</div><div class="code_line">&nbsp;&nbsp;Begin</div><div class="code_line">&nbsp;&nbsp; &nbsp;WritePerm; { Готова перестановка }</div><div class="code_line">&nbsp;&nbsp; &nbsp;I := N; { Проверяем с конца }</div><div class="code_line">&nbsp;&nbsp; &nbsp;While (I &#62; 0) And (Mas[I] &#62;= Mas[I + 1]) Do Dec(I); { Пока числа расположены в возрастающем порядке, продолжаем }</div><div class="code_line">&nbsp;&nbsp; &nbsp;If I = 0 Then Break; { Все числа расположены в возрастающем порядке, значит, все перестановки получены }</div><div class="code_line">&nbsp;&nbsp; &nbsp;For J := I + 1 To N Do { Ищем самое правое число, больше найденного }</div><div class="code_line">&nbsp;&nbsp; &nbsp; &nbsp;If (Mas[J] &#62; Mas[I]) Then K := J;</div><div class="code_line">&nbsp;&nbsp; &nbsp;_Swap(I, K); { Меняем их }</div><div class="code_line">&nbsp;&nbsp; &nbsp;Inc(I);</div><div class="code_line">&nbsp;&nbsp; &nbsp;J := N;</div><div class="code_line">&nbsp;&nbsp; &nbsp;While (I &#60; J) Do { Переворачиваем хвост перестановки }</div><div class="code_line">&nbsp;&nbsp; &nbsp;Begin</div><div class="code_line">&nbsp;&nbsp; &nbsp; &nbsp;_Swap(I, J);</div><div class="code_line">&nbsp;&nbsp; &nbsp; &nbsp;Inc(I);</div><div class="code_line">&nbsp;&nbsp; &nbsp; &nbsp;Dec(J);</div><div class="code_line">&nbsp;&nbsp; &nbsp;End;</div><div class="code_line">&nbsp;&nbsp;End;</div><div class="code_line">end.</div></ol></div></div></div></div><br>
<br>
<span class='tag-u'>2-й способ</span><br>
Обычно именно этот метод приходит на ум первым. Пусть уже есть перестановка (3, 4, 2, 1). Из нее можно получить еще 5 перестановок, вставляя пяторку в любое из 5 мест: <br>
(5, 3, 4, 2, 1)<br>
(3, 5, 4, 2, 1)<br>
(3, 4, 5, 2, 1)<br>
(3, 4, 2, 5, 1)<br>
(3, 4, 2, 1, 5)<br>
Так тоже мы получим все перестановки:<br>
<div class='tag-code'><span class='pre_code'></span><div class='code  code_collapsed ' title='Подсветка синтаксиса доступна зарегистрированным участникам Форума.' style=''><div><div><ol type="1"><div class="code_line">Const MaxN = 10; { Сколько цифр }</div><div class="code_line">Type SMaxN = String[MaxN];</div><div class="code_line">&nbsp;</div><div class="code_line">Procedure Permulate(N: Longint; S: SMaxN); { Сама процедура }</div><div class="code_line">Var</div><div class="code_line">&nbsp;&nbsp;I: Longint;</div><div class="code_line">&nbsp;&nbsp;TmpS: SMaxN;</div><div class="code_line">Begin</div><div class="code_line">&nbsp;&nbsp;If (N = MaxN) Then { Добавили последнюю цифру }</div><div class="code_line">&nbsp;&nbsp;Begin</div><div class="code_line">&nbsp;&nbsp; &nbsp;Writeln(S); { Новая перестановка в переменной S }</div><div class="code_line">&nbsp;&nbsp; &nbsp;Exit;</div><div class="code_line">&nbsp;&nbsp;End;</div><div class="code_line">&nbsp;&nbsp;For I := 0 To N Do { Добавляем еще по одной цифре(N) в каждое место }</div><div class="code_line">&nbsp;&nbsp;Begin</div><div class="code_line">&nbsp;&nbsp; &nbsp;TmpS:=S;</div><div class="code_line">&nbsp;&nbsp; &nbsp;Insert(Chr(Ord(N) + Ord(&#39;0&#39;)) ,TmpS, I + 1);</div><div class="code_line">&nbsp;&nbsp; &nbsp;Permulate(N + 1, TmpS); { Переход от шага N к шагу N+1 }</div><div class="code_line">&nbsp;&nbsp;End;</div><div class="code_line">End;</div><div class="code_line">&nbsp;</div><div class="code_line">Begin</div><div class="code_line">&nbsp;&nbsp;Permulate(0, &#39;&#39;);</div><div class="code_line">End.</div></ol></div></div></div></div><br>
<br>
<span class="tag-color tag-color-named" data-value="purple" style="color: purple"><span class='tag-size' data-value='14' style='font-size:14pt;'>Сочетания</span></span><br>
Сочетание - это выбор из N предметов нескольких (M), причем порядок не важен.<br>
Из курса комбинаторики известно, что число сочетаний из N по M равно N&#33;/(M&#33; * (N - M)&#33;)<br>
Для примера, всего 10 сочетаний из 5 по 3:<br>
(1, 2, 3), (1, 2, 4), (1, 2, 5), (1, 3, 4), (1, 3, 5), (1, 4, 5), (2, 3, 4), (2, 3, 5), (2, 4, 5), (3, 4, 5)<br>
Здесь снова приходит на помощь переход от одного сочетания к другому. Опять же будем записывать сочетания в лексикографическом порядке.<br>
Пусть текущее сочетание из 6 по 4 (в данном варианте последнее сочетние - (3, 4, 5, 6)) будет (1, 2, 3, 6). Найдем с конца числа, которые не &quot;последние&quot; на своих местах (здесь 6 - последнее, а 3, 2 и 1 - нет).Находим первый индекс i, который удовлетворяет этим условиям. Увеличим на 1 элемент с индексом i. получим (1, 2, 4, 6). Для хвоста сочетания переносим предыдущие числа, увеличенные на 1. В данном примере, i = 3. Перенос чисел даст (1, 2, 4, 5) - переносим только 4-й элемент с третьего (4 + 1 = 5)<br>
<div class='tag-code'><span class='pre_code'></span><div class='code  code_collapsed ' title='Подсветка синтаксиса доступна зарегистрированным участникам Форума.' style=''><div><div><ol type="1"><div class="code_line">Const MaxN = 100; { Максимальное число элементов в множестве }</div><div class="code_line">&nbsp;</div><div class="code_line">Var</div><div class="code_line">&nbsp;&nbsp;Mas: Array[1 .. MaxN] Of Longint; { Текущее сочетание }</div><div class="code_line">&nbsp;&nbsp;N, M: Longint;</div><div class="code_line">&nbsp;&nbsp;I, J: Longint;</div><div class="code_line">&nbsp;</div><div class="code_line">Procedure WriteComp; { Печать текущего сочетания }</div><div class="code_line">Var I: Longint;</div><div class="code_line">Begin</div><div class="code_line">&nbsp;&nbsp;For I := 1 To M Do Write(Mas[I], &#39; &#39;);</div><div class="code_line">&nbsp;&nbsp;Writeln;</div><div class="code_line">End;</div><div class="code_line">&nbsp;</div><div class="code_line">Begin</div><div class="code_line">&nbsp;&nbsp;ReadLn(N, M);</div><div class="code_line">&nbsp;&nbsp;For I := 1 To M Do Mas[I]:=I; { Заполняем сочетание числами от 1 до M }</div><div class="code_line">&nbsp;&nbsp;While (True) Do</div><div class="code_line">&nbsp;&nbsp;Begin</div><div class="code_line">&nbsp;&nbsp; &nbsp;WriteComp; { Выводим сочетание }</div><div class="code_line">&nbsp;&nbsp; &nbsp;I := M;</div><div class="code_line">&nbsp;&nbsp; &nbsp;While (I &#62; 0) And (Mas[I] = N - M + I) Do Dec(I); { Пока все правые &nbsp;числа - &nbsp;последние }</div><div class="code_line">&nbsp;&nbsp; &nbsp;If I = 0 Then Break; { Если все числа - последние, то все сочетания сгенерированы }</div><div class="code_line">&nbsp;&nbsp; &nbsp;Inc(Mas[I]); { Увеличиваем на 1 найденный не последний элемент }</div><div class="code_line">&nbsp;&nbsp; &nbsp;For J := I + 1 To M Do Mas[J]:=Mas[J - 1] + 1; { Переносим хвост }</div><div class="code_line">&nbsp;&nbsp;End;</div><div class="code_line">End.</div></ol></div></div></div></div><br>
<br>
<span class="tag-color tag-color-named" data-value="purple" style="color: purple"><span class='tag-size' data-value='14' style='font-size:14pt;'>Размещения</span></span><br>
Размещение - это сочетание, в котором важен порядок.<br>
Получение всех размещений - это объединение задач о получении перестановок и сочетаний: для каждого сочетания находим перестановки.]]></description>
        <author>tserega</author>
        <category>Pascal: Математика</category>
      </item>
	
      </channel>
      </rss>
	