<?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=330364&amp;view=findpost&amp;p=2876454</guid>
        <pubDate>Fri, 15 Apr 2011 10:18:45 +0000</pubDate>
        <title>Комбинаторика: генерация сочетаний</title>
        <link>https://forum.sources.ru/index.php?showtopic=330364&amp;view=findpost&amp;p=2876454</link>
        <description><![CDATA[Romkin: Сочетание удобно также оформить в виде объекта, обеспечивающего функциональность массива: доступ к элементу сочетания по его номеру от 1 до N.<br>
<strong class='tag-b'>Сочетание</strong>, объявления:<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</div><div class="code_line">&nbsp;&nbsp;/// &#60;summary&#62;</div><div class="code_line">&nbsp;&nbsp;/// Максимально возможный размер сочетания или последовательности.</div><div class="code_line">&nbsp;&nbsp;/// &#60;/summary&#62;</div><div class="code_line">&nbsp;&nbsp;MaxQuantity = 32; //количество сочетаний по 16 из 32 - это 601080390, достаточно</div><div class="code_line">&nbsp;</div><div class="code_line">type</div><div class="code_line">&nbsp;&nbsp;// На самом деле в группе нет нулевых элементов.</div><div class="code_line">&nbsp;&nbsp;// Диапазон расширен для особого случая: нулевой индекс дает нулевой элемент.</div><div class="code_line">&nbsp;&nbsp;TSeqNum = 0 .. MaxQuantity;</div><div class="code_line">&nbsp;</div><div class="code_line">&nbsp;&nbsp;// массив для хранения выборки</div><div class="code_line">&nbsp;&nbsp;TSequenceArray = array of TSeqNum;</div><div class="code_line">&nbsp;</div><div class="code_line">&nbsp;&nbsp;TSequenceError = class(Exception);</div><div class="code_line">&nbsp;</div><div class="code_line">&nbsp;&nbsp;/// &#60;summary&#62;</div><div class="code_line">&nbsp;&nbsp;/// Собственно группа-сочетание элементов: запись-массив.</div><div class="code_line">&nbsp;&nbsp;/// Представляет собой массив фиксированной вместимости не более MaxQuantity</div><div class="code_line">&nbsp;&nbsp;/// В массиве содержатся значения от 1 до MaxQuantity</div><div class="code_line">&nbsp;&nbsp;/// &#60;/summary&#62;</div><div class="code_line">&nbsp;&nbsp;/// &#60;stereotype&#62;array&#60;/stereotype&#62;</div><div class="code_line">&nbsp;&nbsp;TGroup = record</div><div class="code_line">&nbsp;&nbsp;strict private</div><div class="code_line">&nbsp;&nbsp; &nbsp;// Просто динамический массив элементов группы. Пока ;)</div><div class="code_line">&nbsp;&nbsp; &nbsp;FGroupArray: TSequenceArray;</div><div class="code_line">&nbsp;&nbsp; &nbsp;FCapacity: TSeqNum;</div><div class="code_line">&nbsp;&nbsp; &nbsp;function GetItem(index: TSeqNum): TSeqNum;</div><div class="code_line">&nbsp;&nbsp; &nbsp;procedure SetItem(index: TSeqNum; const Value: TSeqNum);</div><div class="code_line">&nbsp;&nbsp; &nbsp;function GetCapacity: TSeqNum;</div><div class="code_line">&nbsp;&nbsp;public</div><div class="code_line">&nbsp;&nbsp; &nbsp;constructor Create(Capacity: TSeqNum); //N</div><div class="code_line">&nbsp;&nbsp; &nbsp;class operator Implicit(const Sequence: TSequenceArray): TGroup;</div><div class="code_line">&nbsp;&nbsp; &nbsp;//Выдает список элементов, разделенный пробелами. Медленно :)</div><div class="code_line">&nbsp;&nbsp; &nbsp;class operator Implicit(Group: TGroup): string;</div><div class="code_line">&nbsp;&nbsp; &nbsp;/// &#60;summary&#62;Нумерация элементов от 1 до Capacity. Впрочем, при Index = 0 выдает 0.&#60;/summary&#62;</div><div class="code_line">&nbsp;&nbsp; &nbsp;property Item[index: TSeqNum]: TSeqNum read GetItem write SetItem; default;</div><div class="code_line">&nbsp;&nbsp; &nbsp;property Capacity: TSeqNum read FCapacity;</div><div class="code_line">&nbsp;&nbsp;end;</div></ol></div></div></div></div><script>preloadCodeButtons('1');</script><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">{ TGroup }</div><div class="code_line">&nbsp;</div><div class="code_line">constructor TGroup.Create(Capacity: TSeqNum);</div><div class="code_line">begin</div><div class="code_line">&nbsp;&nbsp;FCapacity := Capacity;</div><div class="code_line">&nbsp;&nbsp;SetLength(FGroupArray, FCapacity);</div><div class="code_line">end;</div><div class="code_line">&nbsp;</div><div class="code_line">function TGroup.GetCapacity: TSeqNum;</div><div class="code_line">begin</div><div class="code_line">&nbsp;&nbsp;Result := Length(FGroupArray);</div><div class="code_line">end;</div><div class="code_line">&nbsp;</div><div class="code_line">function TGroup.GetItem(index: TSeqNum): TSeqNum;</div><div class="code_line">begin</div><div class="code_line">&nbsp;&nbsp;if index = 0 then</div><div class="code_line">&nbsp;&nbsp; &nbsp;Exit(0);</div><div class="code_line">&nbsp;&nbsp;Result := FGroupArray[index - 1];</div><div class="code_line">end;</div><div class="code_line">&nbsp;</div><div class="code_line">class operator TGroup.Implicit(Group: TGroup): string;</div><div class="code_line">var</div><div class="code_line">&nbsp;&nbsp;i: integer;</div><div class="code_line">begin</div><div class="code_line">&nbsp;&nbsp;Result := &#39;&#39;;</div><div class="code_line">&nbsp;&nbsp;for i := 0 to High(Group.FGroupArray) do</div><div class="code_line">&nbsp;&nbsp; &nbsp;Result := Result + IntToStr(Group.FGroupArray[i]) + &#39; &#39;;</div><div class="code_line">&nbsp;&nbsp;Result := Trim(Result);</div><div class="code_line">end;</div><div class="code_line">&nbsp;</div><div class="code_line">class operator TGroup.Implicit(const Sequence: TSequenceArray): TGroup;</div><div class="code_line">var</div><div class="code_line">&nbsp;&nbsp;i: integer;</div><div class="code_line">&nbsp;&nbsp;Capacity: TSeqNum;</div><div class="code_line">begin</div><div class="code_line">&nbsp;&nbsp;Capacity := Length(Sequence);</div><div class="code_line">&nbsp;&nbsp;Result := TGroup.Create(Capacity);</div><div class="code_line">&nbsp;&nbsp;for i := 0 to Capacity - 1 do</div><div class="code_line">&nbsp;&nbsp; &nbsp;Result.FGroupArray[i] := Sequence[i];</div><div class="code_line">end;</div><div class="code_line">&nbsp;</div><div class="code_line">procedure TGroup.SetItem(index: TSeqNum; const Value: TSeqNum);</div><div class="code_line">begin</div><div class="code_line">&nbsp;&nbsp;assert(Value &#62; 0, Format(strElemError, [MaxQuantity]));</div><div class="code_line">&nbsp;&nbsp;FGroupArray[index - 1] := Value;</div><div class="code_line">end;</div></ol></div></div></div></div> <br>
<br>
<span class="tag-color tag-color-named" data-value="gray" style="color: gray"><span class='tag-size' data-value='7' style='font-size:7pt;'>Добавлено <time class="tag-mergetime" datetime="2011-04-15T14:33:41+04:00">15.04.11, 10:33</time></span></span><br>
Теперь нужно написать нумератор, пригодный для применения в цикле for .. in, и собственно генератор сочетаний:<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">type</div><div class="code_line">&nbsp;&nbsp;/// &#60;summary&#62;</div><div class="code_line">&nbsp;&nbsp;/// генератор сочетаний, k элементов из n (k &#60; n),</div><div class="code_line">&nbsp;&nbsp;/// выдается массив Capacity элементов</div><div class="code_line">&nbsp;&nbsp;/// со значениями в пределах от 1 до Quantity, элементы массива упорядочены</div><div class="code_line">&nbsp;&nbsp;/// по возрастанию.</div><div class="code_line">&nbsp;&nbsp;/// &#60;/summary&#62;</div><div class="code_line">&nbsp;&nbsp;/// &#60;stereotype&#62;iterator&#60;/stereotype&#62;</div><div class="code_line">&nbsp;&nbsp;TSequenceEnumerator = class</div><div class="code_line">&nbsp;&nbsp;private</div><div class="code_line">&nbsp;&nbsp; &nbsp;FCurrent: TSequenceArray;</div><div class="code_line">&nbsp;&nbsp; &nbsp;// Для генерации следующей последовательности меняется только часть</div><div class="code_line">&nbsp;&nbsp; &nbsp;// текущей. Индекс, с которого надо менять, хранится.</div><div class="code_line">&nbsp;&nbsp; &nbsp;// В новой последовательности будут изменены элементы с индексами</div><div class="code_line">&nbsp;&nbsp; &nbsp;// от FFixedPart до Capacity.</div><div class="code_line">&nbsp;&nbsp; &nbsp;FFixedPart: TSeqNum;</div><div class="code_line">&nbsp;&nbsp; &nbsp;FCapacity: byte;</div><div class="code_line">&nbsp;&nbsp; &nbsp;FQuantity: byte;</div><div class="code_line">&nbsp;&nbsp;public</div><div class="code_line">&nbsp;&nbsp; &nbsp;constructor Create(Capacity, Quantity: byte);</div><div class="code_line">&nbsp;&nbsp; &nbsp;function GetCurrent: TGroup;</div><div class="code_line">&nbsp;&nbsp; &nbsp;function MoveNext: boolean;</div><div class="code_line">&nbsp;&nbsp; &nbsp;procedure Reset;</div><div class="code_line">&nbsp;&nbsp; &nbsp;property Current: TGroup read GetCurrent;</div><div class="code_line">&nbsp;&nbsp;end;</div><div class="code_line">&nbsp;</div><div class="code_line">&nbsp;&nbsp;/// &#60;summary&#62;Обертка над TSequenceEnumerator для for...in &#60;/summary&#62;</div><div class="code_line">&nbsp;&nbsp;/// &#60;stereotype&#62;wrapper&#60;/stereotype&#62;</div><div class="code_line">&nbsp;&nbsp;TSequence = class</div><div class="code_line">&nbsp;&nbsp; &nbsp;FCapacity: byte;</div><div class="code_line">&nbsp;&nbsp; &nbsp;FQuantity: byte;</div><div class="code_line">&nbsp;&nbsp;public</div><div class="code_line">&nbsp;&nbsp; &nbsp;function GetEnumerator: TSequenceEnumerator;</div><div class="code_line">&nbsp;&nbsp; &nbsp;/// &#60;summary&#62;Количество элементов в сочетании&#60;/summary&#62;</div><div class="code_line">&nbsp;&nbsp; &nbsp;property Capacity: byte read FCapacity write FCapacity;</div><div class="code_line">&nbsp;&nbsp; &nbsp;/// &#60;summary&#62;Множество элементов для выборки&#60;/summary&#62;</div><div class="code_line">&nbsp;&nbsp; &nbsp;property Quantity: byte read FQuantity write FQuantity;</div><div class="code_line">&nbsp;&nbsp;end;</div><div class="code_line">&nbsp;</div><div class="code_line">implementation</div><div class="code_line">&nbsp;</div><div class="code_line">{ TSequenceEnumerator }</div><div class="code_line">&nbsp;</div><div class="code_line">constructor TSequenceEnumerator.Create(Capacity, Quantity: byte);</div><div class="code_line">begin</div><div class="code_line">&nbsp;&nbsp;FCapacity := Capacity;</div><div class="code_line">&nbsp;&nbsp;FQuantity := Quantity;</div><div class="code_line">&nbsp;&nbsp;SetLength(FCurrent, FCapacity);</div><div class="code_line">&nbsp;&nbsp;Reset;</div><div class="code_line">end;</div><div class="code_line">&nbsp;</div><div class="code_line">function TSequenceEnumerator.GetCurrent: TGroup;</div><div class="code_line">begin</div><div class="code_line">&nbsp;&nbsp;assert(FFixedPart &#60;= FCapacity);</div><div class="code_line">&nbsp;&nbsp;// Выдаем объект &quot;группа&quot;</div><div class="code_line">&nbsp;&nbsp;Result := FCurrent;</div><div class="code_line">end;</div><div class="code_line">&nbsp;</div><div class="code_line">function TSequenceEnumerator.MoveNext: boolean;</div><div class="code_line">var</div><div class="code_line">&nbsp;&nbsp;i: integer;</div><div class="code_line">begin</div><div class="code_line">&nbsp;</div><div class="code_line">&nbsp;&nbsp;// собственно алгоритм генерации следующей в лексикографическом порядке группы</div><div class="code_line">&nbsp;&nbsp;Result := False;</div><div class="code_line">&nbsp;</div><div class="code_line">&nbsp;&nbsp;if FCurrent[FCapacity - 1] = FQuantity then</div><div class="code_line">&nbsp;&nbsp; &nbsp;dec(FFixedPart)</div><div class="code_line">&nbsp;&nbsp;else</div><div class="code_line">&nbsp;&nbsp; &nbsp;FFixedPart := FCapacity;</div><div class="code_line">&nbsp;&nbsp;if FFixedPart = 0 then</div><div class="code_line">&nbsp;&nbsp; &nbsp;exit;</div><div class="code_line">&nbsp;&nbsp;if FFixedPart &#62;= 1 then</div><div class="code_line">&nbsp;&nbsp; &nbsp;for i := FCapacity downto FFixedPart do</div><div class="code_line">&nbsp;&nbsp; &nbsp; &nbsp;FCurrent[i - 1] := FCurrent[FFixedPart - 1] + i - FFixedPart + 1;</div><div class="code_line">&nbsp;&nbsp;Result := True;</div><div class="code_line">end;</div><div class="code_line">&nbsp;</div><div class="code_line">procedure TSequenceEnumerator.Reset;</div><div class="code_line">var</div><div class="code_line">&nbsp;&nbsp;i: integer;</div><div class="code_line">begin</div><div class="code_line">&nbsp;&nbsp;// начальная подгруппа</div><div class="code_line">&nbsp;&nbsp;for i := 0 to high(FCurrent) do</div><div class="code_line">&nbsp;&nbsp; &nbsp;FCurrent[i] := i + 1;</div><div class="code_line">&nbsp;&nbsp;// Первым вызывается MoveNext!</div><div class="code_line">&nbsp;&nbsp;// поэтому начальная группа - перед первой, последний элемент равен предыдущему.</div><div class="code_line">&nbsp;&nbsp;// При применении MoveNext из такой группы получается первая, от 1 до Capacity.</div><div class="code_line">&nbsp;&nbsp;if Length(FCurrent) &#62; 1 then</div><div class="code_line">&nbsp;&nbsp; &nbsp;FCurrent[ high(FCurrent)] := FCurrent[ high(FCurrent) - 1]</div><div class="code_line">&nbsp;&nbsp;else</div><div class="code_line">&nbsp;&nbsp; &nbsp;FCurrent[0] := 0;</div><div class="code_line">&nbsp;&nbsp;FFixedPart := FCapacity + 1;</div><div class="code_line">end;</div><div class="code_line">&nbsp;</div><div class="code_line">{ TSequence }</div><div class="code_line">&nbsp;</div><div class="code_line">function TSequence.GetEnumerator: TSequenceEnumerator;</div><div class="code_line">begin</div><div class="code_line">&nbsp;&nbsp;Result := TSequenceEnumerator.Create(FCapacity, FQuantity);</div><div class="code_line">end;</div></ol></div></div></div></div><br>
Фактически первоначальный метод оказался буквально вывернут наизнанку и разбросан по методам: MoveNext - это один шаг цикла, начальные значения задаются в reset, а GetCurrent - это аналог writeln.<br>
Из-за особенностей реализации нумератора начальным значением служит не первое сочетание, а &quot;нулевое&quot;, получаемое из первого отступлением на шаг назад.<br>
<br>
<strong class='tag-b'>Пример использования</strong>, проверка по примеру выше:<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">procedure TestTSequence.SetUp;</div><div class="code_line">begin</div><div class="code_line">&nbsp;&nbsp;FSequence := TSequence.Create;</div><div class="code_line">end;</div><div class="code_line">&nbsp;</div><div class="code_line">procedure TestTSequence.TestSequenceN6k4;</div><div class="code_line">const</div><div class="code_line">&nbsp;&nbsp;TestGroups: array [0..14] of array [0..3] of byte =</div><div class="code_line">&nbsp;&nbsp; &nbsp;((1,2,3,4), (1,2,3,5), (1,2,3,6), (1,2,4,5), (1,2,4,6),</div><div class="code_line">&nbsp;&nbsp; &nbsp; (1,2,5,6), (1,3,4,5), (1,3,4,6), (1,3,5,6), (1,4,5,6),</div><div class="code_line">&nbsp;&nbsp; &nbsp; (2,3,4,5), (2,3,4,6), (2,3,5,6), (2,4,5,6), (3,4,5,6));</div><div class="code_line">&nbsp;&nbsp;ACapacity = 4;</div><div class="code_line">&nbsp;&nbsp;AQuantity = 6;</div><div class="code_line">var</div><div class="code_line">&nbsp;&nbsp;CurrGroup: TGroup;</div><div class="code_line">&nbsp;&nbsp;ind, i: integer;</div><div class="code_line">begin</div><div class="code_line">&nbsp;&nbsp;ind := 0;</div><div class="code_line">&nbsp;&nbsp;FSequence.Capacity := ACapacity;</div><div class="code_line">&nbsp;&nbsp;FSequence.Quantity := AQuantity;</div><div class="code_line">&nbsp;&nbsp;for CurrGroup in FSequence do</div><div class="code_line">&nbsp;&nbsp;begin</div><div class="code_line">&nbsp;&nbsp; &nbsp;assert(ind &#60; 15);</div><div class="code_line">&nbsp;&nbsp; &nbsp;CheckEquals(CurrGroup.Capacity, ACapacity, &#39;Длина группы не соответствует&#39;);</div><div class="code_line">&nbsp;&nbsp; &nbsp;//Проверяем все выданные элементы подгруппы</div><div class="code_line">&nbsp;&nbsp; &nbsp;for i := 0 to High(TestGroups[ind]) do</div><div class="code_line">&nbsp;&nbsp; &nbsp; &nbsp;CheckEquals(CurrGroup[i + 1], TestGroups[ind, i]);</div><div class="code_line">&nbsp;&nbsp; &nbsp;inc(ind);</div><div class="code_line">&nbsp;&nbsp;end;</div><div class="code_line">&nbsp;&nbsp;//Выбраны все подгруппы:</div><div class="code_line">&nbsp;&nbsp;CheckEquals(ind, 15);</div><div class="code_line">end;</div></ol></div></div></div></div> <br>
<br>
<span class="tag-color tag-color-named" data-value="gray" style="color: gray"><span class='tag-size' data-value='7' style='font-size:7pt;'>Добавлено <time class="tag-mergetime" datetime="2011-04-15T10:42:19+00:00">15.04.11, 10:42</time></span></span><br>
Вот и все. Создается экземпляр TSequence, из него в цикле получаются сочетания, и остается только не забыть уничтожить генератор.<br>
Вообще говоря, TSequence может быть и записью, но с классом легче наследовать.<br>
<br>
<strong class='tag-b'>Генерация сочетаний с проспусками</strong><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">&nbsp;&nbsp;/// &#60;summary&#62;генератор сочетаний, вариация: k элементов из n (k &#60; n)</div><div class="code_line">&nbsp;&nbsp;/// выдается массив Capacity элементов со значениями в пределах от 1 до Quantity</div><div class="code_line">&nbsp;&nbsp;/// элементы массива упорядочены по возрастанию.</div><div class="code_line">&nbsp;&nbsp;/// В итоговой группе отсутствуют элементы, поданные как исключаемые</div><div class="code_line">&nbsp;&nbsp;/// в конструктор: множество ExcludeItems&#60;/summary&#62;</div><div class="code_line">&nbsp;&nbsp;TExcludedSequenceEnumerator = class</div><div class="code_line">&nbsp;&nbsp;private</div><div class="code_line">&nbsp;&nbsp; &nbsp;/// &#60;summary&#62;Собственно итератор по сокращенной выборке&#60;/summary&#62;</div><div class="code_line">&nbsp;&nbsp; &nbsp;FSequenceEnumerator: TSequenceEnumerator;</div><div class="code_line">&nbsp;&nbsp; &nbsp;/// &#60;summary&#62;Массив трансляции номеров выборки&#60;/summary&#62;</div><div class="code_line">&nbsp;&nbsp; &nbsp;FTranslate: TSequenceArray;</div><div class="code_line">&nbsp;&nbsp;public</div><div class="code_line">&nbsp;&nbsp; &nbsp;constructor Create(Capacity, Quantity: byte; const ExcludeItems: TGroup);</div><div class="code_line">&nbsp;&nbsp; &nbsp;function GetCurrent: TGroup;</div><div class="code_line">&nbsp;&nbsp; &nbsp;function MoveNext: boolean;</div><div class="code_line">&nbsp;&nbsp; &nbsp;procedure Reset;</div><div class="code_line">&nbsp;&nbsp; &nbsp;property Current: TGroup read GetCurrent;</div><div class="code_line">&nbsp;&nbsp;end;</div><div class="code_line">&nbsp;</div><div class="code_line">&nbsp;&nbsp;/// &#60;summary&#62;Обертка над TExcludedSequenceEnumerator для for...in &#60;/summary&#62;</div><div class="code_line">&nbsp;&nbsp;/// &#60;stereotype&#62;wrapper&#60;/stereotype&#62;</div><div class="code_line">&nbsp;&nbsp;TExcludedSequence = class(TSequence)</div><div class="code_line">&nbsp;&nbsp;private</div><div class="code_line">&nbsp;&nbsp; &nbsp;FExclude: TGroup;</div><div class="code_line">&nbsp;&nbsp;public</div><div class="code_line">&nbsp;&nbsp; &nbsp;function GetEnumerator: TExcludedSequenceEnumerator;</div><div class="code_line">&nbsp;&nbsp; &nbsp;/// &#60;summary&#62;Множество исключаемых элементов&#60;/summary&#62;</div><div class="code_line">&nbsp;&nbsp; &nbsp;property Exclude: TGroup read FExclude write FExclude;</div><div class="code_line">&nbsp;&nbsp;end;</div><div class="code_line">&nbsp;</div><div class="code_line">implementation</div><div class="code_line">&nbsp;</div><div class="code_line">{ TExcludedSequenceEnumerator }</div><div class="code_line">&nbsp;</div><div class="code_line">constructor TExcludedSequenceEnumerator.Create(Capacity, Quantity: byte;</div><div class="code_line">&nbsp;&nbsp;const ExcludeItems: TGroup);</div><div class="code_line">var</div><div class="code_line">&nbsp;&nbsp;RealQuantity: byte;</div><div class="code_line">&nbsp;&nbsp;i, k: integer;</div><div class="code_line">&nbsp;&nbsp;ExcludeSet: set of TSeqNum;</div><div class="code_line">begin</div><div class="code_line">&nbsp;&nbsp;assert(Capacity &#62; ExcludeItems.Capacity, &#39;Нельзя исключить больше чем все&#39;);</div><div class="code_line">&nbsp;&nbsp;RealQuantity := Quantity - ExcludeItems.Capacity;</div><div class="code_line">&nbsp;&nbsp;FSequenceEnumerator := TSequenceEnumerator.Create(Capacity, RealQuantity);</div><div class="code_line">&nbsp;&nbsp;SetLength(FTranslate, RealQuantity);</div><div class="code_line">&nbsp;&nbsp;// Заполняем множество исключаемых элементов</div><div class="code_line">&nbsp;&nbsp;ExcludeSet := [];</div><div class="code_line">&nbsp;&nbsp;for i := 1 to ExcludeItems.Capacity do</div><div class="code_line">&nbsp;&nbsp; &nbsp;include(ExcludeSet, ExcludeItems[i]);</div><div class="code_line">&nbsp;&nbsp;// Формируем массив перевода</div><div class="code_line">&nbsp;&nbsp;k := 0;</div><div class="code_line">&nbsp;&nbsp;for i := 1 to Quantity do</div><div class="code_line">&nbsp;&nbsp;begin</div><div class="code_line">&nbsp;&nbsp; &nbsp;if i in ExcludeSet then</div><div class="code_line">&nbsp;&nbsp; &nbsp; &nbsp;Continue;</div><div class="code_line">&nbsp;&nbsp; &nbsp;FTranslate[k] := i;</div><div class="code_line">&nbsp;&nbsp; &nbsp;inc(k);</div><div class="code_line">&nbsp;&nbsp;end;</div><div class="code_line">&nbsp;&nbsp;// В FTranslate по возрастанию все числа, которых нет в ExcludeItems</div><div class="code_line">end;</div><div class="code_line">&nbsp;</div><div class="code_line">function TExcludedSequenceEnumerator.GetCurrent: TGroup;</div><div class="code_line">var</div><div class="code_line">&nbsp;&nbsp;i: integer;</div><div class="code_line">&nbsp;&nbsp;Item: TSeqNum;</div><div class="code_line">begin</div><div class="code_line">&nbsp;&nbsp;Result := FSequenceEnumerator.GetCurrent;</div><div class="code_line">&nbsp;&nbsp;// Теперь переводим</div><div class="code_line">&nbsp;&nbsp;for i := 1 to Result.Capacity do</div><div class="code_line">&nbsp;&nbsp;begin</div><div class="code_line">&nbsp;&nbsp; &nbsp;Item := Result[i];</div><div class="code_line">&nbsp;&nbsp; &nbsp;// Массив считается от 0</div><div class="code_line">&nbsp;&nbsp; &nbsp;Result[i] := FTranslate[Item - 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">function TExcludedSequenceEnumerator.MoveNext: boolean;</div><div class="code_line">begin</div><div class="code_line">&nbsp;&nbsp;Result := FSequenceEnumerator.MoveNext;</div><div class="code_line">end;</div><div class="code_line">&nbsp;</div><div class="code_line">procedure TExcludedSequenceEnumerator.Reset;</div><div class="code_line">begin</div><div class="code_line">&nbsp;&nbsp;FSequenceEnumerator.Reset;</div><div class="code_line">end;</div><div class="code_line">&nbsp;</div><div class="code_line">{ TExcludedSequence }</div><div class="code_line">&nbsp;</div><div class="code_line">function TExcludedSequence.GetEnumerator: TExcludedSequenceEnumerator;</div><div class="code_line">begin</div><div class="code_line">&nbsp;&nbsp;Result := TExcludedSequenceEnumerator.Create(FCapacity, FQuantity, FExclude);</div><div class="code_line">end;</div></ol></div></div></div></div>]]></description>
        <author>Romkin</author>
        <category>Алгоритмы</category>
      </item>
	
      <item>
        <guid isPermaLink='true'>https://forum.sources.ru/index.php?showtopic=330364&amp;view=findpost&amp;p=2876393</guid>
        <pubDate>Fri, 15 Apr 2011 09:08:41 +0000</pubDate>
        <title>Комбинаторика: генерация сочетаний</title>
        <link>https://forum.sources.ru/index.php?showtopic=330364&amp;view=findpost&amp;p=2876393</link>
        <description><![CDATA[Romkin: <div class='tag-align-center'><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></div><br>
<br>
Под сочетанием понимается выборка k элементов из множества N имеющихся, без повторений (k &lt;= N). Без нарушения общности можно пронумеровать элементы исходного множества числами от 1 до N, и задача сводится к генерации уникальных последовательностей из k чисел от 1 до N.<br>
<br>
Обычно сочетания выдаются в лексикографическом порядке, упорядоченными аналогично строкам, проще всего это продемонстрировать на примере.<br>
<br>
<span class='tag-size' data-value='14' style='font-size:14pt;'><strong class='tag-b'>Пример</strong></span><br>
Пусть N=6, {1,2,3,4,5,6}, k=4. Тогда получается следующий набор из 15 сочетаний:<ul class="tag-list"><li>1 2 3 4</li><li>1 2 3 5</li><li>1 2 3 6</li><li>1 2 4 5</li><li>1 2 4 6</li><li>1 2 5 6</li><li>1 3 4 5</li><li>1 3 4 6</li><li>1 3 5 6</li><li>1 4 5 6</li><li>2 3 4 5</li><li>2 3 4 6</li><li>2 3 5 6</li><li>2 4 5 6</li><li>3 4 5 6</li></ul>Количество сочетаний равно (неслучайно) биномиальному коэффициенту, C(k,N). <br>
Сосчитать биномиальный коэффициент проще всего воспользовавшись следующей ссылкой: <a class='tag-url' href='http://karataev.nm.ru/solvers/sochet.html' target='_blank'>Нахожение числа сочетаний</a>. Надо заметить, что с увеличением N и k число сочетаний растет очень быстро.<br>
<br>
<strong class='tag-b'><span class='tag-size' data-value='14' style='font-size:14pt;'>Алгоритм</span></strong><br>
В книге Липский В. «Комбинаторика для программистов», М., Мир, 1988 предлагается следующий довольно простой алгоритм (чуть изменен):<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">begin </div><div class="code_line">&nbsp;&nbsp;for i := 1 to k do </div><div class="code_line">&nbsp;&nbsp; &nbsp;A[i] := i; //Первое подмножество</div><div class="code_line">&nbsp;&nbsp;p := k; </div><div class="code_line">&nbsp;&nbsp;while p &#62;= 1 do </div><div class="code_line">&nbsp;&nbsp;begin </div><div class="code_line">&nbsp;&nbsp; &nbsp;writeln(A[1],..., A[k]); //вывод очередного сочетания</div><div class="code_line">&nbsp;&nbsp; &nbsp;if A[k] = n then </div><div class="code_line">&nbsp;&nbsp; &nbsp; &nbsp;p := p - 1 </div><div class="code_line">&nbsp;&nbsp; &nbsp;else </div><div class="code_line">&nbsp;&nbsp; &nbsp; &nbsp;p := k; </div><div class="code_line">&nbsp;&nbsp; &nbsp;if p &#62;= 1 then </div><div class="code_line">&nbsp;&nbsp; &nbsp; &nbsp;for i := k downto p do </div><div class="code_line">&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;A[i] := A[p] + i - p + 1; </div><div class="code_line">&nbsp;&nbsp;end; </div><div class="code_line">end;</div></ol></div></div></div></div><br>
В принципе, его можно использовать как есть, но при преобразовании данной процедуры в объекты можно достичь гораздо большей гибкости... <br>
<br>
<span class="tag-color tag-color-named" data-value="gray" style="color: gray"><span class='tag-size' data-value='7' style='font-size:7pt;'>Добавлено <time class="tag-mergetime" datetime="2011-04-15T10:02:04+00:00">15.04.11, 10:02</time></span></span><br>
Delphi предоставляет удобные средства для объектно-ориентированного программирования, сохраняя возможность достаточно строгого контроля над типами. Логично оформить данный алгоритм в виде объекта-генератора сочетаний, который может быть использован в разных частях программы разными способами.<br>
<br>
Первая мысль - создать объект, получающий значения k и N в конструкторе, и имеющий один метод Generate, код которого практически написан выше. Вместо writeln поставить вызов абстрактного метода, тогда для обработки сочетаний надо просто написать потомка, реализовав этот метод. Этот способ носит громкое название паттерна &quot;шаблоннный метод&quot;. <br>
Путь не слишком привлекательный: придется писать потомков класса для каждого случая обработки, да и часто хочется получить весь массив сочетаний или его часть.<br>
<br>
Второй способ, более дельфийский: вместо writeln ставится вызов динамического метода, например DoGenerate, в котором идет вызов эвента OnGenerate. Это подразумевает прежде всего то, что пользователь должен написать объект-владелец этого генератора, и присоединить его метод к обработчику OnGenerate. Совершенно аналогично OnButtonClick в форме.<br>
Это гораздо лучше, но все равно пользователя подталкивают к способу использования данного объекта.<br>
<br>
Третий способ - воспользоваться итератором, тем более что в Delphi сейчас имеются встроенные средства для использования итераторов (см for .. in).]]></description>
        <author>Romkin</author>
        <category>Алгоритмы</category>
      </item>
	
      </channel>
      </rss>
	