8

Например, в комнате 6 стульев и 4 девочки и 2 мальчика. Существует 15 уникальных возможных способов, которыми они могут сидеть на этих стульях 6!/(4!*2!)=15.Как рассчитать лексикографический ранг данной перестановки

Моя проблема заключается в том, чтобы найти эффективный способ расчета позиции, которую они предпочитают сидеть. По положению я имею в виду следующее:

BBGGGG - possible position #1 
BGBGGG - possible position #2 
BGGBGG - possible position #3 
BGGGBG - possible position #4 
BGGGGB - possible position #5 
GBBGGG - possible position #6 
GBGBGG - possible position #7 
GBGGBG - possible position #8 
GBGGGB - possible position #9 
GGBBGG - possible position #10 
GGBGBG - possible position #11 
GGBGGB - possible position #12 
GGGBBG - possible position #13 
GGGBGB - possible position #14 
GGGGBB - possible position #15 

Например, они выбирают позицию GBBGGG ... пока мое решение, чтобы вычислить количество этой позиции (# 6) состоит в цикле все возможные позиции и сравнить каждый из них выбранного заказа и вернуть номер текущей позиции, если они равны.

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

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

ОБНОВЛЕНИЕ: Я точно знаю, сколько стульев, мальчиков и девочек в комнате. Единственная проблема заключается в том, чтобы найти номер позиции, которую они предпочитают сидеть.

Сортировка Я использую в своем примере только для лучшей читаемости. Ответы на любые типы сортировки приветствуются.

+0

Вы гарантировано, что стулья будут начинаться с минимальной лексикографическом представлении каждая итерация итерация увеличит представление на 1? – NathanOliver

+0

@NathanOliver Да, я могу это гарантировать, но я также могу изменить способ увеличения позиций, если есть более эффективный способ расчета позиции выбранной комбинации. – Nicolo

+0

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

ответ

7

Нахождение ранга перестановки положением G в

перестановок в примере в lexicographical order; первая перестановка имеет все B слева, а G - справа; другие перестановки выполняются путем постепенного перемещения G влево. (По аналогии с возрастающей последовательности двоичных чисел: 0011, 0101, 0110, 1001, 1010, 1100)

Чтобы подсчитать, как далеко в этом процессе данная перестановка, обратите внимание на символы по одному слева right: всякий раз, когда вы сталкиваетесь с G, количество перестановок, необходимых для его перемещения, есть (N   выберите   K), где N - количество позиций справа от текущей позиции, а K - количество слева G, включая ток Г.

123456 ← позиции
BBGGGG ← Оценка 0 (или 1)
BGBGGG ← ранга 1 (или 2)
BGGBGG ← ранга 2 (или 3)
BGGGBG ← Оценка 3 (или 4)
BGGGGB ← Оценка 4 (или 5)
GBBGGG ← Оценка 5 (или 6)
GBGBGG ← ранга 6 (или 7)
GBGGBG ← ранга 7 (или 8)

. для GBGGBG в вашем примере есть 4 G в 6 возможных положениях, а первый G находится в позиции 1, поэтому мы рассчитываем (6-1 выбираем 4) = 5; второй G находится в положении 3, поэтому мы добавляем (6-3 выбираем 3) = 1; третий G находится в положении 4, поэтому мы добавляем (6-4 выбираем 2) = 1; последний G находится в позиции 6, поэтому он находится в исходном положении и может быть проигнорирован. Это добавляет до 7, что означает, что перестановка имеет ранг 7 (или 8, если вы начинаете считать с 1, как и в вопросе).

Расчет (N выбирать K) с треугольником Паскаля

Вы можете использовать, например, Pascal's Triangle для расчета (N выберите K). Это треугольный массив, где каждое число является суммой двух чисел над ним:

 
      K=0 K=1 K=2 K=3 K=4 K=5 K=6 
     N=0 1 
    N=1 1 1 
    N=2 1 2 1 
    N=3 1 3 3 1 
    N=4 1 4 6 4 1 
N=5 1 5 10 10 5 1 
N=6 1 6 15 20 15 6 1 

Пример кода

Ниже простая реализация Javascript.Запустите фрагмент кода, чтобы увидеть несколько примеров. Время выполнения линейно зависит от количества стульев, а не от количества возможных перестановок, которое может быть огромным. (обновление: код теперь перебирает символы из справа налево, так что не нужно подсчитать количество G сначала.)

function permutationRank(perm) { 
 
    var chairs = perm.length, girls = 0, rank = 1;   // count permutations from 1 
 
    var triangle = PascalsTriangle(chairs - 1);   // triangle[n][k] = (n choose k) 
 
    for (var i = 1; i <= chairs; i++) { 
 
     if (perm.charAt(chairs - i) == 'G' && ++girls < i) { 
 
      rank += triangle[i - 1][girls]; 
 
     } 
 
    } 
 
    return rank; 
 

 
    function PascalsTriangle(size) { 
 
     var tri = [[1]]; 
 
     for (var n = 1; n <= size; n++) { 
 
      tri[n] = [1]; 
 
      for (var k = 1; k < n; k++) { 
 
       tri[n][k] = tri[n - 1][k - 1] + tri[n - 1][k]; 
 
      } 
 
      tri[n][n] = 1; 
 
     } 
 
     return tri; 
 
    } 
 
} 
 

 
document.write(permutationRank("BBGGGG") + "<BR>"); 
 
document.write(permutationRank("GBGGBG") + "<BR>"); 
 
document.write(permutationRank("GGGGBB") + "<BR>"); 
 
document.write(permutationRank("GGBGBBGBBBGBBBBGGGGGBBBBBGGGGBGGGBGGBGBB"));

Inverse алгоритм: сгенерировать перестановку

Этот алгоритм будет делать обратное: учитывая количество B, количество G и ранг перестановки, он вернет перестановку. Опять же, это делается без необходимости создания всех перестановок. (примечание: я не включил какую-либо проверку достоверности входных данных)

function permutationGenerator(boys, girls, rank) { 
 
    var chairs = boys + girls, perm = ""; 
 
    var triangle = PascalsTriangle(chairs - 1); // triangle[n][k] = (n choose k) 
 
    for (var i = chairs; i > 0; i--) { 
 
     if (i > girls) { 
 
      var choose = triangle[i - 1][girls]; 
 
      if (rank > choose) {     // > if counting from 1, >= if counting from 0 
 
       rank -= choose; 
 
       perm += 'G'; 
 
       --girls; 
 
      } 
 
      else perm += 'B'; 
 
     } 
 
     else perm += 'G';      // only girls left 
 
    } 
 
    return perm; 
 

 
    function PascalsTriangle(size) { 
 
     var tri = [[1]]; 
 
     for (var n = 1; n <= size; n++) { 
 
      tri[n] = [1]; 
 
      for (var k = 1; k < n; k++) { 
 
       tri[n][k] = tri[n - 1][k - 1] + tri[n - 1][k]; 
 
      } 
 
      tri[n][n] = 1; 
 
     } 
 
     return tri; 
 
    } 
 
} 
 

 
document.write(permutationGenerator(2, 4, 1) + "<BR>"); 
 
document.write(permutationGenerator(2, 4, 8) + "<BR>"); 
 
document.write(permutationGenerator(2, 4, 15) + "<BR>"); 
 
document.write(permutationGenerator(20, 20, 114581417274));

+0

Спасибо! Выглядит хорошо, и я буду тестировать его позже для точности, но как насчет обратной операции? Например, когда я запускаю ваш код с помощью «GBGGBG», я получил № 8 и как я могу перевести его обратно в «GBGGBG»? – Nicolo

+0

@ Николо-обратная операция - это другой вопрос, конечно. Тем не менее, я уверен, что вы можете инвертировать метод (n select k), чтобы вычислить это тоже. Создайте треугольник, начните снизу вверх, найдите самые большие числа, которые добавляют к числу перестановок, а затем переместите G на место соответственно. – m69

+0

Получил! :) Я скоро закончу запись функции обратной связи. Можно ли отредактировать свой ответ, когда закончите (я добавлю только фрагмент обратной функции)? Поскольку ваш ответ отмечен как лучший ответ, другие люди могут найти обратную функцию полезной, и лучше, если я добавлю ее к лучшему ответу вместо ответа на свой вопрос с другим ответом. Еще раз спасибо! – Nicolo

