Elektrische Potentialvisualisierung
Dies ist Übung 3.2.23. aus dem Buch Informatik Ein interdisziplinärer Ansatz von Sedgewick & Wayne:
Schreiben Sie ein Programm, das aus den bei der Standardeingabe angegebenen Werten ein Array geladener Teilchen erstellt (jedes geladene Teilchen wird durch seine x-Koordinate, y-Koordinate und seinen Ladungswert angegeben) und eine Visualisierung des elektrischen Potentials im Einheitsquadrat erstellt. Abtasten Sie dazu Punkte im Einheitsquadrat. Berechnen Sie für jeden abgetasteten Punkt das elektrische Potential an diesem Punkt (durch Summieren der elektrischen Potentiale für jedes geladene Teilchen) und zeichnen Sie den entsprechenden Punkt in einem Grauton proportional zum elektrischen Potential auf.
Das Folgende ist die Datentypimplementierung für geladene Teilchen aus dem Buch, das ich modifiziert habe (verschönert und passendere Namen hinzugefügt):
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 + ")";
}
}
Hier ist mein Programm (aber um die Vielfalt zu erhöhen, habe ich zufällige Partikel erstellt, anstatt aus Eingabedaten zu lesen):
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 ist eine einfache API, die von den Autoren des Buches geschrieben wurde. Ich habe mein Programm überprüft und es funktioniert. Hier sind zwei Beispiele dafür:
Eingabe: 3840 2160 200 0,5 10
Ausgabe:
Eingabe: 3840 2160 5000 0,5 5
Ausgabe:
Gibt es eine Möglichkeit, mein Programm zu verbessern?
Danke für Ihre Aufmerksamkeit.
Antworten
Testfälle überzeugen nicht. Ich verstehe, dass das Programm nicht abstürzt und durch visuelle Überprüfung des Codes die richtigen Dinge zu tun scheint. Es ist jedoch so gut wie unmöglich zu überprüfen, wie das Potenzial von 5000 zufälligen Ladungen aussehen sollte. Ich würde lieber mindestens zwei Elementarpotentiale sehen: eines einer einzelnen Ladung und eines eines Dipols .
Bitte keine nackten Loops. Jede Schleife implementiert einen wichtigen Teil des Jobs und verdient einen Namen. Erwägen
public static void main(String[] args) { .... Charge[] charges = generateRandomCharges(numberOfCharges, chargeSignDistribution, chargeSizeDistribution); double[][] potentials = computePotentials(width, height, charges); rescalePotentials(width, height, potentials); ....Eine Neuskalierung ist eine Fehlbezeichnung. Was Sie tun, heißt Klemmen.
Es scheint auch , dass die Werte von 180 gehört zur Verschiebung Klemmphase. Verschieben und Zusammenklemmen bereiten die Visualisierung vor und haben nichts mit Rechenpotentialen zu tun.
Einige kleinere Verbesserungen können auf Ihren Code angewendet werden. Sie haben die folgende Methode:
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);
}
Sie können die Math.hypotMethode anwenden , um den Abstand zwischen zwei Punkten folgendermaßen zu berechnen:
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;
}
Hinweis: Wie ex nihilo in seinem Kommentar festgestellt hat, sollte die Math.hypotMethode verwendet werden, da sie sqrt (x2 + y2) ohne Zwischenüberlauf oder -unterlauf zurückgibt , Fälle, die von der in Ihrem Code enthaltenen Methode nicht behandelt werden.
Sie haben die folgende toStringMethode in Ihrem Code:
public String toString() {
return charge + " at (" + pointXCoordinate + "," + pointYCoordinate + ")";
}
Um die Lesbarkeit zu verbessern, können Sie die String.formatMethode folgendermaßen anwenden :
public String toString() {
return String.format("%f at (%f,%f)", charge, pointXCoordinate, pointYCoordinate);
}
In Ihrem Code sind die folgenden Zeilen vorhanden:
double charge = 0;
if (Math.random() < chargeSignDistribution) charge = -chargeSizeDistribution + Math.random() * chargeSizeDistribution;
else charge = Math.random() * chargeSizeDistribution;
Sie können sie folgendermaßen umschreiben:
double charge = Math.random() * chargeSizeDistribution;
if (Math.random() < chargeSignDistribution) {
charge -= chargeSizeDistribution;
}