Реализации алгоритмов/Алгоритм Нарайаны: различия между версиями
Нет описания правки |
Добавлено описание алгоритма; усовершенствована реализация на Pascal, добавлен пример использования |
||
Строка 2: | Строка 2: | ||
'''Алгори́тм Нарайа́ны''' — нерекурсивный алгоритм, генерирующий по данной [[w:Перестановка|перестановке]] следующую за ней в [[w:Лексикографический порядок|лексикографическом порядке]] перестановку. |
'''Алгори́тм Нарайа́ны''' — нерекурсивный алгоритм, генерирующий по данной [[w:Перестановка|перестановке]] следующую за ней в [[w:Лексикографический порядок|лексикографическом порядке]] перестановку. |
||
=Алгоритм= |
|||
Во всех приведенных ниже реализациях <code>a</code> — массив, в котором в прямом порядке записана перестановка, а <code>n</code> — количество элементов в перестановке. |
|||
Пусть <math>sequence</math> — последовательность из <math>count</math> элементов, для которых требуется найти очередную в лексикографическом порядке перестановку. |
|||
Алгоритм поиска очередной перестановки для последовательности с неубывающим (невозрастающим) '''изначальным''' порядком элементов: |
|||
==[[w:C (язык программирования)|C]]== |
|||
# Найти наибольшее <math>i</math>, для которого выполняется условие <math>sequence_{i} < sequence_{i+1}</math> (<math>sequence_{i} > sequence_{i+1}</math>). Если такого нет, значит все перестановки были сгенерированы. |
|||
# Найти наибольшее <math>j</math>, для которого выполняется условие <math>sequence_{i} < sequence_{j}</math> (<math>sequence_{i} > sequence_{j}</math>). Затем поменять местами <math>sequence_{i}</math> и <math>sequence_{j}</math>. |
|||
# Записать последовательность <math>sequence_{i+1},...,sequence_{count}</math> в обратном порядке. |
|||
=Реализации= |
|||
В нижеприведённых реализациях <code>sequence</code> — последовательность из <code>count</code> элементов, в которой производятся перестановки (как правило — массив из count элементов), <code>compare(x, y)</code> — функция, которая должна возвращать <code>true</code> если x < y и <code>false</code> иначе (если исходный порядок элементов последовательности неубывающий; для невозрастающего исходного порядка условие меняется на x > y). |
|||
Если не оговорено другое, то функция поиска очередной перестановки возвращает ненулевое значение, если перестановка найдена, либо 0, если следующей перестановки не существует (перебор закончен). |
|||
==[[w:C (язык программирования)|C]]/[[w:C++|C++]]== |
|||
'''C:''' Тип <code>T</code> должен быть предварительно определён как: |
|||
<source lang="cpp"> |
<source lang="cpp"> |
||
typedef int T; /* Вместо int можно подставить любой тип */ |
|||
typedef int T; |
|||
</source> |
|||
'''C++:''' Обе функции следует предварить строкой: |
|||
void swapItems (T *array, unsigned index_1, unsigned index_2) { |
|||
<source lang="cpp"> |
|||
T temp = array[index_1]; |
|||
template < typename T > |
|||
array[index_1] = array[index_2]; |
|||
</source> |
|||
array[index_2] = temp; |
|||
Собственно реализация: |
|||
<source lang="cpp"> |
|||
void swapItems (T *sequence, unsigned index_1, unsigned index_2) { |
|||
T _ = sequence[index_1]; |
|||
sequence[index_1] = sequence[index_2]; |
|||
sequence[index_2] = _; |
|||
} |
} |
||
int nextPermutation (T * |
int nextPermutation (T *sequence, int const count) { |
||
int i, j, l; |
int i, j, l; |
||
/* |
/* Этап № 1 */ |
||
for (j = |
for (j = count - 2; j >= 0 && sequence[j] >= sequence[j + 1]; --j); |
||
/* |
/* Этап № 2 */ |
||
if (j == -1) |
if (j == -1) |
||
return 0; |
return 0; |
||
for (l = |
for (l = count - 1; sequence[j] >= sequence[l]; --l); |
||
swapItems( |
swapItems(sequence, l, j); |
||
++j; |
++j; |
||
/* |
/* Этап № 3 */ |
||
for (i = 0; i < ( |
for (i = 0; i < (count - j + 1) >> 1; ++i) |
||
swapItems( |
swapItems(sequence, j + i, count - i - 1); |
||
return j; |
return j; |
||
} |
} |
||
Строка 33: | Строка 53: | ||
==[[w:Паскаль (язык программирования)|Pascal]]== |
==[[w:Паскаль (язык программирования)|Pascal]]== |
||
<source lang="pascal"> |
<source lang="pascal"> |
||
type T = Integer; { Вместо Integer можно использовать любой тип } |
|||
type T = integer; |
|||
{ Функция, задающая отношение порядка для значений типа T (< либо >) } |
|||
TPredicate2 = function (const value_0, value_1: T): Boolean; |
|||
function NextPermutation (var |
function NextPermutation (var sequence: array of T; Compare: TPredicate2): Word; |
||
var |
var count, i, j: Word; |
||
{ Обмен значениями двух элементов последовательности } |
|||
procedure SwapItems (index_1, index_2: word); |
|||
procedure SwapItems (index_1, index_2: Word); |
|||
var temp: T; |
|||
var _: T; { _ — временная переменная } |
|||
begin |
begin |
||
_ := sequence[index_1]; |
|||
sequence[index_1] := sequence[index_2]; |
|||
sequence[index_2] := _ |
|||
end; |
end; |
||
begin |
begin |
||
count := Length(sequence); |
|||
{ Шаг № 1 } |
|||
{ Этап № 1 } |
|||
i := count; |
|||
while (k >= 0) and (a[k] >= a[k + 1]) do |
|||
repeat |
|||
if |
if i <= 1 then |
||
NextPermutation := 0 |
|||
else |
|||
begin |
|||
{ Шаг № 2 } |
|||
l := n - 1; |
|||
while (l >= k + 1) and (a[k] >= a[l]) do |
|||
l := l - 1; |
|||
SwapItems(k, l); |
|||
{ Шаг № 3 } |
|||
for i := k + 1 to (n + k) div 2 do |
|||
begin |
begin |
||
NextPermutation := 0; |
|||
Exit |
|||
end; |
end; |
||
i := i - 1 |
|||
until Compare(sequence[i - 1], sequence[i]); |
|||
end |
|||
{ Этап № 2 } |
|||
j := count; |
|||
while (j > i) and not Compare(sequence[i - 1], sequence[j - 1]) do |
|||
j := j - 1; |
|||
i := i - 1; |
|||
SwapItems(i, j - 1); |
|||
{ Этап № 3 } |
|||
for j := i + 1 to (count + i) shr 1 do { shr 1 — более быстрый вариант div 2 } |
|||
SwapItems(j, count + i - j); |
|||
NextPermutation := j |
|||
end; |
end; |
||
</source> |
|||
Пример использования: |
|||
<source lang="pascal"> |
|||
{ Возвращает true, если value_0 меньше value_1, иначе — false } |
|||
function Less (const value_0, value_1: T): Boolean; |
|||
begin |
|||
Less := value_0 < value_1 |
|||
end; |
|||
{ Возвращает true, если value_0 больше value_1, иначе — false } |
|||
function Greater (const value_0, value_1: T): Boolean; |
|||
begin |
|||
Greater := value_0 > value_1 |
|||
end; |
|||
procedure InitSequence (var sequence: array of T); |
|||
var i: Word; |
|||
begin |
|||
{ Заполнение последовательности значениями 1, 2, 3… } |
|||
for i := Length(sequence) downTo 1 do |
|||
sequence[i - 1] := i; |
|||
end; |
|||
{ Выводит содержимое последовательности } |
|||
procedure OutputSequence (sequence: array of T); |
|||
var i, count: Word; |
|||
begin |
|||
Write('['); |
|||
count := Length(sequence); |
|||
if count > 0 then { Если последовательность не пуста } |
|||
begin |
|||
Write(sequence[0]); |
|||
for i := 1 to count - 1 do |
|||
Write(' ', sequence[i]) |
|||
end; |
|||
WriteLn(']') |
|||
end; |
|||
BEGIN |
|||
InitSequence(sequence); { Формирование исходной последовательности } |
|||
WriteLn('Неубывающая последовательность и её перестановки:'); |
|||
repeat |
|||
OutputSequence(sequence) |
|||
until NextPermutation(sequence, @Less) = 0; { x < y — критерий сравнения для неубывающей последовательности } |
|||
WriteLn; |
|||
WriteLn('Невозрастающая последовательность и её перестановки:'); |
|||
repeat |
|||
OutputSequence(sequence) |
|||
until NextPermutation(sequence, @Greater) = 0 { x > y — критерий сравнения для невозрастающей последовательности } |
|||
END. |
|||
</source> |
</source> |
||
Строка 72: | Строка 146: | ||
===Вариант № 1=== |
===Вариант № 1=== |
||
<source lang="PHP"> |
<source lang="PHP"> |
||
function nextPermutation ($ |
function nextPermutation ($sequence, $count) { |
||
$out = $ |
$out = $sequence; |
||
// |
// Этап № 1 |
||
$k = $ |
$k = $count - 2; |
||
while ($k >= 0 && $out[$k] >= $out[$k + 1]) { |
while ($k >= 0 && $out[$k] >= $out[$k + 1]) { |
||
--$k; |
--$k; |
||
Строка 82: | Строка 156: | ||
return false; |
return false; |
||
} |
} |
||
// |
// Этап № 2 |
||
$t = $ |
$t = $count - 1; |
||
while ($t >= 0 && $t >= $k + 1 && $out[$k] >= $out[$t]) { |
while ($t >= 0 && $t >= $k + 1 && $out[$k] >= $out[$t]) { |
||
--$t; |
--$t; |
||
Строка 90: | Строка 164: | ||
$out[$k] = $out[$t]; |
$out[$k] = $out[$t]; |
||
$out[$t] = $tmp; |
$out[$t] = $tmp; |
||
// |
// Этап № 3 |
||
for ($i = $k + 1; $i < ceil(($ |
for ($i = $k + 1; $i < ceil(($count + $k) / 2); ++$i) { |
||
$t = $ |
$t = $count + $k - $i; |
||
$tmp = $out[$i]; |
$tmp = $out[$i]; |
||
$out[$i] = $out[$t]; |
$out[$i] = $out[$t]; |
Версия от 10:14, 14 мая 2017
Алгори́тм Нарайа́ны — нерекурсивный алгоритм, генерирующий по данной перестановке следующую за ней в лексикографическом порядке перестановку.
Алгоритм
Пусть — последовательность из элементов, для которых требуется найти очередную в лексикографическом порядке перестановку.
Алгоритм поиска очередной перестановки для последовательности с неубывающим (невозрастающим) изначальным порядком элементов:
- Найти наибольшее , для которого выполняется условие (). Если такого нет, значит все перестановки были сгенерированы.
- Найти наибольшее , для которого выполняется условие (). Затем поменять местами и .
- Записать последовательность в обратном порядке.
Реализации
В нижеприведённых реализациях sequence
— последовательность из count
элементов, в которой производятся перестановки (как правило — массив из count элементов), compare(x, y)
— функция, которая должна возвращать true
если x < y и false
иначе (если исходный порядок элементов последовательности неубывающий; для невозрастающего исходного порядка условие меняется на x > y).
Если не оговорено другое, то функция поиска очередной перестановки возвращает ненулевое значение, если перестановка найдена, либо 0, если следующей перестановки не существует (перебор закончен).
C/C++
C: Тип T
должен быть предварительно определён как:
typedef int T; /* Вместо int можно подставить любой тип */
C++: Обе функции следует предварить строкой:
template < typename T >
Собственно реализация:
void swapItems (T *sequence, unsigned index_1, unsigned index_2) {
T _ = sequence[index_1];
sequence[index_1] = sequence[index_2];
sequence[index_2] = _;
}
int nextPermutation (T *sequence, int const count) {
int i, j, l;
/* Этап № 1 */
for (j = count - 2; j >= 0 && sequence[j] >= sequence[j + 1]; --j);
/* Этап № 2 */
if (j == -1)
return 0;
for (l = count - 1; sequence[j] >= sequence[l]; --l);
swapItems(sequence, l, j);
++j;
/* Этап № 3 */
for (i = 0; i < (count - j + 1) >> 1; ++i)
swapItems(sequence, j + i, count - i - 1);
return j;
}
Pascal
type T = Integer; { Вместо Integer можно использовать любой тип }
{ Функция, задающая отношение порядка для значений типа T (< либо >) }
TPredicate2 = function (const value_0, value_1: T): Boolean;
function NextPermutation (var sequence: array of T; Compare: TPredicate2): Word;
var count, i, j: Word;
{ Обмен значениями двух элементов последовательности }
procedure SwapItems (index_1, index_2: Word);
var _: T; { _ — временная переменная }
begin
_ := sequence[index_1];
sequence[index_1] := sequence[index_2];
sequence[index_2] := _
end;
begin
count := Length(sequence);
{ Этап № 1 }
i := count;
repeat
if i <= 1 then
begin
NextPermutation := 0;
Exit
end;
i := i - 1
until Compare(sequence[i - 1], sequence[i]);
{ Этап № 2 }
j := count;
while (j > i) and not Compare(sequence[i - 1], sequence[j - 1]) do
j := j - 1;
i := i - 1;
SwapItems(i, j - 1);
{ Этап № 3 }
for j := i + 1 to (count + i) shr 1 do { shr 1 — более быстрый вариант div 2 }
SwapItems(j, count + i - j);
NextPermutation := j
end;
Пример использования:
{ Возвращает true, если value_0 меньше value_1, иначе — false }
function Less (const value_0, value_1: T): Boolean;
begin
Less := value_0 < value_1
end;
{ Возвращает true, если value_0 больше value_1, иначе — false }
function Greater (const value_0, value_1: T): Boolean;
begin
Greater := value_0 > value_1
end;
procedure InitSequence (var sequence: array of T);
var i: Word;
begin
{ Заполнение последовательности значениями 1, 2, 3… }
for i := Length(sequence) downTo 1 do
sequence[i - 1] := i;
end;
{ Выводит содержимое последовательности }
procedure OutputSequence (sequence: array of T);
var i, count: Word;
begin
Write('[');
count := Length(sequence);
if count > 0 then { Если последовательность не пуста }
begin
Write(sequence[0]);
for i := 1 to count - 1 do
Write(' ', sequence[i])
end;
WriteLn(']')
end;
BEGIN
InitSequence(sequence); { Формирование исходной последовательности }
WriteLn('Неубывающая последовательность и её перестановки:');
repeat
OutputSequence(sequence)
until NextPermutation(sequence, @Less) = 0; { x < y — критерий сравнения для неубывающей последовательности }
WriteLn;
WriteLn('Невозрастающая последовательность и её перестановки:');
repeat
OutputSequence(sequence)
until NextPermutation(sequence, @Greater) = 0 { x > y — критерий сравнения для невозрастающей последовательности }
END.
PHP
Вариант № 1
function nextPermutation ($sequence, $count) {
$out = $sequence;
// Этап № 1
$k = $count - 2;
while ($k >= 0 && $out[$k] >= $out[$k + 1]) {
--$k;
}
if (-1 == $k) {
return false;
}
// Этап № 2
$t = $count - 1;
while ($t >= 0 && $t >= $k + 1 && $out[$k] >= $out[$t]) {
--$t;
}
$tmp = $out[$k];
$out[$k] = $out[$t];
$out[$t] = $tmp;
// Этап № 3
for ($i = $k + 1; $i < ceil(($count + $k) / 2); ++$i) {
$t = $count + $k - $i;
$tmp = $out[$i];
$out[$i] = $out[$t];
$out[$t] = $tmp;
}
return $out;
}
Вариант № 2
Вариант с выводом справа налево:
$b = "123456789";
$a = strrev($b);
while ($a !=$b) {
$i = 0;
while($a[$i] > $a[$i - 1]) {
$i++;
}
$j = 0;
while($a[$j] < $a[$i]) {
++$j;
}
$c = $a[$j];
$a[$j] = $a[$i];
$a[$i] = $c;
$x = strrev(substr($a, 0, $i));
$y = substr($a, $i);
print $a = $x . $y;
print ‘<br/>’;
}