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

Материал из Викиучебника — открытых книг для открытого мира
Содержимое удалено Содержимое добавлено
Поправки к коду реализаций
Добавлена реализация на Java и пример её использования
Строка 201: Строка 201:
delete [] sequence;
delete [] sequence;
return 0;
return 0;
}
</source>

==[[w:Java|Java]]==
<source lang="java">
// Narayana.java
abstract class Narayana {
@FunctionalInterface
interface Predicate2<T extends Comparable> {
boolean compare (final T value_0, final T value_1);
}
// Обмен значениями двух элементов последовательности
static final private <T> void _swapItems (T[] sequence, int index_0, int index_1) {
T temp = sequence[index_0];
sequence[index_0] = sequence[index_1];
sequence[index_1] = temp;
}

static final public <T extends Comparable> boolean nextPermutation (T[] sequence, Predicate2 comparator) {
// Этап № 1
int j = sequence.length, i = j - 1;
do {
if (j < 2)
return false;
} while (!comparator.compare(sequence[--i], sequence[--j]));
// Этап № 2
j = sequence.length;
while (i < j && !comparator.compare(sequence[i], sequence[--j]));
_swapItems(sequence, i, j);
// Этап № 3
j = sequence.length;
while (++i < j && i < --j)
_swapItems(sequence, i, j);
return true;
}
}
</source>
Пример использования:
<source lang="java">
// NarayanaTest.java
import java.util.Arrays;
import java.util.Scanner;


public class NarayanaTest {
// Возвращает true, если value_0 меньше value_1, иначе — false
static final <T extends Comparable> boolean less (final T value_0, final T value_1) {
return value_0.compareTo(value_1) < 0;
}
// Возвращает true, если value_0 больше value_1, иначе — false
static final <T extends Comparable> boolean greater (final T value_0, final T value_1) {
return value_0.compareTo(value_1) > 0;
}
// Инициализация последовательности
static final void initSequence (Integer[] sequence) {
for (int value = sequence.length; value > 0; --value)
sequence[value - 1] = value;
}
// Основная программа
public static void main (String[] args) {
Scanner scanner = new Scanner(System.in);
int count = scanner.nextInt();
Integer[] sequence = new Integer[count];
initSequence(sequence); // Формирование исходной последовательности
System.out.println("Неубывающая последовательность и её перестановки:");
do {
System.out.println(Arrays.deepToString(sequence));
} while (Narayana.nextPermutation(sequence, NarayanaTest::less)); // x < y — критерий сравнения для неубывающей последовательности
System.out.println("Невозрастающая последовательность и её перестановки:");
do {
System.out.println(Arrays.deepToString(sequence));
} while (Narayana.nextPermutation(sequence, NarayanaTest::greater)); // x > y — критерий сравнения для невозрастающей последовательности
}
}
}
</source>
</source>

Версия от 20:01, 25 мая 2017

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

Алгоритм

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

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

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

Реализации

В нижеприведённых реализациях sequence — последовательность из count элементов, в которой производятся перестановки (как правило — массив из count элементов), compare(x, y) — функция, которая должна возвращать true если x < y и false иначе (если исходный порядок элементов последовательности неубывающий; для невозрастающего исходного порядка условие меняется на x > y).

Если не оговорено другое, то функция поиска очередной перестановки возвращает значение «истина», если перестановка найдена, либо «ложь», если следующей перестановки не существует (перебор закончен).

C

/* narayana.h */
typedef int T; /* Вместо int можно подставить любой тип */

/* Обмен значениями двух элементов последовательности */
static void _swapItems (T *sequence, unsigned index_1, unsigned index_2);
/* Поиск очередной перестановки */
unsigned nextPermutation (T *sequence, unsigned const count, int (*compare)(T const, T const));


/* narayana.c */
#include "narayana.h"


/* Обмен значениями двух элементов последовательности */
static void _swapItems (T *sequence, unsigned index_1, unsigned index_2) {
    T _ = sequence[index_1]; /* _ — временная переменная */
    sequence[index_1] = sequence[index_2];
    sequence[index_2] = _;
}
/* Поиск очередной перестановки */
unsigned nextPermutation (T *sequence, unsigned count, int (*compare)(T const, T const)) {
    unsigned i = count, j;
    /* Этап № 1 */
    do {
        if (i < 2)
            return 0; /* Перебор закончен */
        --i;
    } while (!(*compare)(sequence[i - 1], sequence[i]));
    /* Этап № 2 */
    for (j = count; j > i && !(*compare)(sequence[i - 1], sequence[j - 1]); --j);
    --i;
    _swapItems(sequence, i, j - 1);
    /* Этап № 3 */
    for (j = i + 1; j <= (count + i) >> 1; ++j) /* >> 1 — более быстрый вариант / 2 */
		_swapItems(sequence, j, count + i - j);
    return 1;
}

Пример использования:

/* narayana_test.c */
#include <malloc.h>
#include <stdio.h>

#include "narayana.h"


/* Возвращает 1, если value_0 меньше value_1, иначе — 0 */
int less (T const value_0, T const value_1) {
    return value_0 < value_1;
}

