Поиск индекса заданной перестановки

Я читаю цифры 0, 1, ..., (N - 1) один за другим в некотором порядке. Моя цель-найти индекс лексикографии этой данной перестановки, используя только O(1) космос.

этот вопрос был задан раньше, но все алгоритмы, которые я мог бы найти использовать O(N) пространство. Я начинаю думать, что это невозможно. Но это действительно помогло бы мне в сокращении количества ассигнований.

7 ответов


учитывая следующие данные:

chars = [a, b, c, d]
perm = [c, d, a, b]
ids = get_indexes(perm, chars) = [2, 3, 0, 1]

возможное решение перестановки с повторениями проходит следующим образом:

len = length(perm)         (len = 4)
num_chars = length(chars)  (len = 4)

base = num_chars ^ len     (base = 4 ^ 4 = 256)
base = base / len          (base = 256 / 4 = 64)

id = base * ids[0]         (id = 64 * 2 = 128)
base = base / len          (base = 64 / 4 = 16)

id = id + (base * ids[1])  (id = 128 + (16 * 3) = 176)
base = base / len          (base = 16 / 4 = 4)

id = id + (base * ids[2])  (id = 176 + (4 * 0) = 176)
base = base / len          (base = 4 / 4 = 1)

id = id + (base * ids[3])  (id = 176 + (1 * 1) = 177)

обратный процесс:

id = 177
(id / (4 ^ 3)) % 4 = (177 / 64) % 4 =   2 % 4 = 2 -> chars[2] -> c
(id / (4 ^ 2)) % 4 = (177 / 16) % 4 =  11 % 4 = 3 -> chars[3] -> d
(id / (4 ^ 1)) % 4 = (177 / 4)  % 4 =  44 % 4 = 0 -> chars[0] -> a
(id / (4 ^ 0)) % 4 = (177 / 1)  % 4 = 177 % 4 = 1 -> chars[1] -> b

количество возможных перестановок задается num_chars ^ num_perm_digits, имеющего num_chars как количество возможных символов, а num_perm_digits как количество цифр в перестановке.

для этого требуется O(1) в пространстве, рассматривая начальный список как константу стоимость; и это требует O(N) в срок, учитывая N как количество цифр, которые будет иметь ваша перестановка.

основываясь на шагах выше, вы можете сделать:

function identify_permutation(perm, chars) {

    for (i = 0; i < length(perm); i++) {
        ids[i] = get_index(perm[i], chars);
    }

    len = length(perm);
    num_chars = length(chars);

    index = 0;
    base = num_chars ^ len - 1;
    for (i = 0; i < length(perm); i++) {
        index += base * ids[i];
        base = base / len;
    }

}

