PHP中实现拓扑算法

概念

拓扑算法是一个有向无线算法

是一个处理复杂关系的算法

应用场景

A = B+C

B = C+D

C = A+T

这里的值因为计算顺序不同,会导致计算结果不同,而拓扑排序就能解决这个问题,找出先计算那一位

环的概念

拓扑排序中的一个bug

如果

A =B+C

B = C+D

C = A+D

这里A的值会影响C,C会影响A,所以一旦出现这种情况即预示着出现了环,该逻辑有问题

代码示例

php 复制代码
//假设数据结构是这样的
public function text(){
        $accounts = [
            ['name' => 'A', 'value' => "C + B"],
            ['name' => 'B', 'value' => "C + D"],
            ['name' => 'C', 'value' => 100],
            ['name' => 'D', 'value' => 100],
            ['name' => 'E', 'value' => "A + B + C"],
        ];

        $initialValues = [
            'C' => 100,
            'D'=> 100,
        ];

$analysister_config_data = $this->calculateAccountBalances($accounts,$initialValues);
return['data'=>$analysister_config_data ];
}




 /**
     * @param $accounts
     * @param $initial_values
     * @return array
     * 用于计算所有所有账户最终余额
     */
    function calculateAccountBalances($accounts, $initialValues) {
        $graph = $this->buildDependencyGraph($accounts);
        $sortedNodes = $this->topologicalSort($graph);

        $balances = $initialValues;
        foreach ($sortedNodes as $node) {
            if (isset($balances[$node])) {
                continue;
            }
            // 获取当前节点的表达式
            $expression = $accounts[array_search($node, array_column($accounts, 'name'))]['value'];
            // 替换表达式中的变量为已知值
            foreach ($balances as $key => $value) {
                $expression = str_replace($key, $value, $expression);
            }
            // 计算表达式的值
            $balances[$node] = eval("return $expression;");
        }

        return $balances;
    }




/**
     * @param $value:当前账户的值,
     * @param $accountValues:一个引用数组,存储已经计算的值避免重复计算
     * @param $accounts:所有账户
     * @return int*
     */
    function parseValue($value, &$accountValues, $accounts, $initialValues) {
        $parts = explode(' ', $value);
        $result = 0;
        $operator = '+';

        foreach ($parts as $part) {
            if ($part === '+' || $part === '-') {
                $operator = $part;
                continue;
            }

            if (ctype_alnum($part)) { // 只处理有效的账户名称
                if (isset($accountValues[$part])) {
                    $value = $accountValues[$part];
                } elseif (isset($initialValues[$part])) {
                    $value = $initialValues[$part];
                    $accountValues[$part] = $value; // 确保将初始值存储到 accountValues 中
                } else {
                    if (isset($accounts[$part])) {
                        $value = $this->parseValue($accounts[$part]['value'], $accountValues, $accounts, $initialValues);
                    } else {
                        echo ("账户没找到 " . $part);
                    }
                }
            } else {
                // 如果 part 是数字,直接使用
                $value = (int)$part;
            }

            if ($operator === '+') {
                $result += $value;
            } else {
                $result -= $value;
            }
        }

        return $result;
    }


 /**
     * @param $accounts
     * @return array
     * 将需要计算的数据生成为依赖关系图
     */
    function buildDependencyGraph($accounts) {
        $graph = [];
        foreach ($accounts as $account) {
            $name = $account['name'];
            if (!isset($graph[$name])) {
                $graph[$name] = [];
            }
            $parts = explode(' ', $account['value']);
            foreach ($parts as $part) {
                if (is_numeric($part)){
                    continue;
                }
                if (ctype_alnum($part) && !in_array($part, ['+', '-'])) {
                    $graph[$name][] = $part;
                    if (!isset($graph[$part])) {
                        $graph[$part] = [];
                    }
                }
            }
        }
        return $graph;
    }

    /**
     * @param $graph 依赖图,是一个二维数组,表示依赖关系
     * @return array
     * 用于对数据排序,按照依赖关系排序
     */
    function topologicalSort($graph) {
        $inDegree = []; // 节点的入度
        $zeroDegree = []; // 入度为0的节点
        $sorted = []; // 拓扑排序结果

        // 初始化所有节点的入度为0
        foreach ($graph as $node => $dependencies) {
            $inDegree[$node] = 0;
        }

        // 计算每个节点的入度
        foreach ($graph as $node => $dependencies) {
            foreach ($dependencies as $dependency) {
                if (ctype_alnum($dependency)) { // 确保只处理有效的账户名称
                    if (!isset($inDegree[$dependency])) {
                        $inDegree[$dependency] = 0; // 确保所有节点都在 inDegree 数组中
                    }
                    $inDegree[$node]++; // 这里应该增加当前节点的入度
                }
            }
        }

        // 找出入度为0的节点
        foreach ($inDegree as $node => $degree) {
            if ($degree == 0) {
                $zeroDegree[] = $node;
            }
        }

        // 进行拓扑排序
        while (!empty($zeroDegree)) {
            $node = array_shift($zeroDegree);
            $sorted[] = $node;


            // 处理当前节点的依赖关系
            foreach ($graph as $currentNode => $dependencies) {
                if (in_array($node, $dependencies)) {
                    $inDegree[$currentNode]--;
                    if ($inDegree[$currentNode] == 0) {
                        $zeroDegree[] = $currentNode;
                    }
                }
            }
        }

        // 检测是否存在环
        if (count($sorted) != count($graph)) {
            echo '存在环,无法进行拓扑排序';
        }
        return $sorted;
    }
相关推荐
dundunmm12 分钟前
机器学习之scikit-learn(简称 sklearn)
python·算法·机器学习·scikit-learn·sklearn·分类算法
古希腊掌管学习的神12 分钟前
[机器学习]sklearn入门指南(1)
人工智能·python·算法·机器学习·sklearn
波音彬要多做14 分钟前
41 stack类与queue类
开发语言·数据结构·c++·学习·算法
Swift社区22 分钟前
Excel 列名称转换问题 Swift 解答
开发语言·excel·swift
一道微光26 分钟前
Mac的M2芯片运行lightgbm报错,其他python包可用,x86_x64架构运行
开发语言·python·macos
矛取矛求30 分钟前
QT的前景与互联网岗位发展
开发语言·qt
Leventure_轩先生30 分钟前
[WASAPI]从Qt MultipleMedia来看WASAPI
开发语言·qt
向宇it44 分钟前
【从零开始入门unity游戏开发之——unity篇01】unity6基础入门开篇——游戏引擎是什么、主流的游戏引擎、为什么选择Unity
开发语言·unity·c#·游戏引擎
是娜个二叉树!1 小时前
图像处理基础 | 格式转换.rgb转.jpg 灰度图 python
开发语言·python
Schwertlilien1 小时前
图像处理-Ch5-图像复原与重建
c语言·开发语言·机器学习