Question

Il semble que je perds beaucoup de précision avec les flotteurs.

Par exemple, je dois résoudre une matrice:

4.0x -2.0y 1.0z =11.0
1.0x +5.0y -3.0z =-6.0
2.0x +2.0y +5.0z =7.0

C’est le code que j’utilise pour importer la matrice à partir d’un fichier texte:

f = open('gauss.dat')
lines =  f.readlines()
f.close()

j=0
for line in lines:
    bits = string.split(line, ',')
    s=[]
    for i in range(len(bits)):
        if (i!= len(bits)-1):
            s.append(float(bits[i]))
            #print s[i]
    b.append(s)
    y.append(float(bits[len(bits)-1]))

Je dois résoudre en utilisant gauss-seidel, donc je dois réorganiser les équations pour x, y et z:

x=(11+2y-1z)/4
y=(-6-x+3z)/5
z=(7-2x-2y)/7

Voici le code que j’utilise pour réorganiser les équations. b est une matrice de coefficients et y est le vecteur de réponse:

def equations(b,y):
    i=0
    eqn=[]
    row=[]
    while(i<len(b)):
        j=0
        row=[]
        while(j<len(b)):
            if(i==j):
                row.append(y[i]/b[i][i])
            else:
                row.append(-b[i][j]/b[i][i])
            j=j+1
        eqn.append(row)
        i=i+1
    return eqn

Cependant, les réponses que je reçois ne sont pas précises à la décimale.

Par exemple, lors de la réorganisation de la deuxième équation ci-dessus, je devrais obtenir:

y=-1.2-.2x+.6z

Ce que je reçois est:

y=-1.2-0.20000000000000001x+0.59999999999999998z

Cela ne semble pas être un gros problème, mais si vous augmentez le nombre à une puissance très élevée, l’erreur est assez importante. Y a-t-il un moyen de contourner ceci? J'ai essayé la classe Decimal , mais cela ne fonctionne pas bien avec les pouvoirs (i.e, Decimal (x) ** 2 ).

Des idées?

Était-ce utile?

La solution

Je ne connais pas suffisamment la classe Decimal pour vous aider, mais votre problème est dû au fait que les fractions décimales peuvent souvent ne pas être représentées correctement en binaire, aussi ce que vous voyez est l'approximation la plus proche possible; Il n'y a aucun moyen d'éviter ce problème sans utiliser une classe spéciale (comme Decimal, probablement).

EDIT: Qu'en est-il de la classe décimale ne fonctionne pas correctement pour vous? Tant que je commence avec une ficelle plutôt qu'un float, les pouvoirs semblent bien fonctionner.

>>> import decimal
>>> print(decimal.Decimal("1.2") ** 2)
1.44

La documentation du module explique la nécessité et l'utilisation de decimal.Decimal. assez clairement, vous devriez le vérifier si vous ne l'avez pas encore fait.

Autres conseils

La virgule flottante IEEE est binaire, pas décimale. Il n'y a pas de fraction binaire de longueur fixe exactement égale à 0,1, ni aucun de ses multiples. C'est une fraction qui se répète, comme 1/3 en décimal.

Veuillez lire Ce que tout informaticien devrait savoir sur l'arithmétique à virgule flottante

D'autres options que la classe Decimal sont

.
  • en utilisant Common Lisp ou Python 2.6 ou une autre langue avec les rationnels exacts

  • conversion des doublons en fermetures rationnelles en utilisant, par exemple, frap

Tout d’abord, votre saisie peut être beaucoup simplifiée. Vous n'avez pas besoin de lire et d'analyser un fichier. Vous pouvez simplement déclarer vos objets en notation Python. Évaluez le fichier.

b = [
    [4.0, -2.0,  1.0],
    [1.0, +5.0, -3.0],
    [2.0, +2.0, +5.0],
]
y = [ 11.0, -6.0, 7.0 ]

Deuxièmement, y = -1.2-0.20000000000000001x + 0.59999999999999999z n'est pas inhabituel. Il n'y a pas de représentation exacte en notation binaire pour 0.2 ou 0.6. Par conséquent, les valeurs affichées sont les approximations décimales des représentations originales non exactes. Celles-ci sont valables pour pratiquement tous les types de processeurs à virgule flottante.

Vous pouvez essayer le module de fractions de Python 2.6. Il existe un ancien paquet ration qui pourrait vous aider.

Oui, élever le nombre de nombres à virgule flottante en puissances augmente les erreurs. Par conséquent, vous devez veiller à ne pas utiliser les positions les plus à droite du nombre à virgule flottante, car ces bits sont principalement du bruit.

Lorsque vous affichez des nombres à virgule flottante, vous devez les entourer correctement pour éviter de voir les bits de bruit.

>>> a
0.20000000000000001
>>> "%.4f" % (a,)
'0.2000'

Je mets en garde contre le module décimal pour des tâches comme celle-ci. Son objectif est davantage de traiter avec des nombres décimaux du monde réel (par exemple, des pratiques de tenue de livres humaines correspondantes), avec une précision finie, sans effectuer de calcul mathématique avec une précision exacte. Il existe des nombres qui ne sont pas exactement représentables en décimal, tout comme en binaire, et effectuer des calculs arithmétiques en décimal est également beaucoup plus lent que les alternatives.

Au lieu de cela, si vous voulez des résultats exacts, vous devez utiliser l’arithmétique rationnelle. Celles-ci représentent les nombres sous forme d'une paire numérateur / dénomentateur, ce qui permet de représenter exactement tous les nombres rationnels. Si vous utilisez uniquement la multiplication et la division (plutôt que des opérations telles que les racines carrées pouvant entraîner des nombres irrationnels), vous ne perdrez jamais de précision.

Comme d'autres l'ont mentionné, python 2.6 aura un type rationnel intégré, mais notez qu'il ne s'agit pas d'une implémentation très performante. Pour des raisons de rapidité, vous feriez mieux d'utiliser des bibliothèques comme gmpy . Il suffit de remplacer vos appels de float () à gmpy.mpq () et votre code devrait maintenant donner des résultats exacts (bien que vous souhaitiez peut-être formater les résultats sous forme de float à des fins d'affichage).

Voici une version légèrement corrigée de votre code pour charger une matrice qui utilisera plutôt gmpy rationnels:

def read_matrix(f):
    b,y = [], []
    for line in f:
        bits = line.split(",")
        b.append( map(gmpy.mpq, bits[:-1]) )
        y.append(gmpy.mpq(bits[-1]))
    return b,y

Ce n'est pas une réponse à votre question, mais un élément connexe:

#!/usr/bin/env python
from numpy import abs, dot, loadtxt, max
from numpy.linalg import solve

data = loadtxt('gauss.dat', delimiter=',')
a, b = data[:,:-1], data[:,-1:]
x = solve(a, b) # here you may use any method you like instead of `solve`
print(x)
print(max(abs((dot(a, x) - b) / b))) # check solution

Exemple:

$ cat gauss.dat
4.0, 2.0, 1.0, 11.0
1.0, 5.0, 3.0, 6.0 
2.0, 2.0, 5.0, 7.0

$ python loadtxt_example.py
[[ 2.4]
 [ 0.6]
 [ 0.2]]
0.0

Voir aussi qu'est-ce qu'un simple exemple d'erreur en virgule flottante , ici sur SO, qui a quelques réponses. Celui que je donne utilise actuellement python comme exemple de langage ...

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top