Accueil > > > SOLUTION GRAPHIQUE APPROCHÉE AU PROBLÈME DES N CORPS
SOLUTION GRAPHIQUE APPROCHÉE AU PROBLÈME DES N CORPS
Information sur la source
Description
Le titre est assez explicite je pense, sauf pour ceux qui ne savent pas ce que c'est... Le problème des N corps est un problème de physique qui consiste à calculer les trajectoires de corps soumis a l'attraction gravitationnelle. Ce problème n'a pas de solution "analytique", c'est à dire exacte pour N>2 (du moins personne ne l'a encore trouvée), d'ou l'importance de le résoudre de façon approchée. La solution présentée ici est en 2D, et fait intervenir les relations de base de la mécanique (le PFD, principe fondamental de la dynamique). Le code n'est pas optimisé au maximum, en particulier certains calculs pourraient etre evites (comme le calcul de la force alors que celui de l ecceleration peut etre fait directement), mais est "collé" aux formules physique correspondantes. L'affichage est géré par SDL, c'est mon premier code en SDL, et d'ailleurs la fonction d'affichage d'un pixel est reprise d'un Linux Magazine, mais l'interet n'est pas la. Ce programme a été testé sous Linux avec SDL 1.2, et peut etre assez lent sur un PC peu puissant (dans ce cas augmenter la valeur de dt, qui est la precision de la simulation). Enfin, L'algorithme utilisé est FONDAMENTALEMENT un algorithme en O(N²) par rapport au nombre de corps, donc ca peut devenir lent pour de tres nombreux corps.
Source
- /* Solution graphique approchée du problème des N corps */
- /* Metaldwarf 2005 */
- /* Testé sous Linux avec la libSDL 1.2 */
- /* gcc ncorps.c -o ncorps -lm -Wall `sdl-config --cflags --libs` */
-
- #include <stdio.h>
- #include <stdlib.h>
- #include <math.h>
- #include <SDL/SDL.h>
-
- #define SDL_VIDEO_FLAGS (SDL_HWSURFACE | SDL_ANYFORMAT)
-
- /* Le nombre de corps */
- #define N 5
-
- /************************ Partie Algorithmique ************************/
-
- /* La constante universelle de gravitation */
- #define G 6.67e-11
-
- /* vecteur du plan */
- typedef struct _vect
- {
- double x;
- double y;
- } vect;
-
- vect vecteur_nul = {0,0};
-
- typedef struct _corps
- {
- double m; /* masse */
- vect pos; /* position */
- vect v; /* vitesse */
- vect a; /* acceleration */
- } corps;
-
- /* norme d'un vecteur */
- double norme2(vect u)
- {
- return sqrt((u.x * u.x) + (u.y * u.y));
- }
-
- /* res = u1 + u2 */
- vect vect_add(vect u1, vect u2)
- {
- vect res;
- res.x = u1.x + u2.x;
- res.y = u1.y + u2.y;
- return res;
- }
-
- /* res = u1 - u2 */
- vect vect_sub(vect u1, vect u2)
- {
- vect res;
- res.x = u1.x - u2.x;
- res.y = u1.y - u2.y;
- return res;
- }
-
- /* res = a.u (a réel) */
- vect mul_scal(double a, vect u)
- {
- vect res;
- res.x = a * u.x;
- res.y = a * u.y;
- return res;
- }
-
- /* force gravitationnelle exercée par p1 sur p2 */
- vect force_grav(corps p1, corps p2)
- {
- double d; /* distance entre les deux corps */
- vect u; /* vecteur unitaire dirigé de p2 vers p1 */
-
- d = norme2(vect_sub(p1.pos, p2.pos));
- u = mul_scal((1 / d),vect_sub(p1.pos,p2.pos));
- return mul_scal(G * (p1.m * p2.m) * (1/(d*d)), u);
- }
-
- /* application du principe fondamental de la dynamique au point p, d'index i dans un tableau de taille p */
- /* modifie son accélération */
- void pfd(corps *tab, int i, int n)
- {
- int k;
- vect f = vecteur_nul; /* vecteur force */
- corps p = tab[i];
-
- for(k=0;k<n;k++)
- {
- if(k != i)
- f = vect_add(f, force_grav(tab[k], p));
- }
- tab[i].a = mul_scal((1 / p.m),f);
- }
-
- /* optimisation : F(1->2) = -F(2->1) donc on peut éviter la moitie des calculs */
- void pfd2(corps *tab, int i, int n)
- {
- int k;
- corps p = tab[i];
- vect v = vecteur_nul;
-
- /* premier corps : on reinitialise l'acceleration de tous les corps */
- if(i==0)
- for(k=0;k<n;k++)
- tab[k].a = vecteur_nul;
- /* Dernier corps : toutes les contributions ont deja ete calculee */
- if(i==(n-1)) return;
-
- for(k=i+1;k<n;k++)
- {
- v = force_grav(tab[k],p);
- tab[i].a = vect_add(tab[i].a,mul_scal((1/p.m),v));
- tab[k].a = vect_add(tab[k].a,mul_scal((-1/tab[k].m),v));
- }
- }
-
- /* on a : dv/dt = a et dpos/dt = v
- donc : dv = a * dt et dpos = v * dt */
- /* modifie la position d'un corps (on considère que l'accélération a déja été calculée */
- void change_pos(corps *tab, int i, double dt)
- {
- tab[i].v = vect_add(tab[i].v , mul_scal(dt,tab[i].a));
- tab[i].pos = vect_add(tab[i].pos , mul_scal(dt,tab[i].v));
- }
-
- /* met à jour le tableau de corps en avancant de dt dans le temps */
- /* NB : Les deux boucles doivent etre laisees comme ceci sinon l'acceleration des corps n'est pas calculee correctement */
- /* En effet certains corps on deja bouges avant de calculer l'acceleration des suivants */
- void maj(corps *tab, int n, double dt)
- {
- int i;
- for(i=0;i<n;i++) /* on change les accélérations des corps */
- {
- pfd2(tab,i,n);
- }
- for(i=0;i<n;i++) /* puis on met à jour les positions */
- {
- change_pos(tab,i,dt);
- }
- }
-
- /************************ Partie Graphique ************************/
-
- #define NB_COLOR 5
- #define XRES 1024
- #define YRES 768
- Uint32 color[NB_COLOR] = {0x00ff0000, 0x0000ff00, 0x000000ff, 0x00ff00ff, 0x00ffff00};
-
- /* repris de Linux Magazine 65 (p.80) */
- void PutPixel(SDL_Surface *surface, Uint16 x, Uint16 y, Uint32 color)
- {
- Uint8 bpp = surface->format->BytesPerPixel;
- Uint8 *p = ((Uint8 *) surface->pixels) + y * surface->pitch + x * bpp;
-
- switch(bpp)
- {
- case 1:
- *p = (Uint8) color;
- break;
- case 2:
- *(Uint16 *)p = (Uint16) color;
- break;
- case 3:
- if(SDL_BYTEORDER == SDL_BIG_ENDIAN)
- {
- *(Uint16 *)p = ((color >> 8) & 0xff00) | ((color >> 8) & 0xff);
- *(p+2) = color & 0xff;
- }
- else
- {
- *(Uint16 *)p = color & 0xffff;
- *(p+2) = (color >> 16) & 0xff;
- }
- break;
- case 4:
- *(Uint32 *)p = color;
- break;
- }
- }
-
- /* Anciennes positions des corps dans le systeme de coordonnees de l'ecran */
- Uint16 old_pos_x[N];
- Uint16 old_pos_y[N];
-
- void animer(corps *tab, int n, double dt, SDL_Surface *screen)
- {
- int i;
- int need_update = 0;
- while(1)
- {
- maj(tab,n,dt);
-
- for(i=0;i<n;i++)
- {
- Uint16 new_x = (Uint16)tab[i].pos.x;
- Uint16 new_y = (Uint16)tab[i].pos.y;
- if((new_x <= 0) || (new_x >= XRES)
- ||(new_y <= 0) || (new_y >= YRES))
- {
- printf("Un corps a quitté l'écran. Fin du programme\n");
- return;
- }
- /* On ne change l'affichage que si le corps a effectivement bouge a l'ecran */
- if((new_x != old_pos_x[i]) || (new_y != old_pos_y[i]))
- {
- old_pos_x[i] = new_x;
- old_pos_y[i] = new_y;
- SDL_LockSurface(screen);
- PutPixel(screen,new_x, new_y, color[i % NB_COLOR]);
- SDL_UnlockSurface(screen);
- need_update++;
- }
- }
- /* Pour pallier a la lenteur de l'affichage on ne le rafraichit que toutes les 10 modifications */
- if(need_update >= 10)
- {
- need_update = 0;
- SDL_Flip(screen);
- }
- }
- }
-
- int main(int argc, char **argv)
- {
- SDL_Surface *screen;
- corps tab[]= { {1000000000 ,{512,384}, vecteur_nul ,vecteur_nul}, /* le "soleil" */
- {1000000 ,{350,384}, {0,0.01} ,vecteur_nul}, /* les planetes */
- {3000000 ,{512,450}, {0.02,0} ,vecteur_nul},
- {1000500 ,{300,300}, {0.007,-0.001} ,vecteur_nul},
- {2000000 ,{650,450}, {-0.003,0.003} ,vecteur_nul}};
-
- if(SDL_Init(SDL_INIT_VIDEO) == -1)
- {
- printf("Initialisation de SDL impossible : %s\n",SDL_GetError());
- return -1;
- }
- screen = SDL_SetVideoMode(XRES,YRES,24,SDL_VIDEO_FLAGS);
- SDL_FillRect(screen, NULL, SDL_MapRGB(screen->format, 0x00,0x00,0x00)); // écran noir
- SDL_ShowCursor(SDL_DISABLE);
- SDL_Flip(screen);
-
- animer(tab,N,1,screen);
-
- SDL_Quit();
- return 0;
- }
-
/* Solution graphique approchée du problème des N corps */
/* Metaldwarf 2005 */
/* Testé sous Linux avec la libSDL 1.2 */
/* gcc ncorps.c -o ncorps -lm -Wall `sdl-config --cflags --libs` */
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <SDL/SDL.h>
#define SDL_VIDEO_FLAGS (SDL_HWSURFACE | SDL_ANYFORMAT)
/* Le nombre de corps */
#define N 5
/************************ Partie Algorithmique ************************/
/* La constante universelle de gravitation */
#define G 6.67e-11
/* vecteur du plan */
typedef struct _vect
{
double x;
double y;
} vect;
vect vecteur_nul = {0,0};
typedef struct _corps
{
double m; /* masse */
vect pos; /* position */
vect v; /* vitesse */
vect a; /* acceleration */
} corps;
/* norme d'un vecteur */
double norme2(vect u)
{
return sqrt((u.x * u.x) + (u.y * u.y));
}
/* res = u1 + u2 */
vect vect_add(vect u1, vect u2)
{
vect res;
res.x = u1.x + u2.x;
res.y = u1.y + u2.y;
return res;
}
/* res = u1 - u2 */
vect vect_sub(vect u1, vect u2)
{
vect res;
res.x = u1.x - u2.x;
res.y = u1.y - u2.y;
return res;
}
/* res = a.u (a réel) */
vect mul_scal(double a, vect u)
{
vect res;
res.x = a * u.x;
res.y = a * u.y;
return res;
}
/* force gravitationnelle exercée par p1 sur p2 */
vect force_grav(corps p1, corps p2)
{
double d; /* distance entre les deux corps */
vect u; /* vecteur unitaire dirigé de p2 vers p1 */
d = norme2(vect_sub(p1.pos, p2.pos));
u = mul_scal((1 / d),vect_sub(p1.pos,p2.pos));
return mul_scal(G * (p1.m * p2.m) * (1/(d*d)), u);
}
/* application du principe fondamental de la dynamique au point p, d'index i dans un tableau de taille p */
/* modifie son accélération */
void pfd(corps *tab, int i, int n)
{
int k;
vect f = vecteur_nul; /* vecteur force */
corps p = tab[i];
for(k=0;k<n;k++)
{
if(k != i)
f = vect_add(f, force_grav(tab[k], p));
}
tab[i].a = mul_scal((1 / p.m),f);
}
/* optimisation : F(1->2) = -F(2->1) donc on peut éviter la moitie des calculs */
void pfd2(corps *tab, int i, int n)
{
int k;
corps p = tab[i];
vect v = vecteur_nul;
/* premier corps : on reinitialise l'acceleration de tous les corps */
if(i==0)
for(k=0;k<n;k++)
tab[k].a = vecteur_nul;
/* Dernier corps : toutes les contributions ont deja ete calculee */
if(i==(n-1)) return;
for(k=i+1;k<n;k++)
{
v = force_grav(tab[k],p);
tab[i].a = vect_add(tab[i].a,mul_scal((1/p.m),v));
tab[k].a = vect_add(tab[k].a,mul_scal((-1/tab[k].m),v));
}
}
/* on a : dv/dt = a et dpos/dt = v
donc : dv = a * dt et dpos = v * dt */
/* modifie la position d'un corps (on considère que l'accélération a déja été calculée */
void change_pos(corps *tab, int i, double dt)
{
tab[i].v = vect_add(tab[i].v , mul_scal(dt,tab[i].a));
tab[i].pos = vect_add(tab[i].pos , mul_scal(dt,tab[i].v));
}
/* met à jour le tableau de corps en avancant de dt dans le temps */
/* NB : Les deux boucles doivent etre laisees comme ceci sinon l'acceleration des corps n'est pas calculee correctement */
/* En effet certains corps on deja bouges avant de calculer l'acceleration des suivants */
void maj(corps *tab, int n, double dt)
{
int i;
for(i=0;i<n;i++) /* on change les accélérations des corps */
{
pfd2(tab,i,n);
}
for(i=0;i<n;i++) /* puis on met à jour les positions */
{
change_pos(tab,i,dt);
}
}
/************************ Partie Graphique ************************/
#define NB_COLOR 5
#define XRES 1024
#define YRES 768
Uint32 color[NB_COLOR] = {0x00ff0000, 0x0000ff00, 0x000000ff, 0x00ff00ff, 0x00ffff00};
/* repris de Linux Magazine 65 (p.80) */
void PutPixel(SDL_Surface *surface, Uint16 x, Uint16 y, Uint32 color)
{
Uint8 bpp = surface->format->BytesPerPixel;
Uint8 *p = ((Uint8 *) surface->pixels) + y * surface->pitch + x * bpp;
switch(bpp)
{
case 1:
*p = (Uint8) color;
break;
case 2:
*(Uint16 *)p = (Uint16) color;
break;
case 3:
if(SDL_BYTEORDER == SDL_BIG_ENDIAN)
{
*(Uint16 *)p = ((color >> 8) & 0xff00) | ((color >> 8) & 0xff);
*(p+2) = color & 0xff;
}
else
{
*(Uint16 *)p = color & 0xffff;
*(p+2) = (color >> 16) & 0xff;
}
break;
case 4:
*(Uint32 *)p = color;
break;
}
}
/* Anciennes positions des corps dans le systeme de coordonnees de l'ecran */
Uint16 old_pos_x[N];
Uint16 old_pos_y[N];
void animer(corps *tab, int n, double dt, SDL_Surface *screen)
{
int i;
int need_update = 0;
while(1)
{
maj(tab,n,dt);
for(i=0;i<n;i++)
{
Uint16 new_x = (Uint16)tab[i].pos.x;
Uint16 new_y = (Uint16)tab[i].pos.y;
if((new_x <= 0) || (new_x >= XRES)
||(new_y <= 0) || (new_y >= YRES))
{
printf("Un corps a quitté l'écran. Fin du programme\n");
return;
}
/* On ne change l'affichage que si le corps a effectivement bouge a l'ecran */
if((new_x != old_pos_x[i]) || (new_y != old_pos_y[i]))
{
old_pos_x[i] = new_x;
old_pos_y[i] = new_y;
SDL_LockSurface(screen);
PutPixel(screen,new_x, new_y, color[i % NB_COLOR]);
SDL_UnlockSurface(screen);
need_update++;
}
}
/* Pour pallier a la lenteur de l'affichage on ne le rafraichit que toutes les 10 modifications */
if(need_update >= 10)
{
need_update = 0;
SDL_Flip(screen);
}
}
}
int main(int argc, char **argv)
{
SDL_Surface *screen;
corps tab[]= { {1000000000 ,{512,384}, vecteur_nul ,vecteur_nul}, /* le "soleil" */
{1000000 ,{350,384}, {0,0.01} ,vecteur_nul}, /* les planetes */
{3000000 ,{512,450}, {0.02,0} ,vecteur_nul},
{1000500 ,{300,300}, {0.007,-0.001} ,vecteur_nul},
{2000000 ,{650,450}, {-0.003,0.003} ,vecteur_nul}};
if(SDL_Init(SDL_INIT_VIDEO) == -1)
{
printf("Initialisation de SDL impossible : %s\n",SDL_GetError());
return -1;
}
screen = SDL_SetVideoMode(XRES,YRES,24,SDL_VIDEO_FLAGS);
SDL_FillRect(screen, NULL, SDL_MapRGB(screen->format, 0x00,0x00,0x00)); // écran noir
SDL_ShowCursor(SDL_DISABLE);
SDL_Flip(screen);
animer(tab,N,1,screen);
SDL_Quit();
return 0;
}
Conclusion
Je crois que le code n'est pas trop dur à comprendre, n'hesitez pas a formuler des commentaires.
Pour rajouter des corps, il suffit de changer la valeur de N, et de les definir dans main(). Ce n est pas tres evolue, mais c est un petit code fait en 2h. Le systeme de corps present dans la source montre que les trajectoires des "planetes" autour d'un "soleil" sont des ellipses, mais seulement en premiere approximation (si on considere la masse du soleil infinie par rapport a celle des planetes et qu'on neglige l'attraction entre les planetes devant celle exercee par le soleil). C'est pour cela que les trajectoires ne sont pas périodiques.
Historique
- 22 mai 2005 12:17:41 :
- MAJ : Ajout d'un systeme de "planetes" pour la demo contenue dans la source
- 23 mai 2005 18:38:01 :
- Modification de la fonction pfd() (devenue pfd2() ) pour eviter des calculs de force inutiles (F(1->2) = -F(2->1)), et modification du code de mise a jour de l ecran pour gagner en rapidite (l'affichage prend la _majeure_ partie du temps).
Sources du même auteur
Sources de la même categorie
Commentaires et avis
|
Derniers Blogs
IMAGINE CUP 2012, MAKE A SIGN EN FINALEIMAGINE CUP 2012, MAKE A SIGN EN FINALE par junarnoalg
Voilà qui est fait, la nouvelle est officielle ! L'équipe belge "Make a Sign" va au pays des kangourous défendre son projet dans la catégorie Software Design. http://www.imaginecup.com/CompetitionsContent/Competition/WorldwideFinalists.aspx V...
Cliquez pour lire la suite de l'article par junarnoalg KINECT 1.5 IS OUT !KINECT 1.5 IS OUT ! par Vko
La version 1.5 du Kinect For Microsoft vient tout juste de sortir ! Plein de nouveautés: Tracking de squelette en Near Mode Détection en position assise Détection faciale avec un SDK dédié Documentation et des guideline (enfin) Un out...
Cliquez pour lire la suite de l'article par Vko LES ACTUALITéS DE LA SEMAINE SUR C2I.FR (14 MAI - 20 MAI) LES ACTUALITéS DE LA SEMAINE SUR C2I.FR (14 MAI - 20 MAI) par richardc
Mise à jour des Web API du 14 Mai
Réservez dès maintenant votre journée du 20 juin pour le Windows Azure Dev Camp 2012 à Paris
Mise à jour de Team Foundation Service
MechCommander 2 sur Windows 8
Entity Framework 5 Release Candidate e...
Cliquez pour lire la suite de l'article par richardc REACTIVE EXTENSIONS : CONSOMMER DES SERVICES AVEC RX PARTIE 3, LES PIèGES à éVITERREACTIVE EXTENSIONS : CONSOMMER DES SERVICES AVEC RX PARTIE 3, LES PIèGES à éVITER par Groc
Une mauvaise utilisation de rx lors de l'écriture d'une couche d'accès à des services peut conduire à des cas embarassants avec des erreurs mal gérées, des appels qui ne partent lorsqu'ils le devraient, et même des résultats incorrects . le tout nuis...
Cliquez pour lire la suite de l'article par Groc SHAREPOINT BLOG SITE, PROBLèME D'ARCHIVESSHAREPOINT BLOG SITE, PROBLèME D'ARCHIVES par junarnoalg
Dernièrement, nous avons migré le site
myTIC
vers un nouveau serveur SharePoint 2010. Dans les contenus que nous vouloins récupérer, nous avions un certain nombre de blogs.
Nous avons utilisé les commandes Power...
Cliquez pour lire la suite de l'article par junarnoalg
Forum
RE : SAC A DOS RE : SAC A DOS par hadjkaddour
Cliquez pour lire la suite par hadjkaddour
Logiciels
sDEVIS-FACTURES vlPRO (8.1.0.3)SDEVIS-FACTURES VLPRO (8.1.0.3)sDEVIS-FACTURES vlPRO a été mis au point pour les particuliers, créateurs, entrepreneurs, artisa... Cliquez pour télécharger sDEVIS-FACTURES vlPRO 974 Application Server (12.2.4.6)974 APPLICATION SERVER (12.2.4.6)Développez de puissantes applications dans un environnement de 'cloud computing', clusterisé, séc... Cliquez pour télécharger 974 Application Server vPicture (1.4.2.1)VPICTURE (1.4.2.1)Avec vPicture, hébergez vos images facilement et rapidement.
vPicture est un utilitaire simple, ... Cliquez pour télécharger vPicture Easy-Planning (2.2.1.6)EASY-PLANNING (2.2.1.6)Easy-Planning permet de créer des plannings sous la représentation de diagrammes et est adapté au... Cliquez pour télécharger Easy-Planning COM-BACKUP (2.0)COM-BACKUP (2.0)
COM-BACKUP est un logiciel de sauvegarde qui permet de planifier les sauvegardes de vos dossiers ...
Cliquez pour télécharger COM-BACKUP
|