Добавьте ребро к ориентированному ациклическому графу, чтобы он оставался ациклическим

Мой код на самом деле не делает этого как таковой, а скорее находит все возможные грани, которые можно добавить без создания цикла. Возвращаемое значение функции FindNodesToConnect — это карта, где ключ — это узел, а значение — это список всех узлов, к которым мы можем направить ребро от ключа без введения циклов.

Еще одна причуда заключается в том, что это не отдельная функция, а часть более крупного проекта нейроэволюции, поэтому входными данными является не график, а геном, в котором закодирован график, однако геном немедленно транслируется в другой структуры данных, поэтому никаких предварительных знаний не требуется (но при необходимости я был бы рад уточнить детали).

Меня больше всего беспокоит спектакль, эта функция может вызываться сотни раз в каждом поколении для сотен или тысяч поколений, но она содержит вложенные циклы и рекурсивную функцию. Есть ли способ это оптимизировать? или, может быть, совсем другой способ сделать это?

Еще один второстепенный момент: читаемость что я всегда пытаюсь улучшить.

Код:

'use strict';

if (!window.Neat) window.Neat = {};

Neat.GenomeGraph = (function () {
    const genomeToNodeMap = genome => {
        // return value is a map that maps every node to its direct predecessors
        const result = new Map()
        for (let nodeGene of genome.nodeGenes)
            result.set(nodeGene.id, [])
        for (let connectionGene of genome.connectionGenes)
            result.get(connectionGene.outId).push(connectionGene.inId)
        return result
    }

    const getDependencies = (nodeMap, nodeId) => {
        // return value is all the predecessors of a node (found recursively)
        return [].concat.apply(nodeMap.get(nodeId), nodeMap.get(nodeId).map(nextNodeId => getDependencies(nodeMap, nextNodeId)))
    }

    const setDifference = (A, B) => A.filter(x => !B.includes(x))

    return {
        FindNodesToConnect: genome => {
            const nodeMap = genomeToNodeMap(genome)
            const graphNodes = genome.nodeGenes.map(nodeGene => nodeGene.id)
            const result = new Map()

            for (let currentNodeId of graphNodes) {
                const cannotConnectTo = getDependencies(nodeMap, currentNodeId);
                cannotConnectTo.push(currentNodeId)
                for (let [nodeId, dep] of nodeMap)
                    if (dep.includes(currentNodeId))
                        cannotConnectTo.push(nodeId)

                const canConnectTo = setDifference(graphNodes, cannotConnectTo)

                if (canConnectTo.length > 0)
                    result.set(currentNodeId, canConnectTo)
            }
            return result;
        }
    }
})()

Пример вызова функции:

Neat.GenomeGraph.FindNodesToConnect({
    nodeGenes: [1, 2, 3, 4, 5, 6, 7].map(x => ({ id: x })),
    connectionGenes: [
        { inId: 1, outId: 2},
        { inId: 2, outId: 3},
        { inId: 3, outId: 6},
        { inId: 5, outId: 6},
        { inId: 4, outId: 3},
        { inId: 4, outId: 7},
    ]
})

0

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *