Отправка большего количества кода утилиты
см. Github: https://gist.github.com/1233012#file_new.cpp
Это в основном гораздо лучший подход к генерации всех возможных перестановок, основанный на гораздо более простом подходе (поэтому у меня не былореальная причина опубликовать его раньше: в нынешнем виде он не делает ничего, кроме кода Python).
Я решил поделиться им в любом случае, как вы могли бычтобы получить некоторую прибыль от этого в качестве основы для возможного решения.
Pro:
- намного быстрее
- более умный алгоритм (использует STL и математика :))
- оптимизация инструкций
- оптимизация хранения
- универсальная модель задачи
- модель и алгоритмические идеи могут использоваться в качестве основы для правильного алгоритма
- основа для хорошего распараллеливания OpenMP ( n way, для n строк), предназначенная (но не выделенная)
Против:
- Код гораздо эффективнее за счетГибкость: адаптация кода для построения логики об ограничениях и эвристике затрат была бы намного проще с более пошаговым подходом Python
В целом, я чувствую, что мой код C ++ может быть большой выигрыш IFF оказывается, что имитированный отжиг подходит с учетом функции стоимости;Подход, использованный в коде, даст
- высокоэффективную модель хранения
- высокоэффективный способ генерации случайных / тесно связанных новых конфигураций сетки
- удобные функции отображения
Обязательная (произвольная ...) контрольная точка данных (сравнение с версией Python:)
a b c d e
f g h i j
k l m n o
p q r s t
Result: 207360000
real 0m13.016s
user 0m13.000s
sys 0m0.010s
Здесьчто мы получили до сих пор:
Из описания я получаю предположение, что у вас есть базовый граф, такой как 
путьдолжен быть построен так, чтобы он посещал все узлы в сетке ( гамильтонов цикл ).
Дополнительным ограничением является то, что последующие узлы должны быть взяты из следующего rank (ad, да, il является тремя ranks ; после посещения узла из последнего rank путь должен продолжаться с любого не посещенного узла из первого rank
Ребра взвешены в том смысле, что они связаны с затратами, однако весовая функция не является традиционной для алгоритмов графа, поскольку стоимость зависит от полного пути, а не только от конечных точек каждого ребра.
В свете этого я полагаю, что мы находимся в области проблем с «полным покрытием» (требуется алгоритм A *, наиболее известный из статьи Knuths Dancing Links).
Конкретно Без дополнительной информации (эквивалентность путей, конкретные свойства функции стоимости) наилучшим известным алгоритмом для получения «самого дешевого» гамильтонова пути, который удовлетворяет ограничениям, будет
- generate все возможностиible такие пути
- вычисляют функцию фактической стоимости для каждого
- выбирают путь минимальной стоимости
Именно поэтому я отправился и написал действительногенератор тупой грубой силы, который генерирует все возможные уникальные пути в общей сетке NxM.
Конец Вселенной
Выход для сетки выборки 3 × 4 равен 4! 3 = 13824 возможных путей ... Экстраполируя это на 6 × 48 столбцов, можно получить 6! 48 = 1,4 × 10 137 возможностей.Очень ясно , что без дальнейшей оптимизации проблема не поддается решению (NP Hard или что-то - я никогда не помню довольно тонкие определения).
Взрыв времени выполненияоглушает:
- 3 × 4 (измерено) занимает около 0,175 с
- 4 × 5 (измерено) заняло около 6 м5 с (работает без выхода и под PyPy 1.6 на быстрой машине)
- 5 × 6 заняло бы примерно 10 лет и 9+ месяцев ...
В 48x6 мы будем смотреть на ... что ... 8,3x10 107 lightyears (прочитайте внимательно)
В любом случае, вот код Python (все предустановлены для сетки 2 × 3)
#!/usr/bin/python
ROWS = 2
COLS = 3
## different cell representations
def cell(r,c):
## exercise for the reader: _gues_ which of the following is the fastest
## ...
## then profile it :)
index = COLS*(r) + c
# return [ r,c ]
# return ( r,c )
# return index
# return "(%i,%i)" % (r,c)
def baseN(num,b,numerals="abcdefghijklmnopqrstuvwxyz"):
return ((num == 0) and numerals[0]) or (baseN(num // b, b, numerals).lstrip(numerals[0]) + numerals[num % b])
return baseN(index, 26)
ORIGIN = cell(0,0)
def debug(t): pass; #print t
def dump(grid): print("\n".join(map(str, grid)))
def print_path(path):
## Note: to 'normalize' to start at (1,1) node:
# while ORIGIN != path[0]: path = path[1:] + path[:1]
print " -> ".join(map(str, path))
def bruteforce_hamiltonians(grid, whenfound):
def inner(grid, whenfound, partial):
cols = len(grid[-1]) # number of columns remaining in last rank
if cols<1:
# assert 1 == len(set([ len(r) for r in grid ])) # for debug only
whenfound(partial) # disable when benchmarking
pass
else:
#debug(" ------ cols: %i ------- " % cols)
for i,rank in enumerate(grid):
if len(rank)<cols: continue
#debug("debug: %i, %s (partial: %s%s)" % (i,rank, "... " if len(partial)>3 else "", partial[-3:]))
for ci,cell in enumerate(rank):
partial.append(cell)
grid[i] = rank[:ci]+rank[ci+1:] # modify grid in-place, keeps rank
inner(grid, whenfound, partial)
grid[i] = rank # restore in-place
partial.pop()
break
pass
# start of recursion
inner(grid, whenfound, [])
grid = [ [ cell(c,r) for r in range(COLS) ] for c in range(ROWS) ]
dump(grid)
bruteforce_hamiltonians(grid, print_path)