Нарисовать фукцию
Я в целях практики написал код, который должен выводить функцию на экран. Но столкнулся с проблемой, что при выводе функции по типу Math.log(X) * Y == Math.sin(X * Y)//true, приходится увеличивать точность до миллиардных. На графике в форме квадрата от -5 до 5 придётся проверить 100000000000000000000 значений, и потратить на это очень много времени. Есть ли способ оптимизировать это?
код:
import java.awt.*;
import java.awt.image.BufferedImage;
import javax.swing.*;
class FrameHandler{
//основные переменные для установки информации
private static BufferedImage img;
private static Graphics2D g2d;
//константы
private static final double deep = 500; //смещение графика в сторону отрицательных значений
private static final double width = 1000;//минимальная длина линии проходящей через центр графика, и касающейся обоих границ экрана, как ширина или высота квадрата
private static final double zoom = 100;//увеличение масштаба, и уменьшение области допустимых значений... В общем "приближение"
private static final double accur = 1000000000;//точность, если значение = 1, то вариант 1.9,1.1 и тд рассматриваться не будут как возможно соответствующие функции
//окно
static JFrame jFrame = getFrame();
static JButton jButton = new JButton();
//определяем компоненты
private static void init() {
jFrame.add(jButton);
GridBagLayout gridBagLayout = new GridBagLayout();
jButton.setLayout(gridBagLayout);
}
//присваеваем значения
private static void setVars(){
img = new BufferedImage((int)width,(int)width,BufferedImage.TYPE_INT_RGB);
g2d = (Graphics2D) img.getGraphics();
}
//обрабатываем функцию и устанавливаем картинку
public static void mainMethod(){
setVars();
clear();
check_drawAtAllCells();
init();
imageToPanel();
}
//пробегаемся по полю
private static void check_drawAtAllCells(){
for (double X = 0; X < width / zoom * accur; X++){
for (double Y = 0; Y < width / zoom * accur; Y++){
if (checkTheFunc(X / (width / zoom * accur / width), Y / (width / zoom * accur / width))){
System.out.println(X / (width / zoom * accur / width) + " " + Y / (width / zoom * accur / width));
draw(X / (width / zoom * accur / width), Y / (width / zoom * accur / width));
}
}
}
}
//проверяем условие
private static boolean checkTheFunc(double X, double Y){
double imX = ((double) X - (double) deep) / (double) zoom;
double imY = ((double) Y - (double) deep) / (double) zoom;
return Math.log(imX) * imY == Math.sin(imY * imX);//наше уравнение
}
//рисуем
private static void draw(double X, double Y){
g2d.setColor(Color.BLACK);
g2d.fillRect((int)X - 1,(int)Y - 1,3,3);
}
//сила в чистоте))
private static void clear(){
g2d.setColor(Color.WHITE);
g2d.fillRect(0,0,(int)width,(int)width);
}
//задаём картинку панели
private static void imageToPanel(){
jButton.setIcon(new ImageIcon(img));
jButton.setSize((int)width,(int)width);
jButton.repaint();
jFrame.repaint();
jFrame.setVisible(true);
}
//определяем окно
static JFrame getFrame(){
JFrame jFrame = new JFrame();
jFrame.setSize((int)width,(int)width);
jFrame.setResizable(false);
jFrame.setLocationRelativeTo(null);
jFrame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
return jFrame;
}
}
В код не попало, что я вызываю из метода Main.main(String args) метод mainMethod()
Ответы (3 шт):
Разбираясь в данном вопросе, я понял, что тратить меньше времени, или по другому оптимизировать программу нельзя, но есть выход в, виде рассматривания знака равенства как "примерно равно", то есть я просто начал округлять до целой выражения по обе стороны знака равенства. Вполне себе решение проблемы.
График неявной функции так не строится.
Во-первых, нужно немножко изучить функцию. Видно, что X всегда положительно, значит - второй и третий квадранты нас не интересуют.
Одна ветвь решения - луч Y=0.
Кроме того, две ветви решения симметричны относительно оси OX.
Следовательно, используем следующую стратегию: проходим диапазон Y от малого положительного значения до нужного предела с неким шагом. Для каждого Y численно решаем уравнение для X,
ln(X)*Yi=sin(Yi*X)
ставим точку, соединяем отрезком с предыдущей точкой.
Итого получится нечто вроде гиперболы (и симметричная ей с отрицательными Y) c интересными извивами в районе Y=2*Pi*K
Имеем неявную функцию
F(x,y) = G(x,y)
или
Z(x,y) = 0, где Z(x,y) = F(x,y) - G(x,y)
Это эквивалентно сечению трехмерной поверхности плоскостью Z = 0.
Как найти точки пересечения плоскости и поверхности для построения графика? Пройти по пикселам отображаемой области, проверяя, что знак Z в точке (x,y) отличается от знака в точках справа (x+1,y) и снизу (x,y+1). Знак разный - поставили точку. Для обычной отображаемой области типа 500x500 пикселов это будет всего 750000 проверок (втрое меньше, если результаты запоминать в таблице).
Нужен другой масштаб - область остаётся той же самой, только пиксельные координаты масштабируются на меньший диапазон реальных координат, т.е. вычислений столько же.
Вот практический результат для вашей функции. Справа область загогулины в 50-кратном масштабе
Полный код на Delphi. fun преобразует пиксельные координаты в реальные (по уму это преобразование должна делать отдельная функция) и вычисляет нужное значение.
var
ix, iy: Integer;
function fun(ix, iy: integer): Double;
var
x, y: Double;
begin
//y := (300 - iy) / 30;
//x := ix / 30;
y := (300 - iy) / 1500 + 2 * Pi;
x := ix / 1500 +0.75;
Result := ln(x)*y - sin(y*x);
end;
begin
for iy := 1 to 600 do begin
for ix := 1 to 600 do begin
if (fun(ix, iy) * fun(ix + 1, iy) <= 0)
or (fun(ix, iy) * fun(ix, iy + 1) <= 0)
then
Canvas.Pixels[ix, iy] := clRed;
end;
end;
