【问题标题】:How to store data of a symmetric matrix table?如何存储对称矩阵表的数据?
【发布时间】:2019-01-31 17:01:57
【问题描述】:

我会尽量解释我的问题。我需要以与矩阵表类似的方式存储一些数据,但需要使用代码。

我们以一个简单的表格为例:

我对此的第一个想法是创建一个 JS 对象来存储所有可能性,如下所示:

const Y = 'Yellow';
const R = 'Red';
const W = 'White';
const P = 'Pink';

const data = {
   Y: { Y: Y, R: Y, W: Y },
   R: { Y: Y, R: R, W: P }, 
   W: { Y: Y, R: P, W: W }
};

console.log(data['W']['R']); // 'Pink'

但考虑到我会有更多的可能性,这显然是不可维护的。

我也可以像这样减小对象的大小,但我又担心它很难维护:

const data = {
   Y: { Y: Y, R: Y, W: Y },
   R: { R: R, W: P }, 
   W: { W: W }
};

问题听起来很简单,但我找不到另一种存储此类数据的方法。有没有更好的方法来做到这一点?


另外,我想补充一点,我在这里使用了 JS,因为我更熟悉这种语言,但我必须将它与 PHP 一起使用,以防万一它有很大的不同。

【问题讨论】:

  • 二维数组可以工作吗? 代码是什么意思?
  • 只是我的两分钱 - 我不明白为什么它不可维护或者你担心它的大小。除非您正在处理存储空间极其有限的东西,否则我会选择您最清楚的东西。这似乎很清楚 - 就像从电子表格中提取单元格一样。
  • 如何访问数据?从外面看,比如data['W']['R']?为什么它是一个不可维护的结构?您需要节省空间还是需要更快的访问速度(作为对象或地图)?
  • 顺序无所谓,就是data['W']['R'] === data['R']['W']。你可能有一些理由需要一个矩阵。我会把这个放在地图上。只值我的 2 美分
  • @dgig @Nina Scholz 老实说,我认为我做错了,并且已经存在存储此类数据的东西。我有点担心可维护性,因为我还不知道这张表中有多少数据。我不一定需要它很快,也不需要空间,尽管我必须保持灵活。是的,我会得到类似于data['W']['R']@D 的结果。 Seah 我不知道我会按什么顺序收到这两个字母,但也许按字母顺序组织我的数据可能会有所帮助。谢谢大家的帮助。

标签: javascript matrix


【解决方案1】:

最紧凑的表示形式是单个数组中的三角矩阵,除非您真的空间不足,否则我不建议这样做。

function triMatrix(n) { // not really needed
    return new Array(n * (n + 1) / 2);
}

function trindex(row, col) {
    if (col > row) {
        var tmp = row; row = col; col = tmp;
    }
    return row * (row + 1) / 2 + col;
}

function triStore(tri, indexOfKey, rowKey, colKey, value) {
    tri[trindex(indexOfKey[rowKey], indexOfKey[colKey])] = value;
}

function triGet(tri, indexOfKey, rowKey, colKey) {
    return tri[trindex(indexOfKey[rowKey], indexOfKey[colKey])];
}

const keyOfIndex = ['Y', 'R', 'W'];
const indexOfKey = {'Y': 0, 'R': 1, 'W': 2}; // can be calculated
const N = keyOfIndex.length;
var tri = triMatrix(N); // could also be var tri = [];
triStore(tri, indexOfKey, 'Y', 'Y', 'Y');
triStore(tri, indexOfKey, 'Y', 'R', 'Y');
triStore(tri, indexOfKey, 'Y', 'W', 'Y');
triStore(tri, indexOfKey, 'R', 'R', 'R');
triStore(tri, indexOfKey, 'R', 'W', 'P');
triStore(tri, indexOfKey, 'W', 'W', 'W');
tri; // => [ "Y", "Y", "R", "Y", "P", "W" ]
triGet(tri, indexOfKey, 'R', 'W'); // => "P"
triGet(tri, indexOfKey, 'W', 'R'); // => "P"

关键是:你的矩阵是对称的,所以你只需要它的上三角矩阵或下三角矩阵(包括对角线)。在您的建议中,您存储上三角矩阵,在我的建议中,我存储下三角矩阵,因为索引计算要简单得多。该数组将包含 1 个元素的第 1 行、2 个中的第 2 行、3 个中的第 3 行等。只要记住 1+2+...+n=n(n+1)/2,您就会明白如何计算数组索引。

