三对角矩阵原理及C++实现

一、三对角矩阵

1.三对角矩阵概念

在这里插入图片描述

2.三对角矩阵元素数量

对于给定n阶方阵M,若其为三对角矩阵,则元素个数N为:

  • n=1,此时方阵只有一个元素M[0][0],由定义知该元素也在三对角线上。故N=1
  • n>1,由三对角矩阵特点知,矩阵的第一行和最后一行(第n行)分别有两个非零元素,其余行每行各有三个非零元素。故N = 3*(n-2)+2*2 = 3n-2

综上,
N = { 1 , n=1 3 n − 2 , n>1 N=\left\{ \begin{aligned} & 1 ,&\text{n=1}\\ & 3n-2, &\text{n>1}\\ \end{aligned} \right. N={1,3n2,n=1n>1

3.三对角矩阵下标变换推导(行优先和列优先)

对于给定n阶三对角方阵M和存放该方阵的数组A,非零元素M_ij1≤i,j≤n|i-j|≤1)同数组元素A[k]0≤k<N)存在一一对应的关系。下面分别推导从下标i,jkki,j的关系。

3.1行优先

  1. M_ijA[k]:即已知i,j要找出对应的k,需要知道,在数组A中,元素M_ij之前存放的元素个数num,则M_ij存放在A的第num+1个位置,对应数组A的下标正好为num(假设数组下标从0开始)。
  • 先考虑i>1的情况,即寻找第一行以下的非零元素M_ij
    • 第一行有2个非零元素;之后的2~i-1行,每行有三个非零元素。

    • i行,在M_ij之前的非零元素有j-(i-1)个。

k = num = 2+3*(i-1-1)+j-(i-1)=2i+j-3

  • 而当i=1时,有j=1或2。且A[0]=M_11,A[1]=M_12。代入发现也符合上述公式。
    在这里插入图片描述
  1. A[k]M_ij

首先求出A[k]位于M的第几行:由于M的第一行有两个元素,2~i-1行有三个元素,由此可以得到i关于k的表达式:i = ⌊(k+1)/3+1⌋(下取整)。(i+1为第一行“补”一个元素,加一是因为i从1开始)。
然后可以根据上面得到的ki,j的关系得到:j = k-2i+3

3.列优先

由于三对角矩阵具有良好的对称性,所以只需要对行优先推导得到的关系中i,j互换即可。
即:

  • k=2j+i-3
  • j=⌊(k+1)/3+1⌋(下取整)i=k-2j+3

二、C++实现三对角矩阵代码

//diagonalMatrix.hpp
#pragma once
#include "assert.h"
#include <iostream>
template <typename E>
// 检查下标越界
#define CHECK(i, j) \
    assert(i >= 1 && j >= 1 && i <= m_dimension && j <= m_dimension);

// 检查是否是对角阵中的非零元素
#define IS_ZERO(i, j) abs(i - j) > 1

// 根据行/列优先原则获取数组对应下标
#define GET_IDX(i, j) m_priority ? (2 * i + j - 3) : (2 * j + i - 3)

class DiagonalMatrix
{
private:
    int m_dimension; // 方阵阶数
    int m_capacity;  // 数组容量,即方阵中非零元素数量
    E *m_array;      // 存储方阵的数组
    bool m_priority; // 0: 行优先 1:列优先
public:
    DiagonalMatrix(int dimension, bool priority = false) : m_dimension(dimension), m_priority(priority)
    {
        assert(dimension >= 1);
        if (dimension == 1)

            m_capacity = 1;

        else
            m_capacity = 3 * dimension - 2; // capacity = (d - 2)*3 + 2*2 = 3d -2
        m_array = new E[m_capacity];
    }
    ~DiagonalMatrix()
    {
        delete m_array;
        m_array = nullptr;
    }
    // 设置元素M_ij
    void set(const E &element, int i, int j)
    {
        CHECK(i, j);
        if (IS_ZERO(i, j))
            return;
        m_array[GET_IDX(i, j)] = element;
    }
    // 获取元素M_ij
    E get(int i, int j)
    {
        CHECK(i, j);
        if (IS_ZERO(i, j))
            return 0;
        return m_array[GET_IDX(i, j)];
    }

    // 获取数组元素A[k]对应方阵的下标i和j
    void getKIdx(int k, int &i, int &j)
    {
        assert(k >= 0 && k < m_capacity);
        if (!m_priority)
        {
            i = (k + 1) / 3 + 1;
            j = k - 2 * i + 3;
        }
        else
        {
            j = (k + 1) / 3 + 1;
            i = k - 2 * j + 3;
        }
    }
    // 打印方阵
    void printMatrix()
    {
        for (int i = 1; i <= m_dimension; i++)
        {
            for (int j = 1; j <= m_dimension; j++)
                std::cout << get(i, j) << ' ';
            std::cout << '\n';
        }
    }

    int getCapacity()
    {
        return m_capacity;
    }

    int getDimension()
    {
        return m_dimension;
    }
};

使用:

#include "diagonalMatrix.hpp"
int main()
{
    using namespace std;
    DiagonalMatrix<int> d(6);
    int matrix[6][6] =
        {
            {1, 1, 0, 0, 0, 0},
            {1, 1, 1, 0, 0, 0},
            {0, 1, 1, 1, 0, 0},
            {0, 0, 1, 1, 1, 0},
            {0, 0, 0, 1, 1, 1},
            {0, 0, 0, 0, 1, 1}};

    // set value
    int i, j, k;
    for (i = 0; i < 6; i++)
        for (j = 0; j < 6; j++)
            if (matrix[i][j] != 0)
                d.set(matrix[i][j], i + 1, j + 1);

    // get idx and value
    for (k = 0; k < d.getCapacity(); k++)
    {
        d.getKIdx(k, i, j);
        printf("k = %d , i = %d, j = %d , matrix[%d][%d] = %d\n", k, i, j, i, j, d.get(i, j));
    }
    d.printMatrix();
}

结果:

k = 0 , i = 1, j = 1 , matrix[1][1] = 1
k = 1 , i = 1, j = 2 , matrix[1][2] = 1
k = 2 , i = 2, j = 1 , matrix[2][1] = 1
k = 3 , i = 2, j = 2 , matrix[2][2] = 1
k = 4 , i = 2, j = 3 , matrix[2][3] = 1
k = 5 , i = 3, j = 2 , matrix[3][2] = 1
k = 6 , i = 3, j = 3 , matrix[3][3] = 1
k = 7 , i = 3, j = 4 , matrix[3][4] = 1
k = 8 , i = 4, j = 3 , matrix[4][3] = 1
k = 9 , i = 4, j = 4 , matrix[4][4] = 1
k = 10 , i = 4, j = 5 , matrix[4][5] = 1
k = 11 , i = 5, j = 4 , matrix[5][4] = 1
k = 12 , i = 5, j = 5 , matrix[5][5] = 1
k = 13 , i = 5, j = 6 , matrix[5][6] = 1
k = 14 , i = 6, j = 5 , matrix[6][5] = 1
k = 15 , i = 6, j = 6 , matrix[6][6] = 1
1 1 0 0 0 0
1 1 1 0 0 0
0 1 1 1 0 0
0 0 1 1 1 0
0 0 0 1 1 1
0 0 0 0 1 1
  • 0
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
多对角矩阵是一种特殊的稀疏矩阵,只有主对角线及其上下若干条对角线上有非零元素,其余元素均为零。因此,可以采用压缩存储方式来节省存储空间。常用的压缩存储方式有三对角矩阵、对称三对角矩阵和块对角矩阵等。 对于多对角矩阵,我们可以采用对角线压缩存储方式。具体来说,就是将矩阵中的每条对角线都保存在一个一维数组中,然后将这些数组按照对角线的位置排列起来,形成一个二维的压缩矩阵。这样,对于一个 $n\times n$ 的多对角矩阵,压缩后的存储空间为 $O(n)$。 下面是一个 C++ 实现多对角矩阵压缩存储的示例代码: ```c++ #include <iostream> #include <vector> using namespace std; class DiagonalMatrix { private: vector<int> diagonal; // 对角线数组 int n; // 矩阵维度 public: DiagonalMatrix(int n) { this->n = n; diagonal.resize(2 * n - 1, 0); // 初始化对角线数组大小 } void set(int i, int j, int value) { if (i < 1 || i > n || j < 1 || j > n) { cout << "Invalid index!" << endl; return; } if (i == j) { diagonal[n - 1 + i - 1] = value; // 主对角线 } else if (i - j == 1) { diagonal[i - 2] = value; // 上一条对角线 } else if (j - i == 1) { diagonal[n + j - 2] = value; // 下一条对角线 } else { cout << "Invalid index!" << endl; return; } } int get(int i, int j) { if (i < 1 || i > n || j < 1 || j > n) { cout << "Invalid index!" << endl; return 0; } if (i == j) { return diagonal[n - 1 + i - 1]; // 主对角线 } else if (i - j == 1) { return diagonal[i - 2]; // 上一条对角线 } else if (j - i == 1) { return diagonal[n + j - 2]; // 下一条对角线 } else { return 0; } } }; int main() { DiagonalMatrix mat(5); mat.set(1, 1, 1); mat.set(2, 2, 2); mat.set(3, 3, 3); mat.set(4, 4, 4); mat.set(5, 5, 5); mat.set(1, 2, 6); mat.set(2, 3, 7); mat.set(3, 4, 8); mat.set(4, 5, 9); cout << mat.get(1, 2) << endl; // output: 6 cout << mat.get(2, 3) << endl; // output: 7 cout << mat.get(3, 2) << endl; // output: 0 (非对角线元素) return 0; } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值