Visualização do potencial elétrico

Sep 18 2020

Este é o exercício 3.2.23. do livro Computer Science An Interdisciplinary Approach, de Sedgewick & Wayne:

Escreva um programa que crie uma matriz de partículas carregadas a partir de valores dados na entrada padrão (cada partícula carregada é especificada por sua coordenada x, coordenada y e valor de carga) e produza uma visualização do potencial elétrico no quadrado da unidade. Para fazer isso, experimente pontos no quadrado da unidade. Para cada ponto amostrado, calcule o potencial elétrico naquele ponto (somando os potenciais elétricos devido a cada partícula carregada) e plote o ponto correspondente em um tom de cinza proporcional ao potencial elétrico.

A seguir está a implementação do tipo de dados para partículas carregadas do livro que modifiquei (embelezei e adicionei nomes mais apropriados):

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 + ")";
    }
}

Aqui está meu programa (mas para aumentar a variedade, criei partículas aleatórias em vez de ler os dados de entrada):

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();
    }
}

Imagem é uma API simples escrita pelos autores do livro. Verifiquei meu programa e funciona. Aqui estão duas instâncias dele:

Entrada: 3840 2160 200 0,5 10

Resultado:

Entrada: 3840 2160 5000 0,5 5

Resultado:

Existe alguma maneira de melhorar meu programa?

Agradecimentos para sua atenção.

Respostas

10 vnp Sep 19 2020 at 03:14
  • Os casos de teste não são convincentes. Eu entendo que o programa não trava e, pela inspeção visual do código, parece fazer as coisas certas. No entanto, é praticamente impossível verificar como deve ser o potencial de 5.000 cargas aleatórias. Prefiro ver pelo menos dois potenciais elementares: um de uma única carga e um de um dipolo .

  • Sem loops nus, por favor. Cada loop implementa uma parte importante do trabalho e merece um nome. Considerar

      public static void main(String[] args) {
          ....
          Charge[] charges = generateRandomCharges(numberOfCharges, chargeSignDistribution, chargeSizeDistribution);
          double[][] potentials = computePotentials(width, height, charges);
          rescalePotentials(width, height, potentials);
          ....
    
  • Reescalonar é um nome impróprio. O que você faz é chamado de aperto.

    Também parece que o deslocamento dos valores em 180 pertence à fase de fixação . Deslocar e prender juntos prepara a visualização e não tem nada a ver com potenciais de computação.

8 dariosicily Sep 19 2020 at 00:34

Algumas pequenas melhorias podem ser aplicadas ao seu código; você tem o seguinte método:

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);
}

Você pode aplicar o Math.hypotmétodo para calcular a distância entre dois pontos desta forma:

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;
}

Nota: como ex nihilo observou em seu comentário, o Math.hypotmétodo deve ser usado porque retorna sqrt (x2 + y2) sem estouro ou estouro intermediário , casos não tratados pelo método presente em seu código.

Você tem o seguinte toStringmétodo em seu código:

public String toString() {
    return charge + " at (" + pointXCoordinate + "," + pointYCoordinate + ")";
}

Para melhorar a legibilidade, você pode aplicar o String.formatmétodo desta forma:

public String toString() {
    return String.format("%f at (%f,%f)", charge, pointXCoordinate, pointYCoordinate);
}

Em seu código, as seguintes linhas estão presentes:

double charge = 0;
if (Math.random() < chargeSignDistribution) charge = -chargeSizeDistribution + Math.random() * chargeSizeDistribution;
else charge = Math.random() * chargeSizeDistribution;

Você pode reescrevê-los desta forma:

double charge = Math.random() * chargeSizeDistribution;
if (Math.random() < chargeSignDistribution) {
   charge -= chargeSizeDistribution;
}