Visualisation du potentiel électrique
C'est l'exercice 3.2.23. extrait du livre Computer Science An Interdisciplinary Approach de Sedgewick & Wayne:
Écrivez un programme qui crée un tableau de particules chargées à partir des valeurs données sur l'entrée standard (chaque particule chargée est spécifiée par sa coordonnée x, sa coordonnée y et sa valeur de charge) et produit une visualisation du potentiel électrique dans le carré unitaire. Pour ce faire, échantillonnez des points dans le carré unitaire. Pour chaque point échantillonné, calculez le potentiel électrique à ce point (en additionnant les potentiels électriques dus à chaque particule chargée) et tracez le point correspondant dans une nuance de gris proportionnelle au potentiel électrique.
Ce qui suit est l'implémentation du type de données pour les particules chargées du livre que j'ai modifié (embelli et ajouté des noms plus appropriés):
public class Charge {
private final double pointXCoordinate;
private final double pointYCoordinate;
private final double charge;
public Charge(double pointXCoordinate, double pointYCoordinate, double charge) {
this.pointXCoordinate = pointXCoordinate;
this.pointYCoordinate = pointYCoordinate;
this.charge = charge;
}
public double calculatePotentialAt(double otherPointXCoordinate, double otherPointYCoordinate) {
double electrostaticConstant = 8.99e09;
double distanceInXCoordinate = otherPointXCoordinate - pointXCoordinate;
double distanceInYCoordinate = otherPointYCoordinate - pointYCoordinate;
return electrostaticConstant * charge / Math.sqrt(distanceInXCoordinate * distanceInXCoordinate + distanceInYCoordinate * distanceInYCoordinate);
}
public String toString() {
return charge + " at (" + pointXCoordinate + "," + pointYCoordinate + ")";
}
}
Voici mon programme (mais pour augmenter la variété, j'ai créé des particules aléatoires au lieu de lire à partir des données d'entrée):
import java.awt.Color;
public class Potential {
public static void main(String[] args) {
int width = Integer.parseInt(args[0]);
int height = Integer.parseInt(args[1]);
int numberOfCharges = Integer.parseInt(args[2]);
double chargeSignDistribution = Double.parseDouble(args[3]);
double chargeSizeDistribution = Double.parseDouble(args[4]);
Charge[] charges = new Charge[numberOfCharges];
for (int i = 0; i < numberOfCharges; i++) {
double pointXCoordinate = Math.random();
double pointYCoordinate = Math.random();
double charge = 0;
if (Math.random() < chargeSignDistribution) charge = -chargeSizeDistribution + Math.random() * chargeSizeDistribution;
else charge = Math.random() * chargeSizeDistribution;
charges[i] = new Charge(pointXCoordinate, pointYCoordinate, charge);
}
double[][] potentials = new double[width][height];
for (int j = 0; j < width; j++) {
for (int i = 0; i < height; i++) {
for (int k = 0; k < numberOfCharges; k++) {
potentials[j][i] += charges[k].calculatePotentialAt(1.0 * j / width, 1.0 * i / height);
}
/*
Obtained '180' by experimentation.
Scaled down by the amount of electrostatic constant (9e09).
*/
potentials[j][i] = 180 + potentials[j][i] / 9e09;
}
}
int[][] rescaledPotentials = new int[width][height];
for (int j = 0; j < width; j++) {
for (int i = 0; i < height; i++) {
if (potentials[j][i] < 0) rescaledPotentials[j][i] = 0;
else if (potentials[j][i] > 255) rescaledPotentials[j][i] = 255;
else rescaledPotentials[j][i] = (int) potentials[j][i];
}
}
Color[][] colors = new Color[width][height];
for (int j = 0; j < width; j++) {
for (int i = 0; i < height; i++) {
int c = rescaledPotentials[j][i];
colors[j][i] = new Color(c, c, c);
}
}
Picture picture = new Picture(width, height);
for (int j = 0; j < width; j++) {
for (int i = 0; i < height; i++) {
picture.set(j, i, colors[j][i]);
}
}
picture.show();
}
}
Picture est une API simple écrite par les auteurs du livre. J'ai vérifié mon programme et cela fonctionne. En voici deux exemples:
Entrée: 3840 2160200 0,5 10
Production:
Entrée: 3840 2160 5000 0,5 5
Production:
Y a-t-il moyen d'améliorer mon programme?
Merci de votre attention.
Réponses
Les cas de test ne sont pas convaincants. Je comprends que le programme ne plante pas, et par une inspection visuelle du code, il semble faire les bonnes choses. Cependant, il est pratiquement impossible de vérifier à quoi devrait ressembler le potentiel de 5000 charges aléatoires. Je préfère voir au moins deux potentiels élémentaires: l'un d'une seule charge et l'autre d'un dipôle .
Pas de boucles nues s'il vous plaît. Chaque boucle implémente une partie importante du travail et mérite un nom. Considérer
public static void main(String[] args) { .... Charge[] charges = generateRandomCharges(numberOfCharges, chargeSignDistribution, chargeSizeDistribution); double[][] potentials = computePotentials(width, height, charges); rescalePotentials(width, height, potentials); ....Le rééchelonnement est un abus de langage. Ce que vous faites s'appelle le serrage.
Il semble également que le décalage des valeurs de 180 appartienne à la phase de serrage . Le décalage et le serrage ensemble préparent la visualisation et n'ont rien à voir avec les potentiels de calcul.
Certaines améliorations mineures peuvent être appliquées à votre code; vous avez la méthode suivante:
public double calculatePotentialAt(double otherPointXCoordinate, double otherPointYCoordinate) {
double electrostaticConstant = 8.99e09;
double distanceInXCoordinate = otherPointXCoordinate - pointXCoordinate;
double distanceInYCoordinate = otherPointYCoordinate - pointYCoordinate;
return electrostaticConstant * charge / Math.sqrt(distanceInXCoordinate * distanceInXCoordinate + distanceInYCoordinate * distanceInYCoordinate);
}
Vous pouvez appliquer la Math.hypotméthode pour calculer la distance entre deux points de cette manière:
public double calculatePotentialAt(double otherPointXCoordinate, double otherPointYCoordinate) {
double electrostaticConstant = 8.99e09;
double distanceInXCoordinate = otherPointXCoordinate - pointXCoordinate;
double distanceInYCoordinate = otherPointYCoordinate - pointYCoordinate;
double distance = Math.hypot(distanceInXCoordinate, distanceInYCoordinate);
return electrostaticConstant * charge / distance;
}
Remarque: comme observé ex nihilo dans son commentaire, la Math.hypotméthode doit être utilisée car elle retourne sqrt (x2 + y2) sans débordement intermédiaire ni sous-dépassement , cas non traités par la méthode présente dans votre code.
Vous avez la toStringméthode suivante dans votre code:
public String toString() {
return charge + " at (" + pointXCoordinate + "," + pointYCoordinate + ")";
}
Pour améliorer la lisibilité, vous pouvez appliquer la String.formatméthode de cette manière:
public String toString() {
return String.format("%f at (%f,%f)", charge, pointXCoordinate, pointYCoordinate);
}
Dans votre code, les lignes suivantes sont présentes:
double charge = 0;
if (Math.random() < chargeSignDistribution) charge = -chargeSizeDistribution + Math.random() * chargeSizeDistribution;
else charge = Math.random() * chargeSizeDistribution;
Vous pouvez les réécrire de cette manière:
double charge = Math.random() * chargeSizeDistribution;
if (Math.random() < chargeSignDistribution) {
charge -= chargeSizeDistribution;
}