c++ - 在 C++ 中通过网格/矩阵找到成本优化路径

标签 c++ algorithm graph permutation shortest-path

我遇到了一个问题,在网上找不到太多帮助。我需要从多个数字 vector 中找到数字的最小成本组合。所有 vector 的 vector 大小相同。 例如,考虑以下内容:

row [0]:  a  b  c  d   
row [1]:  e  f  g  h  
row [2]:  i  j  k  l  

现在我需要通过从每一行中取一个元素来找到数字的组合,即 vector ,例如:aei
在此之后,我需要找到其他三个不相交的组合,例如:bfj、cgk、dhl。我根据选择的这四种组合计算成本。目标是找到成本最低的组合。另一种可能的组合可以是:afj、bei、chk、dgl。如果总列数为 d,行数为 k,则可能的总组合为 d^k。这些行存储为 vector 。我被困在这里,我发现很难为上述过程编写算法。如果有人可以提供帮助,我将不胜感激。
谢谢。

// I am still working on the algorithm. I just have the vectors and the cost function.  

//Cost Function  , it also depends on the path chosen
float cost(int a, int b, PATH to_a) {  
float costValue;  
...  
...  
return costValue;  
}  

vector< vector < int > > row;  
//populate row  
...   
...
//Suppose  

//    row [0]:  a  b  c  d   
//    row [1]:  e  f  g  h  
//    row [2]:  i  j  k  l   

// If a is chosen from row[0] and e is chosen from row[1] then,  
float subCost1 = cost(a,e, path_to_a);  

// If i is chosen from row[2] ,  
float subCost2 = cost(e,i,path_to_e);  

// Cost for selecting aei combination is  
float cost1 = subCost1 + subCost2;  

//similarly other three costs need to be calculated by selecting other remaining elements  
//The elements should not intersect with each other eg. combinations aei and bej cannot exist on the same set.  

//Suppose the other combinations chosen are bfj with cost cost2, cgk with cost cost3   and dhl with cost cost4  
float totalCost = cost1 + cost2 + cost3 + cost4;   

//This is the cost got from one combination. All the other possible combinations should be enumerated to get the minimum cost combination. 

最佳答案

Posting more utility code

see github: https://gist.github.com/1233012#file_new.cpp

This is basically an much better approach to generating all possible permutations based on much simpler approach (therefore I had no real reason to post it before: As it stands right now, it doesn't do anything more than the python code).

I decided to share it anyways, as you might be able to get some profit out of this as the basis for an eventual solution.

Pro:

  • much faster
    • smarter algorithm (leverages STL and maths :))
    • instruction optimization
    • storage optimization
  • generic problem model
  • model and algorithmic ideas can be used as basis for proper algorithm
  • basis for a good OpenMP parallelization (n-way, for n rows) designed-in (but not fleshed out)

Contra:

  • The code is much more efficient at the cost of flexibility: adapting the code to build in logic about the constraints and cost heuristics would be much easier with the more step-by-step Python approach

All in all I feel that my C++ code could be a big win IFF it turns out that Simulated Annealing is appropriate given the cost function(s); The approach taken in the code would give

  • a highly efficient storage model
  • a highly efficient way to generate random / closely related new grid configurations
  • convenient display functions

Mandatory (abritrary...) benchmark data point (comparison to the python version:)

  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

这是我们到现在为止的:

  • 从描述中,我了解到您有一个基本图表,例如 enter image description here

  • 必须构造一条访问网格中所有节点的路径 ( Hamiltonian cycle )。

  • 额外的限制是后续节点必须从下一个rank(a-d、e-h、i-l 是三个rank em>;一旦访问了最后一个 rank 的节点,路径必须与来自第一个 rank

  • 的任何未访问节点继续
  • 边缘是加权的,因为它们具有相关的成本。然而,权重函数对于图算法来说并不是传统的,因为成本取决于完整路径,而不仅仅是每条边的端点。

鉴于此,我相信我们处于“全覆盖”问题的领域(需要 A* 算法,最著名的是 Knuths Dancing Links 论文)。

具体来说如果没有进一步的信息(路径的等价性,成本函数的特定属性),获得满足约束的“最便宜”汉密尔顿路径的最知名算法将是

  • 生成所有可能的这样的路径
  • 计算每个的实际成本函数
  • 选择成本最低的路径

这就是为什么我开始编写一个非常愚蠢的蛮力生成器,它可以在 NxM 的通用网格中生成所有可能的唯一路径。

宇宙的尽头

3×4 样本网格的输出为 4!3 = 13824 条可能的路径...外推到 6×48 列,得到 6!48 = 1.4×10137 种可能性。很明显 如果不进一步优化,问题就难以解决(NP Hard 之类的——我不记得很微妙的定义)。

运行时的爆炸声震耳欲聋:

  • 3×4(实测)大约需要 0.175 秒
  • 4×5(实测)大约需要 6 分 5 秒(在快速机器上在 PyPy 1.6 下无输出运行)
  • 5×6 大约需要 10 年零 9 个月以上...

在 48x6 时,我们会看到...什么... 8.3x10107 (仔细阅读)

现场观看:http://ideone.com/YsVRE

无论如何,这里是 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)

关于c++ - 在 C++ 中通过网格/矩阵找到成本优化路径,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7391488/

相关文章:

c++ - 观察者模式,智能指针和单例类

c++ - 为路径的每个元素创建一个目录(如果它不存在)

c++ - 算法优化

java - 理解 Donald B. Johnson 算法中的伪代码

c++ - 您在 c/c++ 项目中使用指针到指针和/或指针引用最有用的时间是什么时候?

c++ - 使用git安装多个版本的软件

algorithm - Pollard Rho 分解方法实现

将三角形带的顶点转换为多边形的算法

javascript - 在 D3.js 中创建双向图

python - 图中两个节点之间的多条边