M₀₀ M₀₁ M₀₂      M₀₀              T₀
M₁₀ M₁₁ M₁₂  =>  M₁₀ M₁₁      =>  T₁  T₂      =>  T₀ T₁ T₂ T₃ T₄ T₅
M₂₀ M₂₁ M₂₂      M₂₀ M₂₁ M₂₂      T₃  T₄  T₅

矩阵很容易扩展 1 行/列,无需重新索引数组:

M₀₀ M₀₁ M₀₂ M₀₃      M₀₀                  T₀
M₁₀ M₁₁ M₁₂ M₁₃      M₁₀ M₁₁              T₁  T₂
M₂₀ M₂₁ M₂₂ M₂₃  =>  M₂₀ M₂₁ M₂₂      =>  T₃  T₄  T₅      => T₀ ... T₆ T₇ T₈ T₉
M₃₀ M₃₁ M₃₂ M₃₃      M₃₀ M₃₁ M₃₂ M₃₃      T₆  T₇  T₈  T₉



作为练习,我将以上内容大致翻译为 PHP,这是您的目标语言。尽管我一开始不推荐“单个数组中的三角矩阵”方法,但如果它符合您的需求(如果不符合,也许我可以提供帮助),欢迎您将以下类用作黑盒。

class SymmetricMatrix
{
    private $n = 0;
    private $triangular = [];
    private $key_of_index = [];
    private $index_of_key = [];

    private function add_key_if_necessary($key) {
        if ( !isset($this->index_of_key[$key])) {
            $index = $this->n++;
            $this->index_of_key[$key] = $index;
            $this->key_of_index[$index] = $key;
            for ($i = 0; $i < $this->n; $i++) {
                $this->triangular[] = false; // avoid "jumping" index & init to "absent"
            }
        }
    }

    private static function trindex($row, $col) {
        if ($col > $row) {
            $tmp = $row; $row = $col; $col = $tmp;
        }
        return $row * ($row + 1) / 2 + $col;
    }

    public function put($key1, $key2, $value) {
        $this->add_key_if_necessary($key1);
        $this->add_key_if_necessary($key2);
        $trindex = self::trindex($this->index_of_key[$key1], $this->index_of_key[$key2]);
        $this->triangular[$trindex] = $value;
    }

    public function get($key1, $key2) {
        if (!isset($this->index_of_key[$key1]) || !isset($this->index_of_key[$key2])) {
            return false;
        }
        $trindex = self::trindex($this->index_of_key[$key1], $this->index_of_key[$key2]);
        return $this->triangular[$trindex];
    }

    public function find_first($value) { // $value !== false
        for ($row = 0; $row < $this->n; $row++) {
            for ($col = 0; $col <= $row; $col++) {
                $trindex = trindex($row, $col);
                if ($this->triangular[$trindex] === $value) {
                    return [$this->key_of_index[$row], $this->key_of_index[$col]];
                }
            }
        }
        return false;
    }

    public function get_keys() {
        return $this->key_of_index;
    }

    public function dump() {
        var_export($this);
        echo "\n";
    }
}

$m = new SymmetricMatrix();
$m->put('Y', 'Y', 'Y');
$m->put('Y', 'R', 'Y');
$m->put('Y', 'W', 'Y');
$m->put('R', 'R', 'R');
$m->put('R', 'W', 'P');
$m->put('W', 'W', 'W');
$m->dump();
echo "keys: ", implode(', ', $m->get_keys()), "\n";
echo "m[R][W]: ", $m->get('R', 'W'), "\n";
echo "m[W][R]: ", $m->get('W', 'R'), "\n";

【讨论】:

  • 这些代码看起来很有趣!我不确定你为什么决定将triindexOfKey 作为triStoretriGet 的参数而不是直接使用变量?我还没有时间详细检查所有内容,但我很确定您的 JS 和 PHP 代码都会对我有所帮助。谢谢!
  • 哦,那只是为了保持函数的通用性和可重用性——只是一个有用的习惯
猜你喜欢
  • 2011-03-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-08-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-02-02
相关资源
最近更新 更多