Как я могу максимально разделить набор? - PullRequest
2 голосов
/ 24 октября 2009

Я пытаюсь решить одну из задач Project Euler. Как следствие, мне нужен алгоритм, который поможет мне найти все возможные разделы набора в любом порядке.

Например, с учетом набора 2 3 3 5:

2 | 3 3 5
2 | 3 | 3 5
2 | 3 3 | 5
2 | 3 | 3 | 5
2 5 | 3 3

и так далее. Практически каждая возможная комбинация участников набора. Конечно, я искал в сети, но не нашел много полезного для меня, так как говорю по-программистски, а не по математике.

Может кто-нибудь помочь мне с этим? Я могу читать практически на любом языке программирования, от BASIC до Haskell, так что пишите на любом языке, который пожелаете.

Ответы [ 6 ]

2 голосов
/ 24 октября 2009

Рассматривали ли вы дерево поиска? Каждый узел будет представлять выбор места для размещения элемента, а конечные узлы являются ответами. Я не дам вам код, потому что это часть удовольствия от Project Euler;)

1 голос
/ 01 мая 2010
0 голосов
/ 07 ноября 2014

Вот код, который вам нужен для этой части вашей проблемы:

def memoize(f):
    memo={}
    def helper(x):
        if x not in memo:
            memo[x]=f(x)
        return memo[x]
    return helper

@memoize
def A000041(n):
    if n == 0: return 1
    S = 0
    J = n-1
    k = 2
    while 0 <= J:
        T = A000041(J)
        S = S+T if k//2%2!=0 else S-T
        J -= k if k%2!=0 else k//2
        k += 1
    return S

print A000041(100) #the 100's number in this series, as an example
0 голосов
/ 25 октября 2009

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

Теперь даже рекурсия для количества разделов некоторых идентичных элементов довольно сложна.

Для разбиений мультимножеств подсчет сводится к решению обобщения Задача Эйлера 181 на произвольные мультимножества.

0 голосов
/ 25 октября 2009

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

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

В любом случае, вот код Python:

import time

def separate(toseparate):
  "Find every possible way to separate a given list."
  #The list of every possibility
  possibilities = []
  n = len(toseparate)
  #We can distribute n-1 separations in the given list, so iterate from 0 to n
  for i in xrange(n):
    #Create a copy of the list to avoid modifying the already existing list
    copy = list(toseparate)
    #A boolean list indicating where a separator is put. 'True' indicates a separator
    #and 'False', of course, no separator.
    #The list will contain i separators, the rest is filled with 'False'
    separators = [True]*i + [False]*(n-i-1)
    for j in xrange(len(separators)):
      #We insert the separators into our given list. The separators have to
      #be between two elements. The index between two elements is always
      #2*[index of the left element]+1.
      copy.insert(2*j+1, separators[j])
    #The first possibility is, of course, the one we just created
    possibilities.append(list(copy))
    #The following is a modification of the QuickPerm algorithm, which finds
    #all possible permutations of a given list. It was modified to only permutate
    #the spaces between two elements, so it finds every possibility to insert n
    #separators in the given list.
    m = len(separators)
    hi, lo = 1, 0
    p = [0]*m
    while hi < m:
      if p[hi] < hi:
        lo = (hi%2)*p[hi]
        copy[2*lo+1], copy[2*hi+1] = copy[2*hi+1], copy[2*lo+1]
        #Since the items are non-unique, some possibilities will show up more than once, so we
        #avoid this by checking first.
        if not copy in possibilities:
          possibilities.append(list(copy))
        p[hi] += 1
        hi = 1
      else:
        p[hi] = 0
        hi += 1
  return possibilities

t1 = time.time()
separations = separate([2, 3, 3, 5])
print time.time()-t1
sepmap = {True:"|", False:""}
for a in separations:
  for b in a:
    if sepmap.has_key(b):
      print sepmap[b],
    else:
      print b,
  print "\n",

Он основан на алгоритме QuickPerm, подробнее о котором вы можете прочитать здесь: QuickPerm

По сути, мой код генерирует список, содержащий n разделений, вставляет их в заданный список и затем находит все возможные перестановки разделений в списке.

Итак, если мы воспользуемся вашим примером, мы получим:

2  3  3  5 
2 | 3  3  5 
2  3 | 3  5 
2  3  3 | 5 
2 | 3 | 3  5 
2  3 | 3 | 5 
2 | 3  3 | 5 
2 | 3 | 3 | 5 

Через 0,000154972076416 секунд.

Тем не менее, я прочитал описание проблемы, которую вы делаете, и вижу, как вы пытаетесь решить эту проблему, но, видя, как быстро увеличивается время выполнения, я не думаю, что он будет работать так быстро, как вы ожидаете. Помните, что проблемы Project Euler должны быть решены примерно за минуту.

0 голосов
/ 24 октября 2009

Ну, проблема имеет два аспекта.

Во-первых, предметы можно расположить в любом порядке. Так что для N предметов есть N! перестановки (при условии, что элементы рассматриваются как уникальные).
Во-вторых, вы можете представить группировку в виде битового флага между каждым элементом, указывающего деление. Было бы N-1 из этих флагов, поэтому для данной перестановки было бы 2 ^ (N-1) возможных группировок.
Это означает, что для N элементов будет в общей сложности N! * (2 ^ (N-1)) группировок / перестановок, что становится очень очень быстро.

В вашем примере четыре верхних элемента - это группы из одной перестановки. Последний пункт - это группа другой перестановки. Ваши товары можно просмотреть как:

2 на 3 от 3 от 5
2 на 3 на 3 с 5
2 на 3 с 3 на 5
2 на 3 на 3 на 5
2 от 5 на 3 от 3

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

...