全排列,逆序数与行列式的二三事
行列式计算
在线性代数中,我们接触到了行列式的定义及相关计算,现在我们可以用C语言来帮助我们实现行列式的计算
一起来把这个顽固的行列式算出来(╯‵□′)╯︵┻━┻
行列式计算方式
首先先来回顾一下行列式的计算方式
行列式由定义可知行数和列数相同。对于一个n阶行列式,其行数和列数都是n
a i j 代表行列式中第 i 行第 j 列的元素
行列式的计算公式
其中 p1p2 ··· pn 为自然数 1, 2, ··· , n 的一个排列,t 为这个排列的逆序数
该公式代表对p1p2 ··· pn所有排列对应的若干项求和,排列数即为项数
那么我们可以将 p1p2 ··· pn 看作n个数在n个数位上进行全排列,然后再执行相应的计算
也就是说,行列式计算需要三步
全排列
在之前的排列专题文章中,已介绍了比较法,记录法,交换法三种排列方法,故这里不再多介绍。
由于行列式行数列数相等,所以排列是全排列,且只是计算就不用考虑按序输出排列,因而推荐使用较快的交换法。
逆序数
- 比较法
比较法顾名思义就是按照定义,依次将某数前面的数和它比较大小
#include<stdio.h>
void compare(int digit[],int n);int cnt=0;//用于给逆序数计数 int main()
{int n;printf("数的个数:"); scanf("%d",&n);int digit[n];printf("数:"); for(int i=0;i<n;i++) scanf("%d",&digit[i]);compare(digit,n);printf("逆序数:%d",cnt);
} void compare(int digit[],int n)
{for(int i=0;i<n;i++)//i是当前的数的位置 {for(int j=0;j<i;j++)//j是当前的数前面的数的位置 {if(digit[j]>digit[i]) cnt++;}}
}
运行效果图
- 归并法
归并法利用到了归并排序进行逆序数计算。在之前排序专题的推文中我们已介绍了归并排序。
在这里我们简单回顾一下归并排序的思想。根据分治法的思想,归并排序就是“先分再合”。
先将一个数组无限地一分为二直至不能再分。然后在合并数组的过程中,将每次分割的两个子数组中的元素进行比较,根据顺序依次将元素放到新数组中。重复此步骤直至合并成原来数组的长度。
那么在合并数组过程中的数组元素比较就可以顺便计算逆序数。
#include<iostream>
using namespace std;void mergesort(int temp_digit[],int L,int R);int cnt=0;//用于给逆序数计数 int main()
{int n;cout<<"排序的数的个数:";cin>>n;int temp_digit[n];cout<<"准备排序的数:";for(int i=0;i<n;i++) cin>>temp_digit[i];mergesort(temp_digit,0,n-1);cout<<"经排序过的数:";for(int i=0;i<n;i++) cout<<temp_digit[i]<<" ";cout<<endl<<"逆序数:"<<cnt;
} void mergesort(int temp_digit[],int L,int R)//L,R分别为当前数组的首尾位置
{if(L==R) return;//当数组只剩一个数(无法再分割时),返回 int middle=(L+R)/2;//将当前数组一分为二mergesort(temp_digit,L,middle);//对子数组同样一分为二,直到数组无法再分割 mergesort(temp_digit,middle+1,R);int temp[R-L+1];//临时数组,用于暂时存放排好序的数//达到排序并合并两子数组的效果 int pos1=L,pos2=middle+1,pos3=0;//pos1是在子数组1中的当前位置//pos2是在子数组2中的当前位置//pos3是在临时数组中的当前位置 while(pos1<=middle&&pos2<=R){//两子数组中的数进行比较,较小的数放在临时数组前面 if(temp_digit[pos1]<=temp_digit[pos2]) {temp[pos3++]=temp_digit[pos1++];}else{temp[pos3++]=temp_digit[pos2++];cnt+=middle-pos1+1;//当子数组2当前位置的数比子数组1当前位置的数小时(不能有等于)//子数组1中剩余的数的个数加入逆序数计数中 }} while(pos1<=middle)//将比较后子数组1剩余的数放进临时数组 {temp[pos3++]=temp_digit[pos1++];} while(pos2<=R)//将比较后子数组2剩余的数放进临时数组 {temp[pos3++]=temp_digit[pos2++];}for(int i=0;i<pos3;i++)//将临时数组排好序的数放回原数组{temp_digit[L+i]=temp[i];}
}
运行效果图
公式计算
结合全排列,逆序数,公式即可进行行列式计算
下面的代码以交换法实现全排列和归并法实现逆序数计算为例,这样当行列式阶数n较大时,计算速度相对较快
#include<stdio.h>void swap(int *,int*);
void arrange(int [],int [],int [],int,int);
void mergesort(int [],int,int);int cnt=0;//cnt用于逆序数计数
int term_cnt=0;//term_cnt用于项数计数
int ans=0;//ans为行列式计算结果int main()
{int n;printf("行列式阶数:");scanf("%d",&n);int digit[n];int temp_digit[n];//temp_digit是digit的临时副本,用于给归并法排序来算逆序数 int det[n*n];//为方便将数组传入函数,用一维数组det存储行列式元素 //行列式中i行j列元素为数组det中第i*n+j个元素 printf("\n行列式:\n");for(int i=0;i<n;i++){for(int j=0;j<n;j++){scanf("%d",&det[i*n+j]);}}for(int i=1;i<=n;i++){digit[i-1]=i;}printf("\n行列式计算式的项:\n");arrange(det,digit,temp_digit,n,0);printf("\n行列式计算式项数:%d\n\n",term_cnt);printf("行列式计算结果为:%d",ans);return 0;
}void swap(int *x,int *y)//交换
{int temp;temp = *x;*x = *y;*y = temp;
}void arrange(int det[],int digit[],int temp_digit[],int n,int pos)
{//arrange是用交换法对a1p1 a2p2...anpn(n和pn都是下标)中的p1p2...pn进行全排列 if(pos==n-1)//到最后一位时,开始套公式计算行列式 {int term=1;//term重置为1 for(int i=0;i<n;i++)//此处循环中的i+1是aij的i,digit[i]是j(在数组中下标都减一){term*=det[i*n+digit[i]-1];//term是当前的项 }//为了不改变原排列的顺序,需要用一个临时数组作为原排列的副本 for(int i=0;i<n;i++)temp_digit[i]=digit[i];cnt=0;//cnt重置为0 //将临时数组传入归并法,用于临时排序的同时计算逆序数 mergesort(temp_digit,0,n-1);term_cnt++;//项数+1 if(cnt%2==1)//若逆序数为奇数,则当前项前有负号 {printf(" -");ans=ans-term;}else if(cnt%2==0)//若逆序数为偶数,则当前项前有正号 {printf(" +");ans=ans+term;}for(int i=0;i<n;i++) {//用a(i,j)表示行列式中第i行第j列的元素//将每一项(包括前面的正负符号)输出,展现计算过程 printf(" a(%d,%d) ",i+1,digit[i]);if(i!=n-1) printf("*");}printf("\n");}else{for(int i=pos;i<n;i++)//i是当前位pos后面的位 {swap(digit+i,digit+pos);//digit[i]和digit[pos]交换 arrange(det,digit,temp_digit,n,pos+1);//进入到下一位的排列 swap(digit+i,digit+pos);//还原交换的两数 }}
}void mergesort(int temp_digit[],int L,int R)//L,R分别为当前数组的首尾位置
{if(L==R) return;//当数组只剩一个数(无法再分割时),返回 int middle=(L+R)/2;//将当前数组一分为二mergesort(temp_digit,L,middle);//对子数组同样一分为二,直到数组无法再分割 mergesort(temp_digit,middle+1,R);int temp[R-L+1];//临时数组,用于暂时存放排好序的数//达到排序并合并两子数组的效果 int pos1=L,pos2=middle+1,pos3=0;//pos1是在子数组1中的当前位置//pos2是在子数组2中的当前位置//pos3是在临时数组中的当前位置 while(pos1<=middle&&pos2<=R){//两子数组中的数进行比较,较小的数放在临时数组前面 if(temp_digit[pos1]<=temp_digit[pos2]) {temp[pos3++]=temp_digit[pos1++];}else{temp[pos3++]=temp_digit[pos2++];cnt+=middle-pos1+1;//当子数组2当前位置的数比子数组1当前位置的数小时(不能有等于)//逆序数计数加上子数组1中剩余的数的个数}} while(pos1<=middle)//将比较后子数组1剩余的数放进临时数组 {temp[pos3++]=temp_digit[pos1++];} while(pos2<=R)//将比较后子数组2剩余的数放进临时数组 {temp[pos3++]=temp_digit[pos2++];}for(int i=0;i<pos3;i++)//将临时数组排好序的数放回原数组 {temp_digit[L+i]=temp[i];}
}
运行效果图
以a(i , j)表示行列式中第 i 行第 j 列元素
全排列,逆序数与行列式的二三事相关推荐
- 全排列与逆序数的组合
之前也做过这类题,现在做又忘了,整理一下. ACM中遇到逆序数最多的就是 全排列+逆序数 引入 这样一题: 给出n个数字,求使用冒泡排序所需要交换的次数(n到5e5)(POJ2299) 问题可以转化为 ...
- 互换矩阵任意两行(列),行列式变号——证明:互换排列中任意两个数,排列逆序数的奇偶性改变
关于排列的一个简单结论证明---互换排列中任意两个数,排列的逆序数奇偶性改变 一. 引言:在大学线性代数中,有关于行列式的一个常见的定理:*互换任意两行(列),行列式变号.*由行列式完全展开式可以很明 ...
- 二叉树:二叉搜索树实现 逆序数问题
关于逆序数的问题描述如下: 已知数组nums,求新数组count,count[i]代表了在nums[i]右侧且比 nums[i]小的元素个数. 例如: nums = [5, 2, 6, 1], cou ...
- 001 线性代数之行列式:定义、逆序数、余子式与代数余子式、n个易算行列式、范德蒙行列式
001 线性代数之行列式:定义.逆序数.余子式与代数余子式.n个易算行列式.范德蒙行列式
- 八十二、归并排序求取复杂的逆序数
@Author:Runsen 逆序数,我在很多的面试题都见过,本质上来说难度是比较大,因为如果使用暴力法当数据量一大,必然就会爆掉.你现在就要记住逆序数就是考归并排序. 逆序数 给定一个数组array ...
- 归并算法经典应用——求解逆序数
本文始发于个人公众号:TechFlow,原创不易,求个关注 在之前介绍线性代数行列式计算公式的时候,我们曾经介绍过逆序数:我们在列举出行列式的每一项之后,需要通过逆序数来确定这一项符号的正负性.如果有 ...
- XTU 2021计网期中测试-冰冰的逆序数
链接:登录-专业IT笔试面试备考平台_牛客网 来源:牛客网 题目描述 冰冰是木木的妹妹,她读二年级,她的老师出了一道逆序数的题. 逆序数定义如下: 给定一个数列{a1,a2,a3...an}, ...
- 逆序数问题(归并排序,C++)
在求解八数码问题时,因为要进行逆序数的计算判断两个结点的可达性,同奇偶的逆序数才能可达.如果只是八数,暴力解法还好,当数字多了之后如何知道逆序数呢. 题目描述 通过计算八数码节点的逆序数判断.如果一对 ...
- hdu 5273 Dylans loves sequence 逆序数 区间dp
点击打开链接 题意:给n个数,q次询问,(L,R)区间内的逆序数. 思路: 区间dp 代码一: 1 #include <bits/stdc++.h> 2 using namespace s ...
最新文章
- gevent.hub.LoopExit: ('This operation would block forever'
- 利用霍夫变换做直线检测的原理及OpenCV代码实现
- 远程linux的几个工具
- SAP关于销售来自可选工厂的解决方案
- APT***的那些事
- python traceback什么意思_浅谈Python traceback的优雅处理
- 查询Linux系统网卡流量负载,超好用linux系统查询网卡等流量参数的工具,分享给大家!!!...
- 经典SQL[私家珍藏]
- 网易游戏开发工程师笔试题
- Linux之YUM方式安装SVN
- MATLAB显示图像变白问题
- linux设备驱动的实现与理解
- 相较于本地渲染,云渲染用起来感觉怎么样?
- session钝化活化
- AI 可以从人类思维中学习的11个启示
- 搭建自己的github.io博客
- 实景三维可视化管理平台助力提升景区运营管理水平
- Jmeter对Web Socket进行压力测试 —— 200人直播课实战经验
- python实现守护进程_Python如何实现守护进程的方法示例
- 告诫程序员们,大三/大四有必要去实习吗?
热门文章
- Zynq-7000系列之linux开发学习笔记:编译Linux内核和制作设备树(六)
- 数字图像处理——图像采集和预处理
- 2017年哈工大数理逻辑期末考试参考答案
- Arduino编译Marlin1.1出现 fatal error: U8glib.h: No such file or directory如何解决
- /lib64/libstdc++.so.6: version `CXXABI_1.3.8’ not found
- c语言莹源码,exp5/wc/client.c · 张雪莹20175227/ISSDF - Gitee.com
- 30天自制操作系统 Day3
- 熬夜加班赚钱?放弃吧,你的基因里有一个大写的穷。
- Google的云计算
- HCIA-第八节0615