Сгенерировать k-ю комбинацию без генерации / повторения предыдущего

Дан набор предметов, например:

[ 1, 2, 3, 4, 5, 6 ]

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

Например, начиная с этого:

[ 1, 5, 6 ]

Первая (следующая) комбинация будет:

[ 1, 6, 6 ]

Я успешно использовал itertools.combinations_with_replacement() для генерации комбинаций, но проект, для которого он предназначен, потребует работы с набором, который генерирует слишком много комбинаций - создание их всех в первую очередь и повторение до нужной точки невозможно.

Я нашел этот пример для генерации k-й комбинации, которая, похоже, у меня не очень хорошо работает. Этот ответ казался другой возможностью, но я не могу перенести его с C на Python.

Вот мой код с использованием k-го примера комбинации:

import operator as op
items = [ 1,2,3,4,5,6 ]

# https://stackoverflow.com/a/4941932/1167783
def nCr(n, r):
    r = min(r, n-r)
    if r == 0: 
        return 1
    numer = reduce(op.mul, xrange(n, n-r, -1))
    denom = reduce(op.mul, xrange(1, r+1))
    return numer // denom

# https://stackoverflow.com/a/1776884/1167783
def kthCombination(k, l, r):
    if r == 0:
        return []
    elif len(l) == r:
        return l
    else:
        i = nCr(len(l)-1, r-1)
        if k < i:
            return l[0:1] + kthCombination(k, l[1:], r-1)
        else:
            return kthCombination(k-i, l[1:], r)

# get 1st combination of 3 values from list 'items' 
print kthCombination(1, items, 3)

# returns [ 1, 2, 4 ]

Любая помощь была бы замечательной!


person JeffThompson    schedule 30.08.2013    source источник


Ответы (2)


Вместо того, чтобы изобрести число 37 289 423 987 239 489 826 364 653 колеса времени (считая только людей), вы можете сопоставить числа. itertools вернет первую комбинацию [1,1,1], но вы хотите [1,5,6]. Просто добавьте [0,4,5] mod 6 к каждой позиции. Вы также можете отображать туда и обратно числа, объекты и, конечно же, по модулю.

Это работает, даже если количество элементов в каждой позиции разное.

Однако вы получите больше удовольствия от того, что начали.

person Mario Rossi    schedule 30.08.2013
comment
из-за квадратичной сложности? - person pqnet; 30.08.2013
comment
@pqnet Согласно OP, поворот начинается с заранее определенной комбинации, а не с производительности. Кроме того, генерация перестановок - это не куадратичная, как вы говорите, а n ^ m. Проблема в том, что это не размер решения, а размер проблемы. Другими словами, N нашей проблемы действительно n ^ m. С этой точки зрения все алгоритмы генерации перестановок действительно имеют O (1) (или очень близки к нему). - person Mario Rossi; 30.08.2013
comment
@pqnet Теперь, если вы предложили мне проблему с параметрами n и m, и в моем решении я сказал, что нам нужно сгенерировать эти комбинации / перестановки, тогда да: мой алгоритм в отношении этой проблемы будет n^m. - person Mario Rossi; 30.08.2013
comment
поскольку все перестановки на самом деле n ^ m, было бы невозможно сгенерировать их за более короткое время, чем требуется для их распечатки - person pqnet; 30.08.2013
comment
Интересное решение - моя математика слишком ржавая, чтобы следить за комментариями здесь :) Говоря упрощенно, идея состоит в том, чтобы добавить комбинацию смещения (начальную) к первой комбинации (например, [ 1, 1, 1 ] и продолжить оттуда? обернуть 'где 7 = 0 и т. д.? - person JeffThompson; 30.08.2013

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

Если вы хотите начать с заданной комбинации (например, [1,6,5]) и продолжить оттуда, просто прочтите эту начальную точку как число в base-n. Затем вы можете начать перебирать последовательные комбинации, увеличивая их.

РЕДАКТИРОВАТЬ: Дальнейшее объяснение:

Начнем с массива. В массиве 6 значений, поэтому мы работаем в base-6. Мы будем предполагать, что индекс каждого элемента в массиве является значением элемента base-6.

Значения в базе 6 варьируются от 0 до 5. Это может сбивать с толку, потому что в нашем примере используются цифры, но мы могли бы сделать это с комбинациями чего угодно. Я ставлю кавычки вокруг цифр, которые мы объединяем.

Учитывая комбинацию ['1', '6', '5'], нам сначала нужно преобразовать это в значение base-6. «1» становится 0, «6» становится 5, а «5» становится 4. Используя их позиции в начальном значении в качестве их степеней в базе 6, мы получаем:

(0 * 6 ^ 0) + (5 * 6 ^ 1) + (4 * 6 ^ 2) = 174 (десятичный)

Если мы хотим узнать следующую комбинацию, мы можем добавить 1. Если мы хотим знать 20 комбинаций впереди, мы добавляем 20. Мы также можем вычесть, чтобы вернуться назад. Добавляем 1 к 174 и конвертируем обратно в base-6:

175 (десятичное) = (1 + 6 ^ 0) + (5 * 6 ^ 1) + (4 * 6 ^ 2) = 451 (base-6) = ['2', '6', '5'] ( комбинация)

Подробнее о числовых базах см. http://en.wikipedia.org/wiki/Radix и http://en.wikipedia.org/wiki/Base_%28exponentiation%29

person Centijo    schedule 30.08.2013
comment
Как упоминалось в ответе Марио Росси, моя математика не так хороша, и у меня проблемы с этим. Этот ответ по сути такой же, как у Марио? - person JeffThompson; 30.08.2013
comment
Мой ответ - это общий случай поиска перестановок. Ответ Марио относится к itertools. - person Centijo; 02.09.2013