/* Возвращает 1, если value_0 больше value_1, иначе — 0 */
int greater (T const value_0, T const value_1) {
    return value_0 > value_1;
}

/* Инициализация последовательности */
void initSequence (T *sequence, unsigned count) {
    /* Заполнение последовательности значениями 1, 2, 3… */
    for (unsigned i = count; i; --i)
        sequence[i - 1] = i;
}

/* Вывод содержимого последовательности */
void outputSequence (T const *sequence, unsigned count) {
    putchar('[');
    if (count) { /* Если последовательность не пуста */
        printf("%d", sequence[0]);
        for (unsigned i = 1; i < count; ++i)
            printf(", %d", sequence[i]);
    }
    putchar(']');
    putchar('\n');
}

int main () {
    unsigned const count = 4; /* Длина последовательности. Может быть и любой другой */
    
    T *sequence = (T*)malloc(count * sizeof(T));
    initSequence(sequence, count); /* Формирование исходной последовательности */
    printf("Неубывающая последовательность и её перестановки:\n");
    do {
        outputSequence(sequence, count);
    } while (nextPermutation(sequence, count, &less)); /* x < y — критерий сравнения для неубывающей последовательности */
    putchar('\n');
    printf("Невозрастающая последовательность и её перестановки:\n");
    do {
        outputSequence(sequence, count);
    } while (nextPermutation(sequence, count, &greater)); /* x > y — критерий сравнения для невозрастающей последовательности */
    free(sequence);
    return 0;
}

C++

На основе итераторов.

// narayana.hpp
namespace Narayana {
    // Обмен значениями двух элементов последовательности
    template < typename T >
    static void _swap (T &value_0, T &value_1) {
        T _ = value_0; // _ — временная переменная
        value_0 = value_1;
        value_1 = _;
    }
    // Поиск очередной перестановки
    template < typename Iterator, typename T >
    bool nextPermutation (Iterator const begin, Iterator const end, bool (*compare)(T const, T const)) {
        if (begin == end) // Если последовательность не содержит элементов
            return false;
        // Этап № 1
        Iterator i = end - 1, j = end;
        do {
            if (i == begin)
                return false;
        } while (!(*compare)(*--i, *--j));
        // Этап № 2
        j = end;
        while (j != i && !(*compare)(*i, *--j));
        _swap(*i, *j);
        // Этап № 3
        ++i;
        j = end;
        for ( ; (i != j) && (i != --j); ++i)
            _swap(*i, *j);
        return true;
    }
}

Пример использования:

// narayana_test.cpp
#include <iostream>

#include "narayana.hpp"


// Возвращает true, если value_0 меньше value_1, иначе — false
template < typename T >
bool less (T value_0, T value_1) {
    return value_0 < value_1;
}
// Возвращает true, если value_0 больше value_1, иначе — false
template < typename T >
bool greater (T value_0, T value_1) {
    return value_0 > value_1;
}

template < typename Iterator >
void outputSequence (Iterator const begin, Iterator const end) {
    std::cout << '[';
    if (begin != end) {
        std::cout << *begin;
        for (Iterator current = begin; ++current != end; )
            ((std::cout << ',') << ' ') << *current;
    }
    (std::cout << ']') << '\n';
}

template < typename Iterator >
void initSequence (Iterator const begin, Iterator const end) {
    int i = 0;
    for (Iterator current = begin; current != end; ++current)
        *current = ++i;
}

int main () {
    unsigned count;
    std::cin >> count;
    int *sequence = new int[count], *sequence_end = sequence + count;
    initSequence(sequence, sequence_end); // Формирование исходной последовательности
    std::cout << "Неубывающая последовательность и её перестановки:\n";
    do {
        outputSequence(sequence, sequence_end);
    } while (Narayana::nextPermutation(sequence, sequence_end, &less<int>)); // x < y — критерий сравнения для неубывающей последовательности
    std::cout << "Невозрастающая последовательность и её перестановки:\n";
    do {
        outputSequence(sequence, sequence_end);
    } while (Narayana::nextPermutation(sequence, sequence_end, &greater<int>)); // x > y — критерий сравнения для невозрастающей последовательности
    delete [] sequence;
    return 0;
}

Java

// Narayana.java
abstract class Narayana {
    @FunctionalInterface
    interface Predicate2<T extends Comparable> {
        boolean compare (final T value_0, final T value_1);
    }
    
    // Обмен значениями двух элементов последовательности
    static final private <T> void _swapItems (T[] sequence, int index_0, int index_1) {
        T temp = sequence[index_0];
        sequence[index_0] = sequence[index_1];
        sequence[index_1] = temp;
    }

    static final public <T extends Comparable> boolean nextPermutation (T[] sequence, Predicate2 comparator) {
        // Этап № 1
        int j = sequence.length, i = j - 1;
        do {
            if (j < 2)
                return false;
        } while (!comparator.compare(sequence[--i], sequence[--j]));
        // Этап № 2
        j = sequence.length;
        while (i < j && !comparator.compare(sequence[i], sequence[--j]));
        _swapItems(sequence, i, j);
        // Этап № 3
        j = sequence.length;
        while (++i < j && i < --j)
            _swapItems(sequence, i, j);
        return true;
    }
}

