Рекурсивная итерация по хэш-таблице (Powershell)

#powershell #adjacency-matrix #connected-components

#powershell #матрица смежности #подключенные компоненты

Вопрос:

У меня есть хэш-таблица, которая имитирует эту таблицу. Так, например, $ h [‘C’]. C сопоставляется с 3, $ h [‘A’] .A соответствует 1

Группы A B C D
A 1 0 0 0
B 0 5 2 0
C 0 2 3 0
D 0 0 0 2

Я хотел бы выполнить итерацию по таблице, чтобы найти группировки. Например, вывод будет следующим:
A
B, C
D

A и D находятся на своей собственной строке, поскольку в столбце или строке есть только одна запись без нуля.
B и C вместе, поскольку их столбец и / или строка пересекаются с числом, большим нуля.
Я думаю, что решение должно включать некоторый тип рекурсии.

У меня есть это, но я думаю, что мне нужно рекурсивно вызвать его

 foreach($i in $h.GetEnumerator()){
   $Results = 'Groups'
   foreach($t in $h.GetEnumerator()){
            $iString = $i.Name
            $tString = $t.Name
            if($h[$iString].$tString -ne '0'){
               if($iString -eq $tString){
                    $Results  = ',' $tString 
               }
               else{
                #recursive call
               }
            }
   }
   $Results
}
 

Комментарии:

1. Покажите нам вашу попытку кодирования для решения этой проблемы. Вы также можете подумать о том, чтобы начать принимать ответы на свои предыдущие вопросы.

2. Всегда ли таблица отражается вниз по диагонали? То есть, всегда ли [X, Y] равно [Y, X]? Это просто совпадение в ваших образцах данных?

3. Да, это отражается вниз по диагонали.

4. Я думаю, вы можете преобразовать это в задачу с графом, если вы обрабатываете свою хэш-таблицу как матрицу смежности (см. en.wikipedia.org/wiki/Adjacency_matrix ), где ненулевое значение указывает на ребро, соединяющее два узла в неориентированном графе. Затем вы пытаетесь найти все связанные компоненты — en.wikipedia.org/wiki/Component_ (graph_theory) — которые представляют собой наборы узлов в каждом отдельном подграфе.

5. да, по сути, это матрица смежности, в которой мне нужно найти связанные компоненты

Ответ №1:

Согласно комментариям, вы можете преобразовать это в проблему с графом, если вы рассматриваете свою хэш-таблицу как матрицу смежности, где ненулевое значение указывает на ребро, соединяющее два узла в ориентированном графе.

Затем вы пытаетесь найти все связанные компоненты, которые представляют собой наборы узлов в каждом отдельном подграфе.

Вы можете превратить свою хеш-таблицу в список ребер в ориентированном графе следующим образом:

 $data = [ordered] @{
    "A" = [ordered] @{
        "A" = 1
        "B" = 0
        "C" = 0
        "D" = 0
    }
    "B" = [ordered] @{
        "A" = 0
        "B" = 5
        "C" = 2
        "D" = 0
    }
    "C" = [ordered] @{
        "A" = 0
        "B" = 2
        "C" = 3
        "D" = 0
    }
    "D" = [ordered] @{
        "A" = 0
        "B" = 0
        "C" = 0
        "D" = 2
    }
}

$nodes = $data.GetEnumerator() | foreach-object { $_.Key };
$edges = $data.GetEnumerator() | foreach-object {
    $start = $_.Key;
    $_.Value.GetEnumerator() | foreach-object {
        if( $_.Value -ne 0 )
        {
            write-output (new-object PSCustomObject -Property ([ordered] @{
                "Start" = $start
                "End"   = $_.Key
            }));
        }
    }
};

write-host ($nodes | format-list * | out-string);
# A
# B
# C
# D

write-host ($edges | format-table | out-string);
# Start End
# ----- ---
# A     A
# B     B
# B     C
# C     B
# C     C
# D     D
 

Затем вам «просто» нужно выполнить несколько поисков в глубину, чтобы найти подграфы…

 # based on the non-recursive pseudocode at
# https://en.wikipedia.org/wiki/Depth-first_search#Pseudocode

$subgraphs = new-object System.Collections.ArrayList;

$unvisited = new-object System.Collections.ArrayList;
$unvisited.AddRange($nodes);

while( $unvisited.Count -gt 0 )
{

    $subgraph = @();
    $node     = $unvisited[0];

    # let S be a stack
    $stack = new-object System.Collections.Generic.Stack[string];

    # S.push(v)
    $stack.Push($node);

    # while S is not empty do
    while( $stack.Count -gt 0 )
    {

        # v = S.pop()
        $node = $stack.Pop();

        # if v is not labeled as discovered then
        if( $unvisited -contains $node )
        {

            # label v as discovered
            $unvisited.Remove($node);

            # for all edges from v to w in G.adjacentEdges(v) do
            $adjacents = $edges | where-object { $_.Start -eq $node };
            foreach( $adjacent in $adjacents )
            {
                # S.push(w)
                $stack.Push($adjacent.End);
            }

            $subgraph  = $node;

        }
    }

    $null = $subgraphs.Add($subgraph);

}
 

что дает следующие результаты:

 foreach( $subgraph in $subgraphs )
{
    write-host $subgraph
}
# A
# B C
# D
 

Примечание — не проверялось ни для каких наборов данных, кроме того, который указан в вопросе!