Outil pour Newton
A propos du code
Additions à la bibliothèque
// mouse buttons
#define BUT_LEFT (1 << 0)
#define BUT_RIGHT (1 << 1)
class CSystem
{
public:
[...]
Uint8 buttons; // true while a button is pressed
Uint8 buttonsDown; // true only at the frame the button is pressed
Uint8 buttonsUp; // true only at the frame the button is released
int mouseX;
int mouseY;
mouseX et mouseY contiennent les coordonnées de la souris en pixels.
But du programme
La classe Polynomial
p(z) = c0*zn + c1*zn-1 + ... + cn-1*z + cn
Ainsi que ses racines.
class Polynomial
{
private:
std::vector<Complex> coefficients;
public:
std::vector<Complex> roots;
Une fonction degree() va simplement renvoyer le degré du polynôme qui est le nombre de coefficients moins 1.
int Polynomial::degree()
{
return coefficients.size() - 1;
}
2 fonctions nous permettent d'ajouter une racine ou de retirer la dernière.
void Polynomial::addRoot(Complex r)
{
roots.push_back(r);
coefficients.resize(roots.size() + 1);
}
void Polynomial::remRoot()
{
roots.pop_back();
coefficients.resize(roots.size() + 1);
}
Une fonction nous permet d'évaluer la valeur du polynôme pour un z donné.
Complex Polynomial::evaluate(Complex z)
{
Complex result = Complex(0, 0);
Complex zn = Complex(1, 0);
for (int i = degree(); i >= 0; i--)
{
result = result + zn * coefficients[i];
zn = zn * z;
}
return result;
}
Le calcul de la dérivée est facile aussi.
void Polynomial::derivative(Polynomial& result)
{
result.coefficients.clear();
for (int i = 0; i <= degree()-1; i++)
{
Complex coef = coefficients[i] * (degree() - i);
result.coefficients.push_back(coef);
}
}
Trouver les coefficients
p(z) = c0*zn + c1*zn-1 + ... + cn-1*z + cn
Quand on connait ses racines, il peut être écrit sous une forme factorisée:
p(z) = (z - r0)(z - r1)...(z - rn-1)
Si on développe cette forme, voici ce qu'on trouve:
c0 = 1
c1 = (-r0) + (-r1) + ...
c2 = (-r0)*(-r1) + (-r0)*(-r2) + ... + (-r1)*(-r2) + (-r1)*(-r3) + ...
...
cn = (-r0)*(-r1)*(-r2)...
Chaque coefficient cm est la somme de toutes les combinaisons de m racines prises parmi toutes les racines.
std::vector<int> idx;
Chacun de ces index va représenter le numéro d'une racine.
idx[0] < idx[1] < ... < idx[n-1]
Pour respecter ça, au départ, on va les initialiser de façon à ce que chacun soit égal au précédent plus 1.
idx[0] = 0;
idx[1] = id[0] + 1;
idx[2] = id[1] + 1;
...
Les index n'iront pas tous jusqu'à n-1 non plus. Comme ils doivent être tous différents, ça implique que le dernier
idx[0] = 0;
idx[1] = 1;
idx[2] = 2;
idx[3] = 3;
On incrémente idx[3]. Il pointe maintenant sur la racine numéro 4:
idx[0] = 0;
idx[1] = 1;
idx[2] = 2;
idx[3] = 4;
Au tour suivant, on l'incrémente encore.
idx[0] = 0;
idx[1] = 1;
idx[2] = 2;
idx[3] = 5;
Et on continue jusqu'à ce qu'on atteigne n-1.
idx[0] = 0;
idx[1] = 1;
idx[2] = 2;
idx[3] = n-1;
Maintenant, si on l'incrémente, il sera à n, ce qui dépasse le nombre de racines.
idx[0] = 0;
idx[1] = 1;
idx[2] = 2;
idx[3] = n;
Alors on remonte d'un niveau et on incrémente idx[2].
idx[0] = 0;
idx[1] = 1;
idx[2] = 3;
idx[3] = n;
Puis on réinitialise idx[3] à idx[2] + 1:
idx[0] = 0;
idx[1] = 1;
idx[2] = 3;
idx[3] = 4;
Et on utilise cette configuration.
idx[0] = 0;
idx[1] = 1;
idx[2] = n-2;
idx[3] = n-1;
Maintenant, si on incrémente idx[3], il dépasse encore une fois son maximum:
idx[0] = 0;
idx[1] = 1;
idx[2] = n-2;
idx[3] = n;
Donc on remonte d'un niveau et on incrémente idx[2], mais il atteint aussi son maximum qui est n-1.
idx[0] = 0;
idx[1] = 1;
idx[2] = n-1;
idx[3] = n;
Alors on doit remonter d'un niveau de plus et incrémenter idx[1].
idx[0] = 0;
idx[1] = 2;
idx[2] = n-1;
idx[3] = n;
Puis on ré-initialise idx[2] et idx[3] et on continue le processus:
idx[0] = 0;
idx[1] = 2;
idx[2] = 3;
idx[3] = 4;
Implémentation de findCoefficients
void Polynomial::findCoefficients()
{
std::vector<int> idx;
idx.resize(roots.size() + 1);
Le premier coefficient sera toujours 1.
coefficients[0] = 1;
Maintenant on va faire une boucle pour calculer chacun des autres coefficients:
for (int i = 1; i <= degree(); i++)
{
coefficients[i] = Complex(0, 0);
int j;
On initialise les index. Remarquez que le nombre d'index dépend du numéro du coefficient qu'on calcule.
for (j = 0; j < i; j++)
idx[j] = j;
On démarre une boucle où on va calculer chaque terme du coefficient l'un après l'autre.
while(true)
{
On calcule le terme ici en multipliant toutes les racines qu'on a sélectionnées ensemble.
Complex c = Complex(1, 0);
for (j = 0; j < i; j++)
c = c * (-roots[idx[j]]);
Et on ajoute le terme au coefficient.
coefficients[i] = coefficients[i] + c;
Maintenant on va incrémenter les index en suivant nos règles pour obtenir la combinaison suivante.
j = i - 1;
On démarre une boucle parce qu'on va peut-être remonter de plusieurs niveaux avant de trouver un index qui ne
while(true)
{
On essaye d'incrémenter l'index courant.
idx[j]++;
Si cet index a atteint sa limite, on monte d'un niveau.
if (idx[j] == roots.size() - ((i-1) - j))
{
j--;
if (j < 0)
break;
}
Si on n'a pas atteint de limite cet index a été incrémenté avec succès, alors on ré-initialise tous les index
else
{
for (int k = j + 1; k < i; k++)
idx[k] = idx[k - 1] + 1;
break;
}
}
Si pendant qu'on remontait les niveaux on s'est retrouvé au-dessus du premier index, on sort de la boucle.
if (j < 0)
break;
}
Affichage progressif
void drawNewton(int offsetX, int offsetY, std::vector<Color>& palette)
{
for (int yy = 0; yy < SCREEN_HEIGHT; yy += 8)
{
for (int xx = 0; xx < SCREEN_WIDTH; xx += 8)
{
Le reste de la fonction est la même que la fonction de dessin qu'on a utilisé dans l'article précédent.
static Uint8 pattern[64] =
{
0, 36, 4, 32, 18, 54, 22, 50,
2, 38, 6, 34, 16, 52, 20, 48,
9, 45, 13, 41, 27, 63, 31, 59,
11, 47, 15, 43, 25, 61, 29, 57,
1, 37, 5, 33, 19, 55, 23, 51,
3, 39, 7, 35, 17, 53, 21, 49,
8, 44, 12, 40, 26, 62, 30, 58,
10, 46, 14, 42, 24, 60, 28, 56
};
[...]
while (sys.isQuitRequested() == false)
{
// draw the fractal
if (frameCount < 64)
{
drawNewton(pattern[frameCount] % 8,
pattern[frameCount] / 8,
palette);
frameCount++;
}
Gérer la souris
// display and process the events
gfx.render();
sys.wait(20);
sys.processEvents();
// left mouse button: add a new root
if (sys.buttonsDown & BUT_LEFT)
{
// restart the drawing
frameCount = 0;
// choose a random color
palette[palette.size()-1] = Color(rand() % 256, rand() % 256, rand() % 256);
palette.push_back(Color(0, 0, 0));
// add the root
Complex r;
r.re = (sys.mouseX - centerX) * zoom;
r.im = (sys.mouseY - centerY) * zoom;
p.addRoot(r);
// recompute the coefficients
p.findCoefficients();
p.derivative(dp);
}
// right mouse button: remove the last root
if (sys.buttonsDown & BUT_RIGHT)
{
// if there is at least one root
if (p.roots.size() > 0)
{
// restart the drawing
frameCount = 0;
// remove the root
palette.pop_back();
p.remRoot();
// recompute the coefficients
p.findCoefficients();
p.derivative(dp);
}
}
Télécharger le code source
Télécharger l'exécutable pour Windows
Voici quelques exemples de ce qu'on peut obtenir: