Вычислить разряд комбинации?

Я хочу предварительно вычислить некоторые значения для каждой комбинации в ряде комбинаций. Например, при выборе 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), пятна.

14
задан Claudiu 29 June 2010 в 19:24
поделиться

6 ответов

Вот концептуальный ответ и код, основанный на том, как работает упорядочивание лексики. (Так что я думаю, что мой ответ похож на ответ "дебила", за исключением того, что я думаю, что у него слишком мало деталей, а у его ссылок слишком много). Я написал для вас функцию 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, вместо декрементирования и использования хвостовой рекурсии.

10
ответ дан 1 December 2019 в 12:51
поделиться

Используйте хеш-таблицу для хранения результатов . Хорошая хеш-функция может выглядеть примерно так:

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.

1
ответ дан 1 December 2019 в 12:51
поделиться

На данный момент я пришел к компромиссу: у меня есть массив 13x13x13, который просто сопоставляется с индексом комбинации, занимая 13x13x13x2 байта = 4 килобайта (с использованием коротких целых чисел), плюс нормальный -размер (13 выберите 3) * 2 килобайта = 572 килобайта, всего 576 килобайт. Намного лучше, чем 4 мегабайта, а также быстрее, чем вычисление ранга!

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

1
ответ дан 1 December 2019 в 12:51
поделиться

То, что вам нужно, называется комбинадиками . Вот моя реализация этой концепции на 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
1
ответ дан 1 December 2019 в 12:51
поделиться

Вы можете попробовать использовать лексикографический указатель комбинации. Возможно, вам поможет эта страница: 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 объясняется, как это сделать.

5
ответ дан 1 December 2019 в 12:51
поделиться

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

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

Это в основном хэширование Зобриста - считайте, что "ход" - это добавление или удаление одного элемента комбинации.

EDIT

Причина использования хэш-таблицы в том, что производительность поиска O(n), где n - количество элементов в комбинации (при условии отсутствия коллизий). Вычисление лексикографических индексов в комбинациях значительно медленнее, IIRC.

Недостатком, очевидно, является предварительная работа по созданию таблицы.

2
ответ дан 1 December 2019 в 12:51
поделиться
Другие вопросы по тегам:

Похожие вопросы: