Je résous le système de réaction-diffusion de Turing avec le code C ++ suivant. C'est trop lent: pour une texture de 128x128 pixels, le nombre d'itérations acceptable est de 200 - ce qui entraîne un délai de 2,5 secondes. J'ai besoin de 400 itérations pour obtenir une image intéressante - mais 5 secondes d'attente, c'est trop. De plus, la taille de la texture devrait être en fait 512x512 - mais cela se traduit par un temps d'attente énorme. Les appareils sont iPad, iPod.
Y a-t-il une chance de le faire plus rapidement? La méthode Euler converge lentement (wikipedia) - une méthode plus rapide permettrait de réduire le nombre d'itérations?
EDIT: Comme l'a souligné Thomas Klimpel, les lignes: "if (m_An [i] [j] <0.0) {...}", "if (m_Bn [i] [j] <0.0) {...}" retardent la convergence: après suppression, une image significative apparaît après 75 itérations . J'ai commenté les lignes dans le code ci-dessous.
void TuringSystem::solve( int iterations, double CA, double CB ) {
m_iterations = iterations;
m_CA = CA;
m_CB = CB;
solveProcess();
}
void set_torus( int & x_plus1, int & x_minus1, int x, int size ) {
// Wrap "edges"
x_plus1 = x+1;
x_minus1 = x-1;
if( x == size - 1 ) { x_plus1 = 0; }
if( x == 0 ) { x_minus1 = size - 1; }
}
void TuringSystem::solveProcess() {
int n, i, j, i_add1, i_sub1, j_add1, j_sub1;
double DiA, ReA, DiB, ReB;
// uses Euler's method to solve the diff eqns
for( n=0; n < m_iterations; ++n ) {
for( i=0; i < m_height; ++i ) {
set_torus(i_add1, i_sub1, i, m_height);
for( j=0; j < m_width; ++j ) {
set_torus(j_add1, j_sub1, j, m_width);
// Component A
DiA = m_CA * ( m_Ao[i_add1][j] - 2.0 * m_Ao[i][j] + m_Ao[i_sub1][j] + m_Ao[i][j_add1] - 2.0 * m_Ao[i][j] + m_Ao[i][j_sub1] );
ReA = m_Ao[i][j] * m_Bo[i][j] - m_Ao[i][j] - 12.0;
m_An[i][j] = m_Ao[i][j] + 0.01 * (ReA + DiA);
// if( m_An[i][j] < 0.0 ) { m_An[i][j] = 0.0; }
// Component B
DiB = m_CB * ( m_Bo[i_add1][j] - 2.0 * m_Bo[i][j] + m_Bo[i_sub1][j] + m_Bo[i][j_add1] - 2.0 * m_Bo[i][j] + m_Bo[i][j_sub1] );
ReB = 16.0 - m_Ao[i][j] * m_Bo[i][j];
m_Bn[i][j] = m_Bo[i][j] + 0.01 * (ReB + DiB);
// if( m_Bn[i][j] < 0.0 ) { m_Bn[i][j]=0.0; }
}
}
// Swap Ao for An, Bo for Bn
swapBuffers();
}
}
Réponses:
Vous semblez limité par la stabilité, ce qui est attendu car la diffusion est raide lorsque vous affinez la grille. Les bonnes méthodes pour les systèmes rigides sont au moins partiellement implicites. Cela prendra un certain effort, mais vous pouvez implémenter un algorithme multigrille simple (ou utiliser une bibliothèque) pour résoudre ce système avec un coût inférieur à dix "unités de travail" (essentiellement le coût d'un de vos pas de temps). Lorsque vous affinez la grille, le nombre d'itérations n'augmentera pas.
la source
D'un point de vue pratique: le processeur A5 n'est pas très puissant, vous pouvez donc attendre quelques itérations matérielles, ou si votre ipod / ipad va être connecté à internet, résoudre votre problème à distance ou dans le cloud.
la source
Euler converge lentement par rapport à d'autres méthodes, mais je ne pense pas que ce soit ce qui vous intéresse. Si vous recherchez simplement des images "intéressantes", augmentez la taille de votre pas de temps et prenez moins d'itérations. Le problème, comme le souligne Jed, est que la méthode euler explicite a des problèmes de stabilité avec de grands pas de temps par rapport à la taille de la grille. plus votre grille est petite (c'est-à-dire plus la résolution de votre image est élevée), plus votre pas de temps doit être petit pour en tenir compte.
Par exemple, en utilisant euler implicite au lieu d'explicite, vous ne gagnez aucun ordre de convergence, mais la solution aura une stabilité inconditionnelle, permettant des pas de temps beaucoup plus grands. Les méthodes implicites sont plus compliquées à implémenter et prennent plus de calcul par pas de temps, mais vous devriez voir des gains bien au-delà en prenant moins d'étapes au total.
la source