1

Я бы порекомендовал вам использовать двоичное дерево поиска. Каждый раз, когда вы добавляете стул, каждая сторона дерева будет клонирована, и новый выбор B или G будет единственной разницей. В принципе, вы клонируете то, что имеете, а затем добавляете B или G к каждой записи сбоку.

РЕДАКТИРОВАТЬ: Обратите внимание, что это также можно использовать для поиска в позиции LogN.

1

Ветвь связи (BB или B & B) является парадигмой проектирования алгоритмов для задач дискретной и комбинаторной оптимизации, а также общих реальных ценных задач. Алгоритм ветвления и привязки состоит из систематического перечисления возможных решений с помощью государственного космического поиска: набор возможных решений рассматривается как формирование корневого дерева с полным набором в корне. Алгоритм исследует ветви этого дерева, которые представляют собой подмножества набора решений. Прежде чем перечислять кандидатские решения ветви, ветвь проверяется на верхнюю и нижнюю оценочные оценки оптимального решения и отбрасывается, если она не может дать лучшего решения, чем лучший, найденный до сих пор алгоритмом.

Суть подхода, связанного с веткой и привязкой, заключается в следующем наблюдении: в общем дереве перечислений на любом узле, если я могу показать, что оптимальное решение не может иметь место ни в одном из его потомков, тогда нет необходимости для меня рассмотреть те потомки. Следовательно, я могу «обрезать» дерево в этом узле. Если я могу обрезать достаточно ветвей дерева таким образом, я могу сократить его до размера, управляемого с помощью вычислений. Обратите внимание: я не игнорирую эти решения в листьях ветвей, которые я обрезал, я оставил их вне рассмотрения после того, как убедился, что оптимальное решение не может быть ни в одном из этих узлов. Таким образом, подход с разбивкой и привязкой не является эвристической или аппроксимирующей процедурой, но является точной, оптимизирующей процедурой, которая находит оптимальное решение.

