begin process at 2012 05 27 19:22:30
  Trouver un code source :
 
dans
 
Accueil > 

Code

 > 

Maths & Algorithmes

 > SOLUTION GRAPHIQUE APPROCHÉE AU PROBLÈME DES N CORPS

SOLUTION GRAPHIQUE APPROCHÉE AU PROBLÈME DES N CORPS


 Information sur la source

Note :
Aucune note
Catégorie :Maths & Algorithmes Niveau :Débutant Date de création :22/05/2005 Date de mise à jour :23/05/2005 18:38:01 Vu / téléchargé :5 784 / 155

Auteur : MetalDwarf

Ecrire un message privé
Commentaire sur cette source (6)
Ajouter un commentaire et/ou une note

 Description

Cliquez pour voir la capture en taille normale
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.

 Fichier Zip

Les Membres Club peuvent télécharger directement un fichier contenu dans le zip sans télécharger le zip en entier !

Télécharger le zip


 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

Source avec Zip LECTURE/ECRITURE DES MSR (MODEL SPECIFIC REGISTER) EN C SOUS...
Source avec Zip Source avec une capture SERVEUR DE CHAT MULTITHREADE EN C SOUS LINUX
Source avec Zip TESTE SI UN TRES GRAND NOMBRE (PLUSIEURS MILLIERS DE CHIFFRE...
COMMENT UN PC FAIT DES MULTIPLICATIONS...
Source avec Zip COMPRESSION BZ2 (MIEUX QUE ZLIB) EN C.

 Sources de la même categorie

Source avec Zip UN EXAMPLE D'APPLICATION EN CUDA DE L'ALGORITHME DE SCAN POU... par oguzaras
Source avec Zip Source avec une capture CHIFFREMENT DE VIGENERE par lajouad
Source avec Zip Source avec une capture ANALYSE SYNTAXIQUE par lajouad
Source avec Zip Source avec une capture STRUCTURE D'UNE MATRICE PAR LES LISTE LINÉAIRE (NON CONTUGUS... par benzarabel
Source avec Zip Source avec une capture DESSINER UNE ARBRE BINAIRE( MODE CONSOLE): par benzarabel

Commentaires et avis

Commentaire de JCDjcd le 22/05/2005 13:45:56

Programme amusant, moi j'en ai fait un pareil, mais je ne l'ai pas encore mis, je dois encore l'ameliorer, il faut que j'implemente du Runge-Kutta, car ici du fait du Euler, or le probleme est numeriquement instable.

Note historique : Poincaré a demontrer qu'il n'existait pas de solution générale, donc pas besion de chercher ...

Je trouve le code bien fait (partitionnement des fonctions) et bien presenter.

Commentaire de MetalDwarf le 22/05/2005 19:16:05

En fait je l'ai fait en Caml au début, tout du moins le début, c'était une partie d'un sujet d'informatique de Polytechnique il y a quelques annees (il etait question d'un approximation a base de quadtree permettant de passer le probleme en O(n.log(n))).
C'est vrai que le probleme a des chances d etre chaotique, c est pour ca que le "dt" est petit (0.1s) pour eviter les erreurs.
Faudra que je planche sur Runge-Kitta un jour, Euler c'est un peu limite en effet.

Commentaire de Jarod1980 le 23/05/2005 17:34:43

Très bon code et très bien commenté! en ce qui concerne Runge Kutta il y en a sur le site dont un de moi je crois que JCDjcd  en a publié un également.
Des systèmes d'une telle complexité sont chaotiques. Pour N>3 ils n'existent pas de solution générale.

Commentaire de Kirua le 23/05/2005 17:42:36

Euhm, tu l'as sans doute fait et tu n'as pas précisé la constante du n² comme c'est l''habitude, mais tu peux passer ton algo en O(n²/2) en considérant les couples de corps, comme les forces en jeux entre ces deux corps sont simplement opposées. Ceci dit, je dis pê qq ch que tu as fait, désolé ;).

Commentaire de MetalDwarf le 23/05/2005 18:40:48

Jarod1980 > Oui j'ai deja vu des choses la dessus sur le site, mais je regarderais ca quand j aurais le temps (apres les concours, et oui math spe c'est pas drole tous les jours)

Kirua > Tres bonne idee, d'ailleurs c'est fait! (depuis 10 minutes ;). Cependant la plus grande part du temps processeur est "mangee" par SDL, si vous savez comment changer ca... Je ne connais absolument rien a SDL.

Commentaire de Kirua le 23/05/2005 20:05:36

Envisage peut-être de passer à OpenGL, que tu peux d'ailleurs construire par dessus SDL, mais je te conseille vivement GLFW.

 Ajouter un commentaire




Nos sponsors


Sondage...

Comparez les prix

CalendriCode

Mai 2012
LMMJVSD
 123456
78910111213
14151617181920
21222324252627
28293031   

Consulter la suite du CalendriCode

A découvrir



 
Développement réalisé par Nicolas SOREL (Nix) avec l'aide de : Cyril DURAND et Emmanuel (EBArtSoft), Merci à Vincent pour ses précieux conseils.
CodeS-SourceS.com© Toute reproduction même partielle est interdite sauf accord écrit du Webmaster
CodeS-SourceS.com© est une marque déposée tous droits réservés

Google Coop CodeS-SourceS Google Coop CodeS-SourceS
Temps d'éxécution de la page : 0,624 sec (3)

Nous contacter | Annoncer sur CodeS-SourceS | Mentions légales