【问题标题】:How to set a recursive steps counter using MySQL data in PHP如何在 PHP 中使用 MySQL 数据设置递归步数计数器
【发布时间】:2017-11-04 22:58:26
【问题描述】:

所以,我正在创建一个有趣的迷你策略游戏,但偶然发现了一个我无法真正解决的问题。

我认为这很容易,但似乎我错了,或者解决方案就在我的眼皮底下,但我正在寻找另一种观点来解决我的问题。

我有一个数据库,其中包含记录在地图上的领土列表。这些地区中的每一个都有一个相邻地区的列表。

ID | name  | adjacent     | faction

1  | Name1 | 2;7;10;24    | 5
2  | Name2 | 1;3;7;8      | 4
3  | Name3 | 2;4;5;8      | 8

adjacent 是由分号分隔的 ID 列表

我的目标是从某个派系拥有的所有领土中找到通往某个领土的最短路线。距离是根据到达该地区所需的步数来计算的。

这样,我可以快速获取距离并在这方面应用修饰符。

比如上面的数据sn-p中:

  • 区域 1 与区域 2 的距离为 0,因为它们相邻。

  • 区域 1 与区域 3 的距离为 1,因为 3 与 2 相邻(而 2 与 1 相邻)。

  • 区域 1 与 4 的距离为 2,因为 4 与 3 相邻,而 3 又与 2 相邻。

目前,我的代码如下所示;

function getProximity($connexion, $faction, $terrain)
{
    $query = "SELECT id, adjacent FROM worldmap WHERE faction = ".$faction;
    $result = mysqli_query($connexion, $query);

    $proximity = 10;
    while ($row = mysqli_fetch_row($result))
    {
        $adjacent = explode(";", $row[1]);
        if (in_array($terrain, $adjacent))
        {
            //territory is adjacent, stop looking
            return 0;
        }
        else
        {
            //if not directly adjacent, seek through the list
            foreach($adjacent as $adj)
            {
                $prox = getSubProximity($connexion, array($row[0]), $adj, $terrain, 1);
                if ($prox < $proximity)
                {
                    $proximity = $prox;
                }
            }
        }
    }

    return $proximity;
}

function getSubProximity($connexion, $from, $terrain, $terrain2, $proximity)
{
    //Attempt to break weird loops... not succesful...
    if ($proximity > 10)
    {
        return $proximity;
    }
    $query = "SELECT id, adjacent FROM worldmap WHERE id = ".$terrain;
    $result = mysqli_query($connexion, $query);

    while ($row = mysqli_fetch_row($result))
    {
        $adjacent = explode(";", $row[1]);
        if (in_array($terrain2, $adjacent))
        {
            //territory is adjacent, stop looping
            return $proximity;
        }
        else
        {
            foreach($adjacent as $adj)
            {
                //check if we've been through there
                if (!in_array($adj, $from))
                {
                    array_push($from, $terrain);
                    $prox = getSubProximity($connexion, $from, $adj, $terrain2, $proximity+1);
                    if ($prox < $proximity)
                    {
                        $proximity = $prox;
                    }
                }
            }
        }
    }
}

感谢您的帮助,如果您有其他方法可以获得相同的结果,请随意;数据库结构不是静态的,完全在我的控制之下。

【问题讨论】:

  • 相邻应该是另一个带有one-to-many relationship的表。
  • 使用另一个表也不错,但我可能仍会被困在同一个地方,除非您正在考虑 SQL 中的解决方案
  • 这将使 SQL 查询相关邻居变得更加容易,但实际上在您的 SQL 数据中的哪个位置保存了任何距离值?我没有看到有关距离的数据,所以我不知道(SQL 也不知道)如何计算出从 ab 的最短路线
  • 距离按到达领地所需的步数计算。
  • 我添加了 SQL 标签来帮助那些比我有更多 SQL 知识的人可以看到这个:-)

标签: php mysql sql recursion


【解决方案1】:

我会建议一些事情:

  • MySql 在处理自引用/分层数据方面不是那么强大,因此与通常的答案(“在 SQL 中执行”)相反,在 PHP 中执行逻辑。

  • 由于总的数据集比较小,所以一次查询就读取内存中所有地区的数据,然后把数据库排除在外。

  • 执行非递归的广度优先搜索。这样,您最多只需要访问任何世界领土一次。

  • 保留您已经访问过的地区的列表,以避免无限循环,并为此使用关联数组,以便快速查找键(而不是低效的in_array)。仅记录 $from 区域是不够的。

以下是此类代码的概要:

// This function returns the whole data set in a nice data structure 
function getProximityData($connexion) {
    $query = "SELECT id, adjacent, faction FROM worldmap";
    $result = mysqli_query($connexion, $query);
    while ($row = mysqli_fetch_row($result)) {
        $territories[$row[0]] = [
            "adjacent" => explode(";", $row[1]),
            "faction" => $row[2]
        ];
    }
    return $territories;
}

function getProximity($territories, $faction, $terrain) {
    // mark this terrain as visited
    $visited[$terrain] = 1;
    // Create queue for breadth first search
    $queue = [$terrain];
    $steps = 0;
    while (count($queue)) {
        $next = [];
        foreach($queue as $terrain) {
            if ($territories[$terrain]["faction"] === $faction) {
                // Territory belongs to the faction, stop looking
                return $steps;
            }
            // collect list of unvisited neighbours of this terrain
            foreach($territories[$terrain]["adjacent"] as $adj) {
                if (!isset($visited[$adj])) { // not yet visited
                    $next[] = $adj;
                    $visited[$adj] = 1;
                }
            }
        }
        $queue = $next;
        $steps++;
    }
    // Should never get here: it would mean territories are disconnected
}

// Load the world map: 
$territories = getProximityData($connexion);

// Example use:
$steps = getProximity($territories, 5, 4); // distance of faction 5 to territory 4.

请注意,对于给定派系(第二个参数)拥有的领土(最后一个参数),将返回 0。对于与派系相邻的领土,您将获得 1,...等等。

【讨论】:

  • 我肯定需要一些时间来深入研究这段代码,但您先生是个英雄。这 100% 有效。至于我自己,我保证我不会只是盲目地复制/粘贴并花时间理解所有这些:)
猜你喜欢
  • 2014-03-05
  • 2012-06-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-08-28
  • 2021-11-11
  • 1970-01-01
相关资源
最近更新 更多