阅读这篇文章需要掌握C++类的知识以及线性代数的知识,如果有疑问,可在文章下方评论,作者会尽快回复;本文是在作者阅读了平冈和幸的程序员的数学3:线性代数之后而写,在代码设计上借鉴了书中的方法。
希望这些代码能够帮助你更好地理解线性代数里提到的矩阵运算,笔者所写的矩阵运算代码,都是初学C++时实现的,并不具有工程应用的价值;真诚的希望读者能够使用更好的矩阵运算库,比如Eigen,OpenCV等,笔者对这两个C++库的理解也是比较深入的;当然,如果读者还了解Python的话,笔者建议学习numpy,numpy的Cpp代码可以在Github上搜索NumCpp。
初写这份代码时,笔者才刚入C++,如今笔者将在github上上传新的矩阵运算代码NANAhttps://github.com/YuruTu/NANA
相关文档见地址NANA doxygen生成文档https://yurutu.github.io/NANA/files.html
具体实现的矩阵功能有:
最初版代码及其使用
利用C++的类实现矩阵的运算,可实现矩阵的+-*运算,以及用高斯消去法求解线性方程组Ax=b
2018/10/13新增功能 矩阵的行列变换 高斯消元法得到上三角矩阵
2018/12/9实现矩阵的逆运算等
预编译头文件
pch.h
#ifndef PCH_H
#define PCH_H
#include <cmath>
#include <iostream>
#include <stdlib.h>
#include <cmath>
// TODO: 添加要在此处预编译的标头
#endif //PCH_H
头文件.h
/*
author: cclplus
date:2018/12/09
if you think it is necessary to reward me,
my alipay account number is [email protected]
*/
#ifndef __MATRIX_CLL_H__
#define __MATRIX_CCL_H__
#include "pch.h"
class Matrix {
private:
int rows_num, cols_num;
double **p;
void initialize();//初始化矩阵
public:
Matrix(int, int);
Matrix(int, int, double);//预配分空间
virtual ~Matrix();//析构函数应当是虚函数,除非此类不用做基类
Matrix& operator=(const Matrix&);//矩阵的复制
Matrix& operator=(double *);//将数组的值传给矩阵
Matrix& operator+=(const Matrix&);//矩阵的+=操作
Matrix& operator-=(const Matrix&);//-=
Matrix& operator*=(const Matrix&);//*=
Matrix operator*(const Matrix & m)const;
static Matrix Solve(const Matrix&, const Matrix&);//求解线性方程组Ax=b
void Show() const;//矩阵显示
void swapRows(int, int);
double det();//求矩阵的行列式
double Point(int i, int j) const;
static Matrix inv(Matrix);//求矩阵的逆矩阵
static Matrix eye(int );//制造一个单位矩阵
int row() const;
int col() const;
static Matrix T(const Matrix & m);//矩阵转置的实现,且不改变矩阵
Matrix gaussianEliminate();//高斯消元法
friend std::istream& operator>>(std::istream&, Matrix&);//实现矩阵的输入
};
#endif
头文件.cpp
/*
author: cclplus
date : 2018 / 12 / 09
if you think it is necessary to reward me,
my alipay account number is [email protected]
*/
#include "pch.h"
#include "matrix.h"
using std::endl;
using std::cout;
using std::istream;
const double EPS = 1e-10;
void Matrix::initialize() {//初始化矩阵大小
p = new double*[rows_num];//分配rows_num个指针
for (int i = 0; i < rows_num; ++i) {
p[i] = new double[cols_num];//为p[i]进行动态内存分配,大小为cols
}
}
//声明一个全0矩阵
Matrix::Matrix(int rows, int cols)
{
rows_num = rows;
cols_num = cols;
initialize();
for (int i = 0; i < rows_num; i++) {
for (int j = 0; j < cols_num; j++) {
p[i][j] = 0;
}
}
}
//声明一个值全部为value的矩阵
Matrix::Matrix(int rows, int cols, double value)
{
rows_num = rows;
cols_num = cols;
initialize();
for (int i = 0; i < rows_num; i++) {
for (int j = 0; j < cols_num; j++) {
p[i][j] = value;
}
}
}
//析构函数
Matrix::~Matrix() {
for (int i = 0; i < rows_num; ++i) {
delete[] p[i];
}
delete[] p;
}
//实现矩阵的复制
Matrix& Matrix::operator=(const Matrix& m)
{
if (this == &m) {
return *this;
}
if (rows_num != m.rows_num || cols_num != m.cols_num) {
for (int i = 0; i < rows_num; ++i) {
delete[] p[i];
}
delete[] p;
rows_num = m.rows_num;
cols_num = m.cols_num;
initialize();
}
for (int i = 0; i < rows_num; i++) {
for (int j = 0; j < cols_num; j++) {
p[i][j] = m.p[i][j];
}
}
return *this;
}
//将数组的值传递给矩阵(要求矩阵的大小已经被声明过了)
Matrix& Matrix::operator=(double *a){
for(int i=0;i<rows_num;i++){
for(int j=0;j<cols_num;j++){
p[i][j]= *(a+i*cols_num+j);
}
}
return *this;
}
//+=操作
Matrix& Matrix::operator+=(const Matrix& m)
{
for (int i = 0; i < rows_num; i++) {
for (int j = 0; j < cols_num; j++) {
p[i][j] += m.p[i][j];
}
}
return *this;
}
//实现-=
Matrix& Matrix::operator-=(const Matrix& m)
{
for (int i = 0; i < rows_num; i++) {
for (int j = 0; j < cols_num; j++) {
p[i][j] -= m.p[i][j];
}
}
return *this;
}
//实现*=
Matrix& Matrix::operator*=(const Matrix& m)
{
Matrix temp(rows_num, m.cols_num);//若C=AB,则矩阵C的行数等于矩阵A的行数,C的列数等于B的列数。
for (int i = 0; i < temp.rows_num; i++) {
for (int j = 0; j < temp.cols_num; j++) {
for (int k = 0; k < cols_num; k++) {
temp.p[i][j] += (p[i][k] * m.p[k][j]);
}
}
}
*this = temp;
return *this;
}
//实现矩阵的乘法
Matrix Matrix::operator*(const Matrix & m)const{
Matrix ba_M(rows_num,m.cols_num,0.0);
for(int i=0;i<rows_num;i++){
for(int j=0;j<m.cols_num;j++){
for(int k=0;k<cols_num;k++){
ba_M.p[i][j]+=(p[i][k]*m.p[k][j]);
}
}
}
return ba_M;
}
//解方程Ax=b
Matrix Matrix::Solve(const Matrix &A, const Matrix &b)
{
//高斯消去法实现Ax=b的方程求解
for (int i = 0; i < A.rows_num; i++) {
if (A.p[i][i] == 0) {
cout << "请重新输入" << endl;
}
for (int j = i + 1; j < A.rows_num; j++) {
for (int k = i + 1; k < A.cols_num; k++) {
A.p[j][k] -= A.p[i][k] * (A.p[j][i] / A.p[i][i]);
if (abs(A.p[j][k]) < EPS)
A.p[j][k] = 0;
}
b.p[j][0] -= b.p[i][0] * (A.p[j][i] / A.p[i][i]);
if (abs(A.p[j][0]) < EPS)
A.p[j][0] = 0;
A.p[j][i] = 0;
}
}
// 反向代换
Matrix x(b.rows_num, 1);
x.p[x.rows_num - 1][0] = b.p[x.rows_num - 1][0] / A.p[x.rows_num - 1][x.rows_num - 1];
if (abs(x.p[x.rows_num - 1][0]) < EPS)
x.p[x.rows_num - 1][0] = 0;
for (int i = x.rows_num - 2; i >= 0; i--) {
double sum = 0;
for (int j = i + 1; j < x.rows_num; j++) {
sum += A.p[i][j] * x.p[j][0];
}
x.p[i][0] = (b.p[i][0] - sum) / A.p[i][i];
if (abs(x.p[i][0]) < EPS)
x.p[i][0] = 0;
}
return x;
}
//矩阵显示
void Matrix::Show() const {
//cout << rows_num <<" "<<cols_num<< endl;//显示矩阵的行数和列数
for (int i = 0; i < rows_num; i++) {
for (int j = 0; j < cols_num; j++) {
cout << p[i][j] << " ";
}
cout << endl;
}
cout << endl;
}
//实现行变换
void Matrix::swapRows(int a, int b)
{
a--;
b--;
double *temp = p[a];
p[a] = p[b];
p[b] = temp;
}
//计算矩阵行列式的值
double Matrix::det(){
//为计算行列式做一个备份
double ** back_up;
back_up=new double *[rows_num];
for(int i=0;i<rows_num;i++){
back_up[i]=new double[cols_num];
}
for(int i=0;i<rows_num;i++){
for(int j=0;j<cols_num;j++){
back_up[i][j]=p[i][j];
}
}
if(rows_num!=cols_num){
std::abort();//只有方阵才能计算行列式,否则调用中断强制停止程序
}
double ans=1;
for(int i=0;i<rows_num;i++){
//通过行变化的形式,使得矩阵对角线上的主元素不为0
if(abs(p[i][i])<=EPS){
bool flag=false;
for(int j=0;(j<cols_num)&&(!flag);j++){
//若矩阵的一个对角线上的元素接近于0且能够通过行变换使得矩阵对角线上的元素不为0
if((abs(p[i][j])>EPS)&&(abs(p[j][i])>EPS)){
flag=true;
//注:进行互换后,p[i][j]变为p[j][j],p[j][i]变为p[i][i]
//对矩阵进行行变换
double temp;
for(int k=0;k<cols_num;k++){
temp=p[i][k];
p[i][k]=p[j][k];
p[j][k]=temp;
}
}
}
if(flag)
return 0;
}
}
for(int i=0;i<rows_num;i++){
for(int j=i+1;j<rows_num;j++){
for(int k=i+1;k<cols_num;k++){
p[j][k]-=p[i][k]*(p[j][i]*p[i][i]);
}
}
}
for(int i=0;i<rows_num;i++){
ans*=p[i][i];
}
for(int i=0;i<rows_num;i++){
for(int j=0;j<cols_num;j++){
p[i][j]=back_up[i][j];
}
}
return ans;
}
//返回矩阵第i行第j列的数
double Matrix::Point(int i, int j) const{
return this->p[i][j];
}
//求矩阵的逆矩阵
Matrix Matrix::inv(Matrix A){
if(A.rows_num!=A.cols_num){
std::cout<<"只有方阵能求逆矩阵"<<std::endl;
std::abort();//只有方阵能求逆矩阵
}
double temp;
Matrix A_B=Matrix(A.rows_num,A.cols_num);
A_B=A;//为矩阵A做一个备份
Matrix B=eye(A.rows_num);
//将小于EPS的数全部置0
for (int i = 0; i < A.rows_num; i++) {
for (int j = 0; j < A.cols_num; j++) {
if (abs(A.p[i][j]) <= EPS) {
A.p[i][j] = 0;
}
}
}
//选择需要互换的两行选主元
for(int i=0;i<A.rows_num;i++){
if(abs(A.p[i][i])<=EPS){
bool flag=false;
for(int j=0;(j<A.rows_num)&&(!flag);j++){
if((abs(A.p[i][j])>EPS)&&(abs(A.p[j][i])>EPS)){
flag=true;
for(int k=0;k<A.cols_num;k++){
temp=A.p[i][k];
A.p[i][k]=A.p[j][k];
A.p[j][k]=temp;
temp=B.p[i][k];
B.p[i][k]=B.p[j][k];
B.p[j][k]=temp;
}
}
}
if(!flag){
std::cout<<"逆矩阵不存在\n";
std::abort();
}
}
}
//通过初等行变换将A变为上三角矩阵
double temp_rate;
for(int i=0;i<A.rows_num;i++){
for(int j=i+1;j<A.rows_num;j++){
temp_rate=A.p[j][i]/A.p[i][i];
for(int k=0;k<A.cols_num;k++){
A.p[j][k]-=A.p[i][k]*temp_rate;
B.p[j][k]-=B.p[i][k]*temp_rate;
}
A.p[j][i]=0;
}
}
//使对角元素均为1
for(int i=0;i<A.rows_num;i++){
temp=A.p[i][i];
for(int j=0;j<A.cols_num;j++){
A.p[i][j]/=temp;
B.p[i][j]/=temp;
}
}
//std::cout<<"算法可靠性检测,若可靠,输出上三角矩阵"<<std::endl;
//将已经变为上三角矩阵的A,变为单位矩阵
for(int i=A.rows_num-1;i>=1;i--){
for(int j=i-1;j>=0;j--){
temp=A.p[j][i];
for(int k=0;k<A.cols_num;k++){
A.p[j][k]-=A.p[i][k]*temp;
B.p[j][k]-=B.p[i][k]*temp;
}
}
}
std::cout<<"算法可靠性检测,若可靠,输出单位矩阵"<<std::endl;
for(int i=0;i<A.rows_num;i++){
for(int j=0;j<A.cols_num;j++){
printf("%7.4lf\t\t",A.p[i][j]);
}
cout << endl;
}
A=A_B;//还原A
return B;//返回该矩阵的逆矩阵
}
//制造一个单位矩阵
Matrix Matrix::eye(int n){
Matrix A(n,n);
for(int i=0;i<n;i++){
for(int j=0;j<n;j++){
if(i==j){
A.p[i][j]=1;
}else{
A.p[i][j]=0;
}
}
}
return A;
}
//读取矩阵行列数
int Matrix::row() const{
return rows_num;
}
int Matrix::col() const{
return cols_num;
}
//实现矩阵的转置
Matrix Matrix::T(const Matrix & m)
{ int col_size=m.col();
int row_size=m.row();
Matrix mt(col_size, row_size);
for (int i = 0; i <row_size; i++) {
for (int j = 0; j <col_size; j++) {
mt.p[j][i] = m.p[i][j];
}
}
return mt;
}
//高斯消元法
Matrix Matrix::gaussianEliminate()
{
Matrix Ab(*this);
int rows = Ab.rows_num;
int cols = Ab.cols_num;
int Acols = cols - 1;
int i = 0; //跟踪行
int j = 0; //跟踪列
while (i < rows)
{
bool flag = false;
while (j < Acols && !flag)
{
if (Ab.p[i][j] != 0) {
flag = true;
}
else {
int max_row = i;
double max_val = 0;
for (int k = i + 1; k < rows; ++k)
{
double cur_abs = Ab.p[k][j] >= 0 ? Ab.p[k][j] : -1 * Ab.p[k][j];
if (cur_abs > max_val)
{
max_row = k;
max_val = cur_abs;
}
}
if (max_row != i) {
Ab.swapRows(max_row, i);
flag = true;
}
else {
j++;
}
}
}
if (flag)
{
for (int t = i + 1; t < rows; t++) {
for (int s = j + 1; s < cols; s++) {
Ab.p[t][s] = Ab.p[t][s] - Ab.p[i][s] * (Ab.p[t][j] / Ab.p[i][j]);
if (abs(Ab.p[t][s]) <EPS)
Ab.p[t][s] = 0;
}
Ab.p[t][j] = 0;
}
}
i++;
j++;
}
return Ab;
}
//实现矩阵的输入
istream& operator>>(istream& is, Matrix& m)
{
for (int i = 0; i < m.rows_num; i++) {
for (int j = 0; j < m.cols_num; j++) {
is >> m.p[i][j];
}
}
return is;
}
主程序
#include "matrix.h"
using namespace std;
int main()
{
Matrix A = Matrix(3, 3);
cin >> A;
Matrix b = Matrix(3, 1);
cin >> b;
Matrix x = Matrix::Solve(A, b);
x.Show();
return 0;
}
求赞,求转发
文章浏览阅读1.9k次。formData1.创建一个formData对象var fd = new FormData(‘form表单’);(创建formdtata对象的小括号里面,就是需要一个form表单dom对象)。2.往fd对象中添加对象fd.append(‘sex’,‘男’);3.formData里面就会有form表单中 有name属性的这些标签的取值。//键值对形式console.log(fd.ge...
文章浏览阅读244次。文章来源:【公众号:云加社区】目录简介整体生态工作原理Metric 指标PromQLGrafana 可视化监控告警简介Prometheus 是一个开源的完整监控解决方案,本文将从指标抓取到查询及可视化展示,以及最后的监控告警,对 Prometheus 做一个基本的认识。Prometheus 是古希腊神话里泰坦族的一名神明,名字的意思是“先见之明”,下图中是 Promet..._dtm prometheus
文章浏览阅读900次,点赞15次,收藏6次。对数据序列{6,8,7,9,0,1,3,2,4,5},请分别用快速排序算法和直接选择排序设计算法对数据序列进行排序;
文章浏览阅读597次。/*找出10000以内的同构数同构数 376*376=141376思路:1、输入一个数num 先判断是几位数。记住数位length。 2、然后算它(num)的平方, square。 3、取square的后length位的数值temp 4、temp与num相等,则是同构数。*/#include <iostream>#include &..._小于10000的同构数
文章浏览阅读5.1k次,点赞3次,收藏26次。写了很久的语音呼叫功能、调用在线语音合成的调用系统自带的;现在客户又要求搞网页版的语音呼叫还是不带联网的。客户太难伺候了详细使用请参考 【web语音API】完整代码<!DOCTYPE><html xmlns="http://www.w3.org/1999/xhtml" lang="zh-CN"><head><title>网页文字转语音</title><meta http-equiv="Content-Typ._js tts
文章浏览阅读62次。System.out.println("100个和尚吃了100个馒头 ,100和尚有大和尚和小和尚,一个大和尚能吃3馒头,三个小和尚吃1个馒头,问大和尚和小和尚有多少个?System.out.println("大和尚有"+i+"个人");System.out.println("小和尚有"+j+"个人");System.out.println("查看答案请按回车键");
文章浏览阅读3.8w次,点赞9次,收藏10次。1. css3简介CSS 用于控制网页的样式和布局,CSS3 是最新的CSS标准,CSS3 完全向后兼容,因此您不必改变现有的设计。浏览器通常支持 CSS2,但是现在大部分浏览器也实现了css3的很多特性。CSS3 被划分为模块。其中最重要的 CSS3 模块包括:选择器框模型背景和边框文本效果2D/3D 转换动画多列布局用户界面2. css3边框2.1 边框圆角Internet Explorer 9+ 支持 border-radius 和 box-shadow 属性。Fir_采用css转换,边框阴影等新特性完成css3偏光图像画廊设计
文章浏览阅读47次。定义:允许将对象组成树形结构来表现 “整体/部分” 层次结构。组合能让客户以一致的方式处理个别对象及对象组合。说白了,就是类似于树形结构。 只是它要求子节点和父节点都具备统一的接口。类图如下:示例如下:比如我们常见的电脑上的目录,目录下面有文件夹,也有文件,然后文件夹里面还有文件及文件夹。这样一层层形成了树形结构。示例代码如下:#include <iostream>#include <stdio.h>#include "string"#includ..
文章浏览阅读1.9w次,点赞26次,收藏185次。目录一.请简述下什么是kotlin?它有什么特性?二.Kotlin 中注解 @JvmOverloads 的作用?三.Kotlin中的MutableList与List有什么区别?四.kotlin实现单例的几种方式?五. kotlin中关键字data的理解?相对于普通的类有哪些特点?六.什么是委托属性?简单说一下应用场景?七.kotlin中with、run、apply、let函数的区别?一般用于什么场景?八.kotlin中Unit的应用以及和Java中void的区别?九.Ko_kotlin面试题
文章浏览阅读2.8k次。有这个想法一方面是确实很多时候会记不得一些缩写是什么意思。另外也是受 http://blog.csdn.net/lin453701006/article/details/52797415这篇博客的启发,本文主要用于自己记忆 内容主要整理自http://blog.sina.com.cn/s/blog_520811730101hmj9.html http://blog.csdn.net/feix_反量化 英文缩写
文章浏览阅读7.3k次,点赞6次,收藏36次。超级简单的Python爬虫入门教程(非常详细),通俗易懂,看一遍就会了_爬虫python入门
文章浏览阅读1.2k次。您的代码存在一些问题。首先,您在此处显示的两个模型是not等效的:尽管您将scikit-learn LogisticRegression设置为fit_intercept=True(这是默认设置),但您并没有这样做statsmodels一;来自statsmodels docs:默认情况下不包括拦截器,用户应添加。参见statsmodels.tools.add_constant。另一个问题是,尽管您处..._sm fit(method