Я хочу предварительно вычислить некоторые значения для каждой комбинации в ряде комбинаций. Например, при выборе 3 чисел от 0 до 12, я вычислю некоторое значение для каждого:
>>> for n in choose(range(13), 3):
print n, foo(n)
(0, 1, 2) 78
(0, 1, 3) 4
(0, 1, 4) 64
(0, 1, 5) 33
(0, 1, 6) 20
(0, 1, 7) 64
(0, 1, 8) 13
(0, 1, 9) 24
(0, 1, 10) 85
(0, 1, 11) 13
etc...
Я хочу сохранить эти значения в массиве так, чтобы, учитывая комбинацию, я мог вычислить ее и получить значение. Например:
>>> a = [78, 4, 64, 33]
>>> a[magic((0,1,2))]
78
Что было бы magic
быть?
Первоначально я думал, чтобы просто сохранить его как 3-ю матрицу размера 13 x 13 x 13, таким образом, я могу легко индексировать его тот путь. В то время как это хорошо для 13, выбирают 3, это имело бы слишком, много служебное для чего-то как 13 выбирает 7.
Я не хочу использовать dict, потому что в конечном счете этот код будет в C, и массив был бы намного более эффективным так или иначе.
ОБНОВЛЕНИЕ: у Меня также есть подобная проблема, но комбинации использования с повторениями, таким образом, любые ответы о том, как получить разряд тех, очень ценились бы =).
ОБНОВЛЕНИЕ: Для прояснения я пытаюсь сохранить пространство. Каждая из этих комбинаций на самом деле индексирует во что-то, поднимают много пространства, скажем, 2 килобайта. Если бы я должен был использовать 13x13x13 массив, который составил бы 4 мегабайта, из которых мне только нужно использование 572 килобайтов (13, выбирают 3), пятна.
Вот концептуальный ответ и код, основанный на том, как работает упорядочивание лексики. (Так что я думаю, что мой ответ похож на ответ "дебила", за исключением того, что я думаю, что у него слишком мало деталей, а у его ссылок слишком много). Я написал для вас функцию unchoose(n,S)
, которая работает в предположении, что S - это подмножество упорядоченного списка range(n)
. Идея: Либо S содержит 0, либо нет. Если содержит, то удалите 0 и вычислите индекс для оставшегося подмножества. Если нет, то оно идет после binomial(n-1,k-1)
подмножества, которое содержит 0.
def binomial(n,k):
if n < 0 or k < 0 or k > n: return 0
b = 1
for i in xrange(k): b = b*(n-i)/(i+1)
return b
def unchoose(n,S):
k = len(S)
if k == 0 or k == n: return 0
j = S[0]
if k == 1: return j
S = [x-1 for x in S]
if not j: return unchoose(n-1,S[1:])
return binomial(n-1,k-1)+unchoose(n-1,S)
def choose(X,k):
n = len(X)
if k < 0 or k > n: return []
if not k: return [[]]
if k == n: return [X]
return [X[:1] + S for S in choose(X[1:],k-1)] + choose(X[1:],k)
(n,k) = (13,3)
for S in choose(range(n),k): print unchoose(n,S),S
Теперь верно и то, что вы можете кэшировать или хэшировать значения обеих функций, binomial и unchoose. И что самое приятное, вы можете найти компромисс между предварительным вычислением всего и предварительным вычислением ничего. Например, вы можете предварительно вычислить только для len(S) <= 3
.
Вы также можете оптимизировать unchoose так, чтобы он добавлял биномиальные коэффициенты с помощью цикла, если S[0] > 0
, вместо декрементирования и использования хвостовой рекурсии.
Используйте хеш-таблицу для хранения результатов . Хорошая хеш-функция может выглядеть примерно так:
h (x) = (x1 * p ^ (k - 1) + x2 * p ^ (k - 2) + ... + xk * p ^ 0)% pp
Где x1 ... xk
- числа в вашей комбинации (например, (0, 1, 2)
имеет x1 = 0, x2 = 1, x3 = 2
) и p
и pp
являются простыми числами.
Таким образом, вы должны сохранить Hash [h (0, 1, 2)] = 78
, а затем получить его таким же образом.
Примечание : хеш-таблица - это просто массив размером pp
, а не dict.
На данный момент я пришел к компромиссу: у меня есть массив 13x13x13, который просто сопоставляется с индексом комбинации, занимая 13x13x13x2 байта = 4 килобайта (с использованием коротких целых чисел), плюс нормальный -размер (13 выберите 3) * 2 килобайта = 572 килобайта, всего 576 килобайт. Намного лучше, чем 4 мегабайта, а также быстрее, чем вычисление ранга!
Я сделал это отчасти из-за того, что, похоже, я не мог заставить ответ Морона работать. Кроме того, это более расширяемо - у меня есть случай, когда мне нужны комбинации с повторениями, и я еще не нашел способа вычислить их ранг.
То, что вам нужно, называется комбинадиками . Вот моя реализация этой концепции на Python:
def nthresh(k, idx):
"""Finds the largest value m such that C(m, k) <= idx."""
mk = k
while ncombs(mk, k) <= idx:
mk += 1
return mk - 1
def idx_to_set(k, idx):
ret = []
for i in range(k, 0, -1):
element = nthresh(i, idx)
ret.append(element)
idx -= ncombs(element, i)
return ret
def set_to_idx(input):
ret = 0
for k, ck in enumerate(sorted(input)):
ret += ncombs(ck, k + 1)
return ret
Вы можете попробовать использовать лексикографический указатель комбинации. Возможно, вам поможет эта страница: http://saliu.com/bbs/messages/348.html
Эта страница MSDN содержит более подробную информацию: Создание m-го лексикографического элемента математической комбинации .
Чтобы быть более конкретным:
При обработке как кортеже вы можете упорядочить комбинации лексикографически.
Итак, (0,1,2) <(0,1,3) <(0,1,4) и т. Д.
Допустим, у вас было число от 0 до n-1, и вы выбрали k из них.
Теперь, если первый элемент равен нулю, вы знаете, что это один из первых n-1, выберите k-1.
Если первый элемент равен 1, то это один из следующих n-2, выберите k-1.
Таким образом, вы можете рекурсивно вычислить точное положение данной комбинации в лексикографическом порядке и использовать это, чтобы сопоставить ее со своим числом.
Это тоже работает в обратном порядке, и на странице MSDN объясняется, как это сделать.
Я бы предложил специализированную хэш-таблицу. Хэш для комбинации должен быть эксклюзивным или исключением из хэшей для значений. Хэши для значений - это, по сути, случайные битовые шаблоны.
Вы можете закодировать таблицу, чтобы справиться с коллизиями, но будет довольно легко вывести минимально совершенную хэш-схему - такую, где никакие две комбинации из трех элементов не дают одинакового хэш-значения, и где размер хэша и размер таблицы минимальны.
Это в основном хэширование Зобриста - считайте, что "ход" - это добавление или удаление одного элемента комбинации.
EDIT
Причина использования хэш-таблицы в том, что производительность поиска O(n), где n - количество элементов в комбинации (при условии отсутствия коллизий). Вычисление лексикографических индексов в комбинациях значительно медленнее, IIRC.
Недостатком, очевидно, является предварительная работа по созданию таблицы.