это псевдокод, но это также довольно легко преобразовать в любой язык (:


Если вы ищете способ получить лексикографический индекс или ранг уникальной комбинации вместо перестановки, то ваша проблема попадает под биномиальный коэффициент. Биномиальный коэффициент решает задачи выбора уникальных комбинаций в группах из K с общим числом n элементов.

Я написал класс на C# для обработки общих функций для работы с биномиальным коэффициентом. Он выполняет следующие задачи:

  1. выводит все K-индексы в хорошем формате для любого N выберите K в файл. K-индексы могут быть заменены более описательными строками или буквами.

  2. преобразует K-индексы в соответствующий лексикографический индекс или ранг записи в отсортированной таблице биномиальных коэффициентов. Этот метод намного быстрее, чем старые опубликованные методы, которые полагаются на итерацию. Он делает это, используя математическое свойство, присущее треугольнику Паскаля, и очень эффективен по сравнению с итерацией в наборе.

  3. преобразует индекс в отсортированной таблице биномиальных коэффициентов в соответствующие K-индексы. Я считаю, что это также быстрее, чем старые итерационные решения.

  4. использует Марк Доминус метод расчета биномиального коэффициента, который гораздо реже переполняется и работает с большими числами.

  5. класс написан на .NET C# и предоставляет способ управления объектами, связанными с проблема (если есть) с помощью общего списка. Конструктор этого класса принимает значение bool, называемое InitTable, которое при true создает общий список объектов для управления. Если это значение равно false, то таблица не будет создана. Таблицу не нужно создавать, чтобы использовать 4 вышеуказанных метода. Методы доступа для доступа к таблице.

  6. есть соответствующий тестовый класс, который показывает, как использовать класс и его методы. Он был тщательно протестирован с 2 случаев, и нет никаких известных ошибок.

чтобы прочитать об этом классе и загрузить код, см. Tablizing Бином Coeffieicent.

следующий тестируемый код будет повторять каждую уникальную комбинацию:

public void Test10Choose5()
{
   String S;
   int Loop;
   int N = 10;  // Total number of elements in the set.
   int K = 5;  // Total number of elements in each group.
   // Create the bin coeff object required to get all
   // the combos for this N choose K combination.
   BinCoeff<int> BC = new BinCoeff<int>(N, K, false);
   int NumCombos = BinCoeff<int>.GetBinCoeff(N, K);
   // The Kindexes array specifies the indexes for a lexigraphic element.
   int[] KIndexes = new int[K];
   StringBuilder SB = new StringBuilder();
   // Loop thru all the combinations for this N choose K case.
   for (int Combo = 0; Combo < NumCombos; Combo++)
   {
      // Get the k-indexes for this combination.  
      BC.GetKIndexes(Combo, KIndexes);
      // Verify that the Kindexes returned can be used to retrive the
      // rank or lexigraphic order of the KIndexes in the table.
      int Val = BC.GetIndex(true, KIndexes);
      if (Val != Combo)
      {
         S = "Val of " + Val.ToString() + " != Combo Value of " + Combo.ToString();
         Console.WriteLine(S);
      }
      SB.Remove(0, SB.Length);
      for (Loop = 0; Loop < K; Loop++)
      {
         SB.Append(KIndexes[Loop].ToString());
         if (Loop < K - 1)
            SB.Append(" ");
      }
      S = "KIndexes = " + SB.ToString();
      Console.WriteLine(S);
   }
}

вы должны быть в состоянии перенести этот класс довольно легко на язык по вашему выбору. Вероятно, вам не придется переносить универсальную часть класса на достигайте своих целей. В зависимости от количества комбинаций, с которыми вы работаете, вам может потребоваться использовать больший размер слова, чем 4 байта.


есть N! перестановки. Для представления индекса необходимо не менее N бит.


существует решение java для этой проблемы на geekviewpoint. У него есть хорошее объяснение, почему это правда, и код легко следовать. http://www.geekviewpoint.com/java/numbers/permutation_index. Он также имеет модульный тест, который запускает код с разными входами.


вот способ сделать это, если вы хотите предположить, что арифметические операции постоянная времени:

def permutationIndex(numbers):
  n=len(numbers)
  result=0
  j=0
  while j<n:
    # Determine factor, which is the number of possible permutations of
    # the remaining digits.
    i=1
    factor=1
    while i<n-j:
      factor*=i
      i+=1
    i=0
    # Determine index, which is how many previous digits there were at
    # the current position.
    index=numbers[j]
    while i<j:
      # Only the digits that weren't used so far are valid choices, so
      # the index gets reduced if the number at the current position
      # is greater than one of the previous digits.
      if numbers[i]<numbers[j]:
        index-=1
      i+=1
    # Update the result.
    result+=index*factor
    j+=1
  return result

Я целенаправленно выписал некоторые вычисления, которые можно было бы сделать более просто, используя некоторые встроенные операции Python, но я хотел сделать более очевидным, что не используется дополнительный непостоянный объем пространства.

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


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

import numpy as np
import math
vfact = np.vectorize(math.factorial, otypes='O')

def perm_index(p):
    return np.dot( vfact(range(len(p)-1, -1, -1)),
                   p-np.sum(np.triu(p>np.vstack(p)), axis=0) )

Я только что написал код с помощью Visual Basic, и моя программа может напрямую вычислять каждый индекс или каждую соответствующую перестановку для данного индекса до 17 элементов (этот предел обусловлен приближением научной нотации чисел более 17! моего компилятора).

Если вы заинтересованы, я могу отправить программу или опубликовать ее где-нибудь для загрузки. Он отлично работает, и он может быть полезен для тестирования и парагона вывода ваших кодов.

Я использовал метод о Джеймсе Д. МакКаффри называют факторидиком и об этом можно прочитать здесь и что-то еще здесь (в обсуждении в конце страницы).