3

Моя проблема заключается в том, чтобы найти эффективный способ расчета позиции возможности, которую они предпочитают сидеть. Ответы на любые типы сортировки приветствуются. Есть ли какая-либо формула или более эффективный способ, который я могу использовать для определения позиции выбранной возможности?

я выберу отображение конфигурации в двоичную: B является 1 и G является 0.

За 7 мальчиков и 3 девочек существуют 10!/(7! 3!) = 120 комбинации, вот некоторые позиции комбинаций:

GGGBBBBBBB <--> 0001111111 
BBGBBGBBGB <--> 1101101101 
BBBBBBBGGG <--> 1111111000 

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

+0

Это неверно, потому что вы подсчитываете каждое двоичное число, а не только те, которые содержат правильное количество 0 и 1. Посмотрите на примеры в вопросе. – m69

+0

@ m69 Вы ошибаетесь, я не считаю двоичное число '1111111111', например. В примере в вопросе я рассчитывал бы только двоичные числа с 2 '1' и 4' 0'. – user1803551

+0

Итак, как вы определяете позицию почти сразу? – m69

2

Вот О (п) эффективный алгоритм. Нет треугольников паскалей - он вычисляет комбинации «на лету». Я тестировал против больших значений, генерируя комбинации и сопоставляя ряды, но если вы найдете пример, это не сработает, сообщите мне.

http://dev-task.blogspot.com/2015/12/rank-of-n-bit-numbers-with-exactly-k.html

+0

Спасибо. Похоже, что это может повысить производительность, но основная проблема производительности в обратном режиме. Можно ли также запустить обратную операцию «на лету»? – Nicolo

+0

Вы правы, при обратном действии единственным вариантом является сначала вычесть большое значение комбинации, чтобы найти остаток, поэтому он должен начинаться с большого значения комбинации C (n-1, k). Я думаю, что как только вы вычислили C (n-1, k), меньшие комбинации могут быть выведены итеративно «на лету», основанные на изменении n и k. Но значение C (n-1, k) должно быть доступно, хотя. –

+1

Интересно. Кстати, не могли бы вы описать основные идеи ответа здесь, а не просто ссылку на него? Это предпочтительнее на SO. – m69