Реализации алгоритмов/Алгоритм Нарайаны: различия между версиями

Материал из Викиучебника — открытых книг для открытого мира
Содержимое удалено Содержимое добавлено
Нет описания правки
Добавлено описание алгоритма; усовершенствована реализация на 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 *a, int n) {
int nextPermutation (T *sequence, int const count) {
int i, j, l;
int i, j, l;
/* Шаг № 1 */
/* Этап № 1 */
for (j = n - 2; j >= 0 && a[j] >= a[j + 1]; --j);
for (j = count - 2; j >= 0 && sequence[j] >= sequence[j + 1]; --j);
/* Шаг № 2 */
/* Этап № 2 */
if (j == -1)
if (j == -1)
return 0;
return 0;
for (l = n - 1; a[j] >= a[l]; --l);
for (l = count - 1; sequence[j] >= sequence[l]; --l);
swapItems(a, l, j);
swapItems(sequence, l, j);
++j;
++j;
/* Шаг № 3 */
/* Этап № 3 */
for (i = 0; i < (n - j + 1) >> 1; ++i)
for (i = 0; i < (count - j + 1) >> 1; ++i)
swapItems(a, j + i, n - i - 1);
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 a: array of T; n: integer): integer;
function NextPermutation (var sequence: array of T; Compare: TPredicate2): Word;
var i, k, l: integer;
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
temp := a[index_1];
_ := sequence[index_1];
a[index_1] := a[index_2];
sequence[index_1] := sequence[index_2];
a[index_2] := temp
sequence[index_2] := _
end;
end;
begin
begin
count := Length(sequence);
{ Шаг № 1 }
k := n - 2;
{ Этап 1 }
i := count;
while (k >= 0) and (a[k] >= a[k + 1]) do
k := k - 1;
repeat
if k = -1 then
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
l := n + k - i;
NextPermutation := 0;
SwapItems(i, l)
Exit
end;
end;
NextPermutation := i
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 ($a, $n) {
function nextPermutation ($sequence, $count) {
$out = $a;
$out = $sequence;
// Шаг № 1
// Этап № 1
$k = $n - 2;
$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
// Этап № 2
$t = $n - 1;
$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
// Этап № 3
for ($i = $k + 1; $i < ceil(($n + $k) / 2); ++$i) {
for ($i = $k + 1; $i < ceil(($count + $k) / 2); ++$i) {
$t = $n + $k - $i;
$t = $count + $k - $i;
$tmp = $out[$i];
$tmp = $out[$i];
$out[$i] = $out[$t];
$out[$i] = $out[$t];

Версия от 10:14, 14 мая 2017

Алгори́тм Нарайа́ны — нерекурсивный алгоритм, генерирующий по данной перестановке следующую за ней в лексикографическом порядке перестановку.

Алгоритм

Пусть  — последовательность из элементов, для которых требуется найти очередную в лексикографическом порядке перестановку.

Алгоритм поиска очередной перестановки для последовательности с неубывающим (невозрастающим) изначальным порядком элементов:

  1. Найти наибольшее , для которого выполняется условие (). Если такого нет, значит все перестановки были сгенерированы.
  2. Найти наибольшее , для которого выполняется условие (). Затем поменять местами и .
  3. Записать последовательность в обратном порядке.

Реализации

В нижеприведённых реализациях 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/>;
}