Пример использования:

// NarayanaTest.java
import java.util.Arrays;
import java.util.Scanner;


public class NarayanaTest {
    // Возвращает true, если value_0 меньше value_1, иначе — false
    static final <T extends Comparable> boolean less (final T value_0, final T value_1) {
        return value_0.compareTo(value_1) < 0;
    }
    // Возвращает true, если value_0 больше value_1, иначе — false
    static final <T extends Comparable> boolean greater (final T value_0, final T value_1) {
        return value_0.compareTo(value_1) > 0;
    }
    // Инициализация последовательности
    static final void initSequence (Integer[] sequence) {
        for (int value = sequence.length; value > 0; --value)
            sequence[value - 1] = value;
    }
    // Основная программа
    public static void main (String[] args) {
        Scanner scanner = new Scanner(System.in);
        int count = scanner.nextInt();
        Integer[] sequence = new Integer[count];
        initSequence(sequence); // Формирование исходной последовательности
        System.out.println("Неубывающая последовательность и её перестановки:");
        do {
            System.out.println(Arrays.deepToString(sequence));
        } while (Narayana.nextPermutation(sequence, NarayanaTest::less)); // x < y — критерий сравнения для неубывающей последовательности
        System.out.println("Невозрастающая последовательность и её перестановки:");
        do {
            System.out.println(Arrays.deepToString(sequence));
        } while (Narayana.nextPermutation(sequence, NarayanaTest::greater)); // x > y — критерий сравнения для невозрастающей последовательности
     }
}

Pascal

{ Narayana.pas }
UNIT Narayana;

INTERFACE

    type T = Integer; { Вместо Integer можно использовать любой тип }
         { Функция, задающая отношение порядка для значений типа T: < либо > }
         TPredicate2 = function (const value_0, value_1: T): Boolean;

    { Поиск очередной перестановки }
    function NextPermutation (var sequence: array of T; Compare: TPredicate2): Word;

IMPLEMENTATION

    { Поиск очередной перестановки }
    function NextPermutation (var sequence: array of T; Compare: TPredicate2): Boolean;
    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 < 2 then
            begin
                NextPermutation := false;
                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 := true
    end;

END.

Пример использования:

{ NarayanaTest.pas }
uses Narayana;


var sequence: array [0..3] of T; { Последовательность. Границы индексов могут быть и другими }

{ Возвращает 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 (const 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 not NextPermutation(sequence, @Less); { x < y — критерий сравнения для неубывающей последовательности }
    WriteLn;
    WriteLn('Невозрастающая последовательность и её перестановки:');
    repeat
        OutputSequence(sequence)
    until not NextPermutation(sequence, @Greater) { 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/>;
}

Rust

// narayana.rs
// Поиск очередной перестановки
fn next_permutation <T: Copy + PartialOrd> (sequence: &mut[T], compare: fn (T, T) -> bool) -> bool {
    let count = sequence.len();
    let mut i = count;
    // Этап № 1
    loop {
        if i < 2 {
            return false;
        }
        i -= 1;
        if compare(sequence[i - 1], sequence[i]) {
            break;
        }
    } 
    // Этап № 2
    let mut j = count;
    while j > i && !compare(sequence[i - 1], sequence[j - 1]) {
        j -= 1;
    }
    i -= 1;
    sequence.swap(i, j - 1);
    // Этап № 3
    j = i + 1;
    while j <= (count + i) >> 1 { // >> 1 — более быстрый вариант / 2
	    sequence.swap(j, count + i - j);
	    j += 1;
    }
    true
}

Пример использования:

// narayana_test.rs
// Возвращает true, если value_0 меньше value_1, иначе — false
fn less <T: PartialOrd> (value_0: T, value_1: T) -> bool {
    value_0 < value_1
}
// Возвращает true, если value_0 больше value_1, иначе — false
fn greater <T: PartialOrd> (value_0: T, value_1: T) -> bool {
    value_0 > value_1
}
// Инициализация последовательности
fn init_sequence (sequence: &mut [usize]) {
    // Заполнение последовательности значениями 1, 2, 3…
    let mut i = sequence.len();
    while i > 0 {
        sequence[i - 1] = i;
        i -= 1;
    }
}
// Основная программа
fn main () {
    let mut sequence = [0usize; 4]; // Длина последовательности - 4. Может быть и любой другой
    init_sequence(&mut sequence); // Формирование исходной последовательности
    println!("Неубывающая последовательность и её перестановки:");
    while {
        println!("{:?}", sequence);
        narayana::next_permutation(&mut sequence, less::<usize>) // x < y — критерий сравнения для неубывающей последовательности
    } { }
    println!("");
    println!("Невозрастающая последовательность и её перестановки:");
    while {
        println!("{:?}", sequence);
        narayana::next_permutation(&mut sequence, greater::<usize>) // x > y — критерий сравнения для невозрастающей последовательности
    } { }
}