Что такое хороший алгоритм для получения минимального покрытия вершины дерева?

Оптимизация компилятора операторов switch является сложной задачей. Конечно, вам нужно включить оптимизацию (например, попробуйте скомпилировать ваш код с gcc -O2 -fverbose-asm -S с GCC и заглянуть в сгенерированный файл .s ассемблера). Кстати, в обоих ваших примерах мой GCC 7 на Debian / Sid / x86-64 дает просто:

        .type   main, @function
main:
.LFB0:
        .cfi_startproc
# rsp.c:13: }
        xorl    %eax, %eax      #
        ret
        .cfi_endproc

(поэтому в этом сгенерированном коде нет никаких следов switch) sup >

Если вам нужно понять, как компилятор может оптимизировать switch, есть несколько статей на эту тему, например, , эта .

Если у меня будет больше случаев переключения, то порядок падений влияет на производительность?

Не в целом, если вы используете какой-то оптимизирующий компилятор и просить его оптимизировать. Смотрите также этот .

Если это так важно для вас (но это не так, оставьте микрооптимизации для вашего компилятора!), Вам нужно провести тестирование, профилировать и, возможно, изучить сгенерированный ассемблерный код. Кстати, кеширование пропускает и распределение регистров может иметь гораздо большее значение, чем порядок case -s, поэтому я думаю, что вам вообще не стоит беспокоиться. Имейте в виду приблизительные временные оценки современных компьютеров. Поместите case в наиболее читаемый порядок (для следующего следующего разработчика , работающего над тем же исходным кодом). Читайте также о резьбовом коде . Если у вас есть объективные (связанные с производительностью) причины для переупорядочения case -ов (что очень маловероятно и должно произойти не чаще одного раза в жизни), напишите хороший комментарий, объясняющий эти причины.

Если вы так сильно заботитесь о производительности, обязательно выполните тесты и профиля , выберите хороший компилятор и используйте его с соответствующими параметрами оптимизации. Возможно, поэкспериментируйте с несколькими различными настройками оптимизации (и, возможно, с несколькими компиляторами). Вы можете добавить -march=native (в дополнение к -O2 или -O3). Вы можете рассмотреть возможность компиляции и связывания с -flto -O2, чтобы включить оптимизацию во время связи и т. Д. Вы также можете захотеть оптимизации на основе профиля .

Кстати, многие компиляторы являются огромными проектами свободного программного обеспечения (в частности GCC и Clang ). Если вы так сильно заботитесь о производительности, вы можете пропатчить компилятор, расширить его, добавив дополнительный проход оптимизации (путем разветвления исходного кода, путем добавления некоторого плагина в GCC или некоторого GCC MELT расширения). Это требует месяцев или лет работы (особенно для понимания внутренних представлений и организации этого компилятора).

(Не забудьте учесть затраты на разработку; в большинстве случаев они стоят намного больше) sup>

18
задан Bill the Lizard 19 September 2012 в 02:00
поделиться

3 ответа

Я надеюсь здесь вы можете найти более связанный ответ на свой вопрос.


Я думал о своем решении, возможно, вам нужно будет его отполировать, но пока динамическое программирование находится в одном из ваших тегов, вам, вероятно, потребуется:

  1. Для каждой вершины u определить S + (u) равно размер покрытия с вершиной u и S- (u) покрытие без вершины u.
  2. S + (u) = 1 + Sum (S- (v)) для каждого потомка v элемента u.
  3. S- (u) = Sum (max {S- (v), S + (v)}) для каждого потомка v из u.
  4. Ответ max (S + (r), S- (r)), где r - корень вашего дерева.

После прочтения this . Изменил вышеупомянутый алгоритм, чтобы найти максимальное независимое множество, так как в вики-статье

набор является независимым тогда и только тогда, когда его дополнение является вершинным покрытием.

Таким образом, изменив min на max, мы можем найти максимальное независимое множество и добавлением минимального покрытия вершин, поскольку обе задачи эквивалентны.

10
ответ дан 30 November 2019 в 07:23
поделиться
{- Haskell implementation of Artem's algorithm -}

data Tree = Branch [Tree]
    deriving Show

{- first int is the min cover; second int is the min cover that includes the root -}
minVC :: Tree -> (Int, Int)
minVC (Branch subtrees) = let
    costs = map minVC subtrees
    minWithRoot = 1 + sum (map fst costs) in
    (min minWithRoot (sum (map snd costs)), minWithRoot)
2
ответ дан 30 November 2019 в 07:23
поделиться

T (V, E) - это дерево, из которого следует, что для любого листа любое минимальное покрытие вершин должно включать в себя либо лист, либо вершину, смежную с листом. Это дает нам следующий алгоритм поиска S, вершинного покрытия:

  1. Найти все листья дерева (BFS или DFS), O (| V |) в дереве.
  2. Если (u, v) является ребро такое, что v является листом, добавьте u к покрытию вершины и удалите (u, v). Это оставит вас с лесом T_1 (V_1, E_1), ..., T_n (U_n, V_n).
  3. Теперь, если V_i = {v}, то есть | V_i | = 1, то это дерево можно отбросить так как все инцидентные на v ребра покрыты. Это означает, что у нас есть условие завершения рекурсии, при котором у нас либо одна вершина, либо нет, и мы можем вычислить S_i как покрытие для каждого T_i и определить S как все вершины из шага 2 объединяют покрытие каждого T_i .

Теперь,

11
ответ дан 30 November 2019 в 07:23
поделиться
Другие вопросы по тегам:

Похожие вопросы: