Поиск (лексикографического) индекса перестановки заданного массива.

Учитывая, что массив говорит "bca", мне нужно найти число перестановок, которые лексикографически больше заданной перестановки.

Таким образом, в этом примере cab, cba - это перестановки, которые больше. Таким образом, ответ будет равен 2.

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

Приветствуется любая помощь/указатели в правильном направлении!

Ответы

Ответ 1

Посмотрим на перестановку dacb. Где это происходит в лексикографическом порядке среди 4!= 24 перестановок abcd?

  • Рассмотрим первую букву d. Среди оставшихся букв (acb) есть три буквы меньше, чем d, а 3!= 6 перестановок, начиная с каждого из них, в общей сложности 18 перестановок.
  • Рассмотрим первые две буквы da. Среди оставшихся букв (cb) букв меньше a (если бы они были, то были бы 2!= 2 перестановки, начиная с d плюс каждый), для всего 0 перестановок.
  • Рассмотрим первые три буквы dac. Среди оставшихся букв (b) есть одна буква, меньшая, чем c, и 1!= 1 перестановок, начинающихся с dab, для всего 1 перестановки.

Таким образом, в общей сложности существует 19 перестановок меньше dacb. Пусть это проверит.

>>> from itertools import permutations
>>> list(enumerate(''.join(p) for p in permutations('abcd')))
[(0, 'abcd'), (1, 'abdc'), (2, 'acbd'), (3, 'acdb'),
 (4, 'adbc'), (5, 'adcb'), (6, 'bacd'), (7, 'badc'),
 (8, 'bcad'), (9, 'bcda'), (10, 'bdac'), (11, 'bdca'),
 (12, 'cabd'), (13, 'cadb'), (14, 'cbad'), (15, 'cbda'),
 (16, 'cdab'), (17, 'cdba'), (18, 'dabc'), (19, 'dacb'),
 (20, 'dbac'), (21, 'dbca'), (22, 'dcab'), (23, 'dcba')]

Выглядит хорошо. Так что есть 4! - 19 - 1 = 4 перестановки, превышающие dacb.

Теперь должно быть ясно, как обобщить метод для создания алгоритма. Здесь реализация в Python:

from math import factorial

def lexicographic_index(p):
    """
    Return the lexicographic index of the permutation `p` among all
    permutations of its elements. `p` must be a sequence and all elements
    of `p` must be distinct.

    >>> lexicographic_index('dacb')
    19
    >>> from itertools import permutations
    >>> all(lexicographic_index(p) == i
    ...     for i, p in enumerate(permutations('abcde')))
    True
    """
    result = 0
    for j in range(len(p)):
        k = sum(1 for i in p[j + 1:] if i < p[j])
        result += k * factorial(len(p) - j - 1)
    return result

def lexicographic_followers(p):
    """
    Return the number of permutations of `p` that are greater than `p`
    in lexicographic order. `p` must be a sequence and all elements
    of `p` must be distinct.
    """
    return factorial(len(p)) - lexicographic_index(p) - 1

Ответ 2

Существует очень чистый способ сделать это на основе системы факториалов и кодов Lehmer. Идея состоит в том, чтобы присвоить числовой код каждой возможной перестановке, которая кодирует порядок, в котором происходят значения (Lehmer code). Затем вы можете преобразовать код Lehmer в число, определяющее индекс перестановки в списке всех перестановок (это использует систему факториальных номеров ). Учитывая индекс перестановки, вы можете вычислить (n! - 1) и вычесть индекс, чтобы определить, сколько еще перестановок существует.

Если вам интересно, как это сделать, у меня есть реализация этого алгоритма, который позволяет вам отображать из перестановок в индексы или наоборот. Я также рассказал о том, как это сделать; детали находятся во второй половине слайдов.

Надеюсь, это поможет!

Ответ 3

Вот решение Backtracking:

Программа переставляет все решения для данной строки и возвращает список решений, а также сколько здесь.

Пример: для acb он возвращает:

c a b
c b a
b a c
b c a
4

код:

#include <iostream>
#include <stdio>

using namespace std;

int v[100], n, cnt;
char *str;

void init(int k)
{
    v[k] = -1;
}

bool solutionReached( int k ) 
{
    if (k == n + 1)
        return true;
    return false;
}

void printSolution( int k ) 
{
    for (int i = 1; i < k; i++)
    {
        printf("%c ", str[v[i]]);
    }

    printf("\n");

    cnt++;
}

bool hasSuccesor( int k ) 
{
    if(v[k] < n - 1)
    {
        v[k]++;
        return true;
    }
    return false;
}

bool isValid( int k ) 
{
    for (int i = 1; i < k; i++)
    {
        if (v[i] == v[k])
        {
            return false;
        }
    }

    if (k == n)
    {
        char *cuv = (char *) malloc(n * sizeof(char));

        for (i = 0; i < n; i++)
            cuv[i] = str[v[i + 1]];

        if (strcmp(cuv, str) > 0)
        {
            return true;
        }
        else
            return false;
    }

    return true;
}

void bkt(int k)
{
    if(solutionReached(k))
        printSolution(k);
    else
    {
        init(k);
        while(hasSuccesor(k))
            if(isValid(k))
                bkt(k + 1);
    }
}

int main(int argc, char* argv[])
{
    str = "bca";

    n = strlen(str);
    bkt(1);

    printf("%i \n", --cnt);

    return 0;
}

Ответ 4

Прямое решение python полагается на то, что генератор перестановок Pythons будет генерировать в лексикографическом порядке из начальной отсортированной строки.

In [68]: from itertools import permutations

In [69]: from math import factorial

In [70]: def lexigreaterperms(perm):
    ...:     return factorial(len(perm)) - 1 -  list(permutations(sorted(perm))).index(tuple(perm))

In [71]: lexigreaterperms('bca')
Out[71]: 2

In [72]: lexigreaterperms('dacb')
Out[72]: 4