Определите, является ли символ частью i-й комбинации nCr - PullRequest
3 голосов
/ 04 мая 2011

UPDATE: Комбинаторика и unranking были в конце концов тем, что мне нужно. Ссылки, приведенные ниже, очень помогли:

http://msdn.microsoft.com/en-us/library/aa289166(v=vs.71).aspx

http://www.codeproject.com/Articles/21335/Combinations-in-C-Part-2

Проблема
При заданном списке из N символов произнесите {0,1,2,3,4 ...}
И NCr комбинации этих

например. NC3 сгенерирует:

0 1 2  
0 1 3  
0 1 4  
...  
...  
1 2 3  
1 2 4  
etc...  

Для i-й комбинации (i = [1 .. NCr]) я хочу определить, является ли символ (ы) его частью.
Func (N, r, i, s) = True / False или 0/1
например. Продолжая сверху 1-я комбинация содержит 0 1 2, но не 3

F(N,3,1,"0") = TRUE  
F(N,3,1,"1") = TRUE  
F(N,3,1,"2") = TRUE  
F(N,3,1,"3") = FALSE  

Современные подходы и подсказки, которые могут помочь или иметь отношение.
Отношение к матрицам Для r = 2, например. 4C2 комбинации представляют собой верхнюю (или нижнюю) половину двумерной матрицы

    1,2 1,3 1,4  
    ----2,3 2,4  
    --------3,4  

Для r = 3 это угол трехмерной матрицы или куба для r = 4 это «угол» матрицы 4D и т. д.

Другое отношение
В идеале решение должно иметь форму, похожую на ответ на это: Рассчитать комбинацию на основе позиции

n-ая комбинация в списке комбинаций длины r (с разрешенной повторностью), i-й символ может быть вычислен
Использование целочисленного деления и остатка:

n / r ^ i% r = (0 для 0-го символа, 1 для 1-го символа .... и т. Д.)

например, для 6-й гребенки из 3 символов 0-й, 1-й и 2-й символы:

i = 0 => 6 / 3^0 % 3 = 0   
i = 1 => 6 / 3^1 % 3 = 2   
i = 2 => 6 / 3^2 % 3 = 0   

6-ая гребенка будет тогда 0 2 0

Мне нужно что-то подобное, но с повторением не разрешено.

Спасибо, что ответили на этот вопрос:]
Кевин.

Ответы [ 3 ]

1 голос
/ 05 мая 2011

Существует очень эффективный алгоритм для этой проблемы, который также содержится в недавно опубликованной:Кнут, Искусство компьютерного программирования , том 4А (раздел 7.2.1.3).

Поскольку вас не волнует порядок, в котором создаются комбинации, давайте используем лексикографический порядокиз комбинаций, где каждая комбинация перечислена в порядке по убыванию .Таким образом, для r = 3 первые 11 комбинаций из 3 символов будут: 210, 310, 320, 321, 410, 420, 421, 430, 431, 432, 510. Преимущество этого порядка состоит в том, что перечисление не зависит отп;на самом деле это перечисление более всех комбинаций из 3 символов из {0, 1, 2,…}.

Существует стандартный метод для непосредственного генерации i th комбинация, заданная i, поэтому, чтобы проверить, является ли символ s частью i th комбинации, вы можете просто сгенерировать ее и проверить.

Метод

Сколько комбинаций r символов начинается с определенного символа s?Итак, оставшиеся позиции r-1 должны исходить из символов s 0, 1, 2,…, s-1, поэтому это (s выбирают r-1), где (s выбирают r-1) или C (s, r-1) - биномиальный коэффициент, обозначающий количество способов выбора объектов r-1 из s объектов.Поскольку это верно для всех s, первый символ i -ой комбинации является наименьшим s таким, что

k = 0 s (k выберите r-1) ≥ i.

Как только вы узнаете первый символ, проблема сводится к нахождению (i - ∑ k = 0 s-1 (k выберите r-1)) - это комбинация символов r-1, где мы вычли те комбинации, которые начинаются с символа, меньшего s.

Код

Код Python (вы можете написать C(n,r) большеэффективно, но это достаточно быстро для нас):

#!/usr/bin/env python

tC = {}
def C(n,r):
    if tC.has_key((n,r)): return tC[(n,r)]
    if r>n-r: r=n-r
    if r<0: return 0
    if r==0: return 1
    tC[(n,r)] = C(n-1,r) + C(n-1,r-1)
    return tC[(n,r)]

def combination(r, k):
    '''Finds the kth combination of r letters.'''
    if r==0: return []
    sum = 0
    s = 0
    while True:
        if sum + C(s,r-1) < k:
            sum += C(s,r-1)
            s += 1
        else:
            return [s] + combination(r-1, k-sum)

def Func(N, r, i, s): return s in combination(r, i)

for i in range(1, 20): print combination(3, i)
print combination(500, 10000000000000000000000000000000000000000000000000000000000000000)

Обратите внимание, как быстро это:. он находит сочетание 10000000000000000000000000000000000000000000000000000000000000000th 500 букв (он начинается с 542) менее чем за 0,5 секунды

1 голос
/ 04 мая 2011

Я считаю, что ваша проблема заключается в unranking комбинациях или подмножествах.

Я дам вам реализацию в Mathematica из пакета Combinatorica , но приведенная выше ссылка на Google, вероятно, является лучшим местом для начала, если вы не знакомы с семантикой.

UnrankKSubset::usage = "UnrankKSubset[m, k, l] gives the mth k-subset of set l, listed in lexicographic order."

UnrankKSubset[m_Integer, 1, s_List] := {s[[m + 1]]}
UnrankKSubset[0, k_Integer, s_List] := Take[s, k]
UnrankKSubset[m_Integer, k_Integer, s_List] := 
       Block[{i = 1, n = Length[s], x1, u, $RecursionLimit = Infinity}, 
             u = Binomial[n, k]; 
             While[Binomial[i, k] < u - m, i++]; 
             x1 = n - (i - 1); 
             Prepend[UnrankKSubset[m - u + Binomial[i, k], k-1, Drop[s, x1]], s[[x1]]]
       ]

Использование как:

UnrankKSubset[5, 3, {0, 1, 2, 3, 4}]
<b>   {0, 3, 4}</b>

Возвращает шестую (с нумерацией 0) комбинацию длины {3, 1, 2, 3, 4}.

0 голосов
/ 25 сентября 2012

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

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

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

  3. Преобразует индекс в отсортированной таблице биномиальных коэффициентов в соответствующие K-индексы.

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

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

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

Чтобы прочитать об этом классе и загрузить код, см. Табулирование биномиального коэффициента .

Этот класс может быть легко применен к вашей проблеме.Если у вас есть ранг (или индекс) для таблицы биномиальных коэффициентов, просто вызовите метод класса, который возвращает K-индексы в массиве.Затем выполните цикл по возвращенному массиву, чтобы увидеть, совпадает ли какое-либо из значений K-индекса с имеющимся у вас значением.Довольно прямо вперед ...

...