遇到一个问题,就是计算方块矩阵的行列式值。
在网上找了一些现成的,但是问题是太慢了,原因是这些现成的都是依据行列式值定义来进行计算的。
没办法自己按照书上的比较快速的计算方法写了个程序,如下。
基本原理就是变成上三角为0的来进行计算。
这里用到了double的原因是因为涉及到多次的乘除法消去一行,可能会有舍入误差,所以用double来尽量减少这个误差。
算法的来源见 https://books.google.com/books?id=1BxMryiz0_QC&pg=PA110&lpg=PA110&dq=%E8%A1%8C%E5%88%97%E5%BC%8F+%E8%AE%A1%E7%AE%97+%E5%BF%AB%E9%80%9F&source=bl&ots=JwUhB_F5zj&sig=jmLAjRdMJezkOFLs2YP0grAspkA&hl=zh-CN&sa=X&ved=0ahUKEwiTjrvXzsLMAhVHG5QKHe-eD-wQ6AEIaTAJ#v=onepage&q=%E8%A1%8C%E5%88%97%E5%BC%8F%20%E8%AE%A1%E7%AE%97%20%E5%BF%AB%E9%80%9F&f=false
代码如下:
#include "stdafx.h"
#include "stdio.h"
#include "stdlib.h"
#include "math.h"
#define g_order 5
#define M(m, i, j) (m[(i) * g_order + (j)])
int findNoneZero(double* matrix, int column);
double det(double* matrix, int startRowColumn);
int allSign = 1;
float result = 1;
void display(double* matrix) {
printf("\n");
for (int i=0; i
另:在计算的过程中发现,double还是不够用,一些精度丢失了。要解决这个问题,只能用高精度乘法除法来计算。于是从网上找了个大整数算法,改进了上面的程序:
#include "stdafx.h"
#include "stdio.h"
#include "stdlib.h"
#include "math.h"
#define MAXDIGITS 1024 /* maximum length bignum */
#define PLUS 1 /* positive sign bit */
#define MINUS -1 /* negative sign bit */
typedef struct {
char digits[MAXDIGITS]; /* represent the number */
int signbit; /* 1 if positive, -1 if negative */
int lastdigit; /* index of high-order digit */
} bignum;
void subtract_bignum(bignum *a, bignum *b, bignum *c);
void multiply_bignum(bignum *a, bignum *b, bignum *c);
void zero_justify(bignum *n);
int compare_bignum(bignum *a, bignum *b);
void print_bignum(bignum *n);
#define g_order 4
struct fractor {
bignum top;
bignum down;
} ;
typedef struct fractor fractor;
#define M(m, i, j) (m[(i) * g_order + (j)])
int findNoneZero(fractor* matrix, int column);
bignum det(fractor* matrix, int startRowColumn);
int allSign = 1;
void display(fractor* matrix) {
printf("\n");
for (int i=0; isignbit == MINUS) printf("-");
for (i=n->lastdigit; i>=0; i--)
printf("%c",'0'+ n->digits[i]);
}
float bignum_to_float(bignum *n)
{
int i;
float r = 0;
// if (n->signbit == MINUS) r = -1.0;
int first = 1;
for (i=n->lastdigit; i>=0; i--) {
if (first == 1) {
r = n ->digits [i];
first = 0;
} else {
r = 10 * r + n->digits[i];
}
}
return r;
}
void int_to_bignum(int s, bignum *n)
{
int i; /* counter */
int t; /* int to work with */
if (s >= 0) n->signbit = PLUS;
else n->signbit = MINUS;
for (i=0; idigits[i] = (char) 0;
n->lastdigit = -1;
t = abs(s);
while (t > 0) {
n->lastdigit ++;
n->digits[ n->lastdigit ] = (t % 10);
t = t / 10;
}
if (s == 0) n->lastdigit = 0;
}
void initialize_bignum(bignum *n)
{
int_to_bignum(0,n);
}
int max(int a, int b)
{
if (a > b) return(a); else return(b);
}
/* c = a +-/* b; */
void add_bignum(bignum *a, bignum *b, bignum *c)
{
int carry; /* carry digit */
int i; /* counter */
initialize_bignum(c);
if (a->signbit == b->signbit) c->signbit = a->signbit;
else {
if (a->signbit == MINUS) {
a->signbit = PLUS;
subtract_bignum(b,a,c);
a->signbit = MINUS;
} else {
b->signbit = PLUS;
subtract_bignum(a,b,c);
b->signbit = MINUS;
}
return;
}
c->lastdigit = max(a->lastdigit,b->lastdigit)+1;
carry = 0;
for (i=0; i<=(c->lastdigit); i++) {
c->digits[i] = (char) (carry+a->digits[i]+b->digits[i]) % 10;
carry = (carry + a->digits[i] + b->digits[i]) / 10;
}
zero_justify(c);
}
void subtract_bignum(bignum *a, bignum *b, bignum *c)
{
int borrow; /* has anything been borrowed? */
int v; /* placeholder digit */
int i; /* counter */
initialize_bignum(c);
if ((a->signbit == MINUS) || (b->signbit == MINUS)) {
b->signbit = -1 * b->signbit;
add_bignum(a,b,c);
b->signbit = -1 * b->signbit;
return;
}
if (compare_bignum(a,b) == PLUS) {
subtract_bignum(b,a,c);
c->signbit = MINUS;
return;
}
c->lastdigit = max(a->lastdigit,b->lastdigit);
borrow = 0;
for (i=0; i<=(c->lastdigit); i++) {
v = (a->digits[i] - borrow - b->digits[i]);
if (a->digits[i] > 0)
borrow = 0;
if (v < 0) {
v = v + 10;
borrow = 1;
}
c->digits[i] = (char) v % 10;
}
zero_justify(c);
}
int compare_bignum(bignum *a, bignum *b)
{
int i; /* counter */
if ((a->signbit == MINUS) && (b->signbit == PLUS)) return(PLUS);
if ((a->signbit == PLUS) && (b->signbit == MINUS)) return(MINUS);
if (b->lastdigit > a->lastdigit) return (PLUS * a->signbit);
if (a->lastdigit > b->lastdigit) return (MINUS * a->signbit);
for (i = a->lastdigit; i>=0; i--) {
if (a->digits[i] > b->digits[i]) return(MINUS * a->signbit);
if (b->digits[i] > a->digits[i]) return(PLUS * a->signbit);
}
return(0);
}
void zero_justify(bignum *n)
{
while ((n->lastdigit > 0) && (n->digits[ n->lastdigit ] == 0))
n->lastdigit --;
if ((n->lastdigit == 0) && (n->digits[0] == 0))
n->signbit = PLUS; /* hack to avoid -0 */
}
void digit_shift(bignum *n, int d) /* multiply n by 10^d */
{
int i; /* counter */
if ((n->lastdigit == 0) && (n->digits[0] == 0)) return;
for (i=n->lastdigit; i>=0; i--)
n->digits[i+d] = n->digits[i];
for (i=0; idigits[i] = 0;
n->lastdigit = n->lastdigit + d;
}
void multiply_bignum(bignum *a, bignum *b, bignum *c)
{
bignum row; /* represent shifted row */
bignum tmp; /* placeholder bignum */
int i,j; /* counters */
initialize_bignum(c);
row = *a;
for (i=0; i<=b->lastdigit; i++) {
for (j=1; j<=b->digits[i]; j++) {
add_bignum(c,&row,&tmp);
*c = tmp;
}
digit_shift(&row,1);
}
c->signbit = a->signbit * b->signbit;
zero_justify(c);
}
void divide_bignum(bignum *a, bignum *b, bignum *c)
{
bignum row; /* represent shifted row */
bignum tmp; /* placeholder bignum */
int asign, bsign; /* temporary signs */
int i,j; /* counters */
initialize_bignum(c);
c->signbit = a->signbit * b->signbit;
asign = a->signbit;
bsign = b->signbit;
a->signbit = PLUS;
b->signbit = PLUS;
initialize_bignum(&row);
initialize_bignum(&tmp);
c->lastdigit = a->lastdigit;
for (i=a->lastdigit; i>=0; i--) {
digit_shift(&row,1);
row.digits[0] = a->digits[i];
c->digits[i] = 0;
while (compare_bignum(&row,b) != PLUS) {
c->digits[i] ++;
subtract_bignum(&row,b,&tmp);
row = tmp;
}
}
zero_justify(c);
a->signbit = asign;
b->signbit = bsign;
}
bignum zero;
int main()
{
//init
int_to_bignum(0,&zero);
int a[g_order * g_order] = {41, 18467, 6334, 26500,
19169, 15724, 11478, 29358,
26962, 24464, 5705, 28145,
23281, 16827, 9961, 491
};
//double a[g_order * g_order] = {1,2,3,4,3,2,1,3,2};
fractor f[g_order * g_order];
for (int i=0; i