Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/ruby/22.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/algorithm/11.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Ruby 解决依赖约束_Ruby_Algorithm_Graph_Directed Graph - Fatal编程技术网

Ruby 解决依赖约束

Ruby 解决依赖约束,ruby,algorithm,graph,directed-graph,Ruby,Algorithm,Graph,Directed Graph,我有一个典型的依赖解决问题。我原以为我朝着正确的方向前进,但现在我遇到了一个路障,我不知道如何前进 背景 在已知的宇宙(所有工件及其依赖项的缓存)中,每个工件和版本之间都有1->n关系,每个版本可能包含一组不同的依赖项。例如: A 1.0.0 B (>= 0.0.0) 1.0.1 B (~> 0.1) B 0.1.0 1.0.0 给定一组“需求约束”,我想找到最好的解决方案(其中“最佳”是仍然满足所有约束的最高可能版本)。以下是解决方案的“需求约束”

我有一个典型的依赖解决问题。我原以为我朝着正确的方向前进,但现在我遇到了一个路障,我不知道如何前进

背景 在已知的宇宙(所有工件及其依赖项的缓存)中,每个工件和版本之间都有1->n关系,每个版本可能包含一组不同的依赖项。例如:

A
  1.0.0
    B (>= 0.0.0)
  1.0.1
    B (~> 0.1)
B
  0.1.0
  1.0.0
给定一组“需求约束”,我想找到最好的解决方案(其中“最佳”是仍然满足所有约束的最高可能版本)。以下是解决方案的“需求约束”示例:

solve!('A' => '~> 1.0') #=> {"A" => "1.0.1", "B" => "0.1.0"}
事实上,有更多的需求:

solve!('A' => '~> 1.0', 'B' => '>= 0.0.0', 'C' => '...', 'D' => '...')
(版本遵循标准)

我试过了 当前的解决方案使用回溯,性能不是很好。我做了一些挖掘,发现由于宇宙的大小而导致的性能问题。我决定尝试另一种方法,仅为一组需求构建一个“可能性”DAG图:

类图
def初始化
@节点={}
@边={}
结束
def节点(对象)
@节点[对象]| |=Set.new
自己
结束
def边缘(a、b)
节点(a)
节点(b)
@节点[a]。添加(b)
自己
结束
def节点
@节点.密钥
结束
def边缘
@节点。值
结束
def邻接(节点)
@节点[节点]
结束
结束
然后,我构建了一个包含宇宙中所有可能解的DAG。这大大减少了可能性的数量,并为我提供了一个带有真实工件可能性的实际图形

def填充(工件)
加载时返回?(工件)
@节点(工件)
artifact.dependency.each do | dependency|
版本(依赖)。每个版本都有依赖工件|
@图边(伪影、从属伪影)
填充(依赖_工件)
结束
结束
结束
私有的
def版本(依赖项)
可能值=@universe.versions(dependency.name,dependency.constraint)
#如果此依赖项没有版本,则短路,
#因为我们知道这个图是不可解的。
如果可能,请提出“无#{dependency}解决方案!”是否为空?
可能的
结束
因此,从前面的示例图中,如果我有需求
'A','>=0.0.0'
,我的DAG将如下所示:

+---------+   +---------+
| A-1.0.0 |   | A-1.0.1 |
+---------+   +---------+
       /  \        |
      /    \       |
     /      \      |
    /        \     |
+---------+   +---------+
| B-1.0.0 |   | B-0.1.0 |
+---------+   +---------+
由于A-1.0.0的可能值为“B的任何值”,但A-1.0.1的约束条件为“0.1系列中的任何B”。这是目前正在工作(与一个完整的测试套件)的预期

换句话说,DAG接受抽象依赖约束并创建一个“真实”图,其中每个边都是依赖项,每个顶点(我称之为
节点
)都是实际工件。如果一个解决方案存在,它就在这个图的某个地方

可悲的是,这就是我陷入困境的地方。我无法想出一个算法或程序来通过这个图表找到“最佳”路径。我也不确定一种方法来检测这个图是否是不可解的

我做了一些研究,我认为拓扑排序(tsort)是我需要的过程。但是,该算法确定依赖项的插入顺序,而不是最佳解决方案

我相当肯定这是一个np难问题,运行时可能效率低下。我认为使用DAG可以减少我必须进行的比较。我的假设错了吗?是否有更好的数据结构可供使用

最后的想法
  • 我将这个问题标记为“Ruby”,因为我正在使用Ruby,但我正在寻找psuedo代码/方向。这不是一个家庭作业问题——我真的在努力学习
  • 我已经尽可能多地介绍了背景知识,但如果您想了解某个特定主题的更多细节,请留下评论。这已经是一篇很长的文章了,但是我有更多的代码可以分享

我不是这个问题的专家,我提出的是一个不是最优的完整解决方案,因为有很多东西可以优化

算法很简单,理想情况下它是一个递归集

算法

Def

操作

堆叠。按
在堆叠的前面插入一个项目

System.assert(Condition a, Condition b):
    if (a is INVALID) then return SKIP
    else if (b.Range = a.Range) then IDENTICAL
    else if (b.Range - a.Range = {}) then VALID
    else INVALID
Stack.pop
从堆栈前面移除项目

System.assert(Condition a, Condition b):
    if (a is INVALID) then return SKIP
    else if (b.Range = a.Range) then IDENTICAL
    else if (b.Range - a.Range = {}) then VALID
    else INVALID
Set.find(x)
根据条件x搜索项目

Condition.apply(Condition b) = { this.Name, intersection(this.Range,b.Range) }

这很难。看这个问题:你看过Bundler做什么了吗?我看过。他们的解析器非常特定于bundler和Ruby生态系统。您是否绝对保证依赖关系图是非循环的?如果我没记错的话,树状SAT问题是可以处理的。编辑:我为树状SAT调用的算法称为“警告传播”,尽管我在任何地方都找不到好的、简短的编写。如果我能找到时间,我可能自己做一个。@AndyJones据我所知,这些算法只有在约束图的宽度较低(分支、树等)时才有效。一般DAG可能具有线性宽度。
for (Condition c in C)
{
    S.find(c.Name).apply(c)
}

While (Q.size > 0)
{
    Condition q = Q.pop()

    switch (T.assert(S.find(q.Name),q))
    {
      case VALID:
        S.find(q.Name).apply(q)
        q.push(S.find(q.Name).Requirement)

      case INVALID:
        S.find(q.Name).set(INVALID)

      case IDENTICAL:
      case SKIP:
    }
}

return S aka Solution
System.assert(Condition a, Condition b):
    if (a is INVALID) then return SKIP
    else if (b.Range = a.Range) then IDENTICAL
    else if (b.Range - a.Range = {}) then VALID
    else INVALID
Condition.apply(Condition b) = { this.Name, intersection(this.Range,b.Range) }