DAGがあります。ノード関数があります(大まかに言うと、ノードに番号を付けます)。これらのルールを使用して、新しい有向グラフを作成します。
- :私たちは、新しいノード間のすべての古いエッジ追加。
- この新しいグラフはまだDAGです。
最小値は何ですか?最小限の新しいグラフを作成するアルゴリズムとは何ですか?
DAGがあります。ノード関数があります(大まかに言うと、ノードに番号を付けます)。これらのルールを使用して、新しい有向グラフを作成します。
最小値は何ですか?最小限の新しいグラフを作成するアルゴリズムとは何ですか?
回答:
この問題を解決する1つの方法は、整数線形計画法(ILP)を使用することです。レッツは、問題の決定版に取り組む:与えられた、サイズのDAGを取得する契約同色の頂点への道があり≤ kは?
これは、標準的な手法を使用してILPインスタンスとして表現できます。元のグラフの各頂点の色が与えられています。私たちは、ラベルと各頂点にラベルを付けることを示唆している。同じラベルと同じ色のすべての頂点が縮小されます。したがって、決定の問題は次のようになります。同じ色の同じラベルのすべての頂点を縮小するとDAGが生成されるようなラベル付けが存在するのでしょうか。
整数線形プログラムとしてこれを表現するために、整数変数導入各頂点のためのVの頂点のラベル表現するために、Vを。不平等追加1 ≤ ℓ V ≤ kは。
次のステップは、縮約されたグラフがDAGでなければならないという要件を表現することです。上記の形式のラベルがあり、一般性を失うことなく、ラベルが縮約グラフでトポロジカルソートを引き起こすようなラベルが存在することに注意してください(つまり、縮約グラフでがwの前にある場合、vのラベルwのラベルよりも小さい)。したがって、元のグラフの各エッジv → wに対して、vとwのラベルと色が同じであるか、vのラベルがwのラベルより小さいという制約を追加します。具体的には、各エッジv初期グラフで V 、Wと同じ色を有するが、不等式追加 ℓのV ≤ ℓをwは。各エッジ用の V → Wここで、V 、Wを有する異なる色は、不等式追加 ℓのV < ℓ Wは。
次に、この整数線形プログラムに実行可能な解決策があるかどうかを確認します。(すなわち、すべて同じ色、同じラベルの頂点を契約すると、DAGを生成する)標識が所望の形態である場合にのみ実行可能な解決策があるでしょう。サイズのDAGに元のグラフを縮小する方法がある場合にのみ換言すれば、実現可能な解が存在するであろう。任意の整数線形計画法ソルバーを使用できます。ILPソルバ私たちに答えを与えるならば、我々は、元の決定問題への答えを持っています。
もちろん、これは多項式時間で完了するとは限りません。保証はありません。ただし、ILPソルバーはかなり良くなっています。妥当なサイズのグラフでは、ILPソルバーが妥当な時間でこの問題を解決できる可能性が十分にあると期待しています。
これをSATインスタンスとしてエンコードし、SATソルバーを使用することもできます。それがより効果的かどうかはわかりません。ただし、ILPバージョンはおそらく考えやすいでしょう。
(これが正しいことを願っています。詳細を注意深く確認していないので、理由を再確認してください。
更新(10/21):この形式のILPは、トポロジ的にソートされた順序でDAGを処理し、各頂点のラベルの下限を追跡することにより、線形時間で解決できるようです。これは私の解決策に疑念を抱かせます:どこかで間違いを犯しましたか?
注:AFAICT、DWはこの削減に穴を見つけましたが、これは間違っています(コメントを参照)。歴史的な理由でここに保管してください。
イントロ:最初の私は減少しますモノトーン3SATの我々の問題に問題が。けれどもモノトーン3SATの問題は自明充足、私たちの問題はさらに解決することができる最小の真のモノトーン3SATの NP困難であるという問題を、。したがって、この問題はNPハードです。
一連の変数と一連の句として表現される単調なブール式があります。CNFは、次のような形式です。
これらのノードは、同じラベルを持っているため、もちろん縮小できます。契約されている変数/ノードはfalseと評価され、契約されていないものはtrueと評価されると見なします。
node labeled . (click image for full view)
After this step, we should have nodes.
Now, if , and get contracted, will result in a cycle.
Here is another visualization, unrolling the clause constraint:
Thus, each clause constraint requires that at least one of the variables it contains remain uncontracted; since the uncontracted nodes are valued as true, this requires that one of the variables be true; exactly what Monotone SAT requires for its clauses.
Monotone 3SAT is trivially satisfiable; you can simply set all the variables to true.
However, since our DAG minimization problem is to find the most contractions, this translates to finding the satisfying assignment that produces the most false variables in our CNF; which is the same as finding the minimum true variables. This problem is sometimes called Minimum True Monotone 3SAT or here (as an optimization problem, or decision problem), or k-True Monotone 2SAT (as a weaker decision problem); both NP-hard problems. Thus our problem is NP-hard.
References:
Graph sources:
With each replacement (except for direct-parent-child replacements), you add new ancestor-descendant relationships that make it non-trivial to determine which one is actually worth it in the long-term. Therefore, a simple greedy algorithm will fail in the general case. However, if you do a brute-force approach, you can determine the smallest graph:
Python-ish (not tested):
def play((V,E),F,sequence=[]):
"""
(V,E) -- a dag.
V -- a set of vertices.
E -- a set of directed-edge-tuples.
F -- a function that takes a vertex, returns an integer.
sequence -- the sequence of moved taken so far; starts with/defaults to
an empty list, will contain tuples of the form (x,y)
where x is removed and replaced with y.
Returns the best recursively found solution.
"""
#find all the integer values in the graph, remember which
# values correspond to what vertices. Of the form {integer => {vertices}}.
n2v = {}
for x in V:
n = F(x)
#for each integer, make sure you have a set to put the vertices in.
if n not in n2v:
n2v[n] = set()
#for each integer, add the vertex to the equivalent set.
n2v[n].add(v)
#record the best sequence/solution. You start with the current sequence,
# and see if you can obtain anything better.
best_solution = list(sequence)
#Now you will try to combine a single pair of vertices, obtain a new
# graph and then recursively play the game again from that graph.
#for each integer and equivalent set of vertices,
for n,vset in n2v.iteritems():
#pick a pair of vertices
for x in vset:
for y in vset:
#no point if they are the same.
if x == y:
continue
#If there is a path from x => y or y => x, then you will be
# introducing a cycle, breaking a rule. So in that case, disregard
# this pair.
#However, the exception is when one is a direct child of the other;
# in that case you can safely combine the vertices.
if pathtest((V,E),x,y) and (x,y) not in E and (x,y) not in E:
continue
#combine the vertices (function is defined below), discard x,
# replace it with y, obtain the new graph, (V',E').
Vp,Ep = combine_vertex((V,E),x,y))
#record the sequence for this move.
sequencep = list(sequence) + [(x,y)]
#recurse and play the game from this new graph.
solution = play(Vp,Ep,F,sequencep)
#if the returned solution is better than the current best,
if len(solution) > len(best_solution):
#record the new best solution
best_solution = solution
#return the best recorded solution
return best_solution
def combine_vertex((V0,E0),x,y):
"""
(V0,E0) -- an initial digraph.
V0 -- a set of vertices.
E0 -- a set of directed-edge-tuples.
x -- vertex to discard.
y -- vertex to replace it with.
returns a new digraph replacing all relationships to and from x to relate
to y instead, and removing x from the graph entirely.
"""
#the final vertex set will have everything except x
V = set(V0)
V.discard(x)
#now you construct the edge set.
E = set()
#for every edge,
for (u0,v0) in E0:
#recreate the edge in the new graph, but replace any occurence
# of x.
u,v = u0,v0
#if x is in the edge: replace it
if u == x:
u = y
if v == x:
v == y
#sometimes u=v=y and can now be pointing to itself, don't add that
# edge
if u == v:
continue
#add the new/replaced edge into the edge-set.
E.add( (u,v) )
return (V,E)
I am not sure if it really a hard problem, but playing with some graphs manually, it seems very combinatorial. I am curious if something difficult can be reduced to this problem, or if there is an algorithm with better running time.