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;
    }
相关推荐
864记忆16 小时前
Qt QML 模块及其函数详解
开发语言·qt
无敌最俊朗@16 小时前
C++ 对象布局之padding(填充字节)
开发语言·c++
Miraitowa_cheems16 小时前
LeetCode算法日记 - Day 104: 通配符匹配
linux·数据结构·算法·leetcode·深度优先·动态规划
共享家952716 小时前
高级IO-poll
开发语言·操作系统·io
程序员东岸16 小时前
从零开始学二叉树(上):树的初识 —— 从文件系统到树的基本概念
数据结构·经验分享·笔记·学习·算法
Chiang木16 小时前
C++进阶:coroutine 协程
开发语言·c++·协程
星光一影16 小时前
陪诊陪检系统源码,陪诊小程序,陪诊APP,陪诊服务,家政上门系统,居家护理陪护源码
mysql·小程序·uni-app·php
ivy1598683771516 小时前
JM20329是一款高性能、低功耗的USB桥接芯片,实现串行接口(如SATA、IDE)与USB接口之间的数据转换。
c语言·开发语言·ide·嵌入式硬件·eureka·音视频·视频编解码
渡我白衣16 小时前
深入 Linux 内核启动:从按下电源到用户登录的全景解剖
java·linux·运维·服务器·开发语言·c++·人工智能
七夜zippoe16 小时前
Java 9+模块化系统(JPMS)详解:设计与迁移实践
java·开发语言·maven·模块化·jmm