我编写了一个小程序,用Jacobi(迭代)方法求解n个方程组。以下是代码:
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
int main() {
float *a, *b, *x, *xnew, temp;
int i, j, k, maxiter=10000000, n=4;
a = malloc(n*n*sizeof(*a));
b = malloc(n*sizeof(*b));
x = malloc(n*sizeof(*x));
xnew = malloc(n*sizeof(*xnew));
srand((unsigned) time(NULL));
// Filling the matrix
for (i=0;i<=n-1;i++) {
for (j=0;j<=n-1;j++) {
a[n*i+j] = rand()%60;
}
b[i] = rand();
x[i] = rand();
xorg[i]=x[i];
}
// Establishing diagonal dominance
for (i=0;i<=n-1;i++) {
temp=0;
for (j=0;j<=n-1;j++) {
if (j==i) {continue;}
temp = temp + a[n*i+j];
}
a[n*i+i] = temp+1;
}
// Solve the system. Break when residue is low
for (k=0;k<=maxiter-1;k++) {
for (i=0;i<=n-1;i++) {
temp=0;
for (j=0;j<=n-1;j++) {
if (j==i) {continue;}
temp = temp + a[n*i+j]*x[j];
}
xnew[i] = (b[i]-temp)/a[n*i+i];
}
temp=0;
for (i=0;i<=n-1;i++) {
temp = temp + fabs(x[i]-xnew[i]);
x[i]=xnew[i];
}
if (temp<0.0001) {
break;
}
}
printf("Iterations = %d\n",k-1);
return 0;
}
突破循环标准非常容易。这个程序永远不会失败。然而它显然没有收敛(它耗尽了循环中的所有迭代),除非我将浮点数更改为双精度数。浮子比这更精确。怎么了? 在Windows 7下使用CodeBlocks 16.01进行编译,即使这很重要。
答案 0 :(得分:0)
if (temp<0.0001) {
对于给定float
和值的请求来说太精细了。
通过添加x[i]
和xnew[i]
之差的ULP来尝试不同的限制方法。
#include <assert.h>
#include <stdint.h>
static uint32_t ULPf(float x) {
union {
float f;
uint32_t u32;
} u;
assert(sizeof(float) == sizeof(uint32_t));
u.f = x;
if (u.u32 & 0x80000000) {
u.u32 ^= 0x80000000;
return 0x80000000 - u.u32;
}
return u.u32 + 0x80000000;
}
static uint32_t ULP_diff(float x, float y) {
uint32_t ullx = ULPf(x);
uint32_t ully = ULPf(y);
if (x > y) return ullx - ully;
return ully - ullx;
}
...
uint64_t sum0 = -1;
unsigned increase = 0;
for (k = 0; k <= maxiter - 1; k++) {
...
uint64_t sum = 0;
for (i = 0; i <= n - 1; i++) {
uint32_t e = ULP_diff(x[i], xnew[i]);
// printf("%u %e %e %llu\n", i, x[i], xnew[i], (unsigned long long) e);
sum += e;
x[i] = xnew[i];
}
if (sum < sum0) {
// answer is converging
sum0 = sum;
increase = 0;
} else {
increase++;
// If failed to find a better answer in `n` iterations and
// code did at least n*N iterations, break.
if (increase > n && k > n*n) break;
}
答案 1 :(得分:0)
似乎float
数据类型没有上述算法所需的精度,给定方式已编码。该算法确实收敛,但是&#34;残留&#34;永远不会低到退出循环。
我理解这一点的方式是,由于float
变量的内部存储方式,您无法使用极小(0.0001
)和极大(RAND_MAX
)数字进行计算,期望合理的准确性,如上例所示(temp
在最里面的循环中增长到一个巨大的数字。)
因此,设置b[i] = rand()%60;
和x[i] = rand()%60;
可以缓解此问题。
设置b[i] = rand()%6;
x[i] = rand()%6;
和a[n*i+j] = rand()%6
可以最终满足更严格的退出循环条件。
有趣的是,建立更大的对角优势(将a[n*i+i] = temp+1'
更改为a[n*i+i] = temp+10;
也会使程序收敛,而之前不会。
我不熟悉其他人描述的ULP条件,但会投入一些时间
如果未来的读者有时间和精力,也许他们应该阅读"What Every Computer Scientist Should Know About Floating-Point Arithmetic",即使我没有。
BTW,xorg
用于存储原始的x
向量,用于调试目的,因为我在编写CodeBlocks时非常困难
感谢所有人的贡献。