Programm sehr langsam, liegt es am new?

Also, ich wollte nur mal schnell was ausprobieren, nämlich eine Collage zu erstellen mit Bildern beliebiger Weite und Höhe, so dass die Collage die kleinsten Ausmaße hat… Dafür schaue ich jede Sekunde einmal im Clipboard nach, ob dort ein neues Image ist. Wenn ja, wird es der Collage hinzugefügt. Die Collage soll die Images in einem 15x15-Raster platzieren. Ein Platz hat dabei immer die Ausmaße 100x100 Pixel. Mir ist aufgefallen, dass die Collage für JEDES Image JEDES MAL alle 225 Möglichkeiten ausprobiert, lässt sich das nicht vereinfachen? Bei drei Images ist es langsam, bei vier Images ist es seeehr langsam und bei fünf Images hält es nicht mehr an. Vielen Dank fürs Lesen.

import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Toolkit;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.StringSelection;
import java.awt.datatransfer.Transferable;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Comparator;

import javax.imageio.ImageIO;

public class PlaceClipboardCollage {

	public static Image getImageFromClipboard() throws Exception {
		Transferable transferable = Toolkit.getDefaultToolkit().getSystemClipboard().getContents(null);
		if (transferable != null && transferable.isDataFlavorSupported(DataFlavor.imageFlavor)) {
			return (Image) transferable.getTransferData(DataFlavor.imageFlavor);
		} else {
			return null;
		}
	}

	public static class GridImage {
		Image image;
		int x, y;
		int w, h;
	}

	public static boolean place(boolean[][] grid, int x, int y, int w, int h, boolean val) {
		for (int i = 0; i < w; i++) {
			for (int j = 0; j < h; j++) {
				if (i + x >= grid.length || j + y >= grid.length || grid[i + x][j + y] == val) {
					return false;
				}
			}
		}
		for (int i = 0; i < w; i++) {
			for (int j = 0; j < h; j++) {
				grid[i + x][j + y] = val;
			}
		}
		return true;
	}

	public static ArrayList<GridImage> bestGridImages = null;

	public static Comparator<ArrayList<GridImage>> comparator = new Comparator<ArrayList<GridImage>>() {
		@Override
		public int compare(ArrayList<GridImage> o1, ArrayList<GridImage> o2) {
			int wmax1 = 0;
			int hmax1 = 0;
			int wmax2 = 0;
			int hmax2 = 0;
			for (GridImage gridImage : o1) {
				if (gridImage.x + gridImage.w > wmax1) {
					wmax1 = gridImage.x + gridImage.w;
				}
				if (gridImage.y + gridImage.h > hmax1) {
					hmax1 = gridImage.y + gridImage.h;
				}
			}
			for (GridImage gridImage : o2) {
				if (gridImage.x + gridImage.w > wmax2) {
					wmax2 = gridImage.x + gridImage.w;
				}
				if (gridImage.y + gridImage.h > hmax2) {
					hmax2 = gridImage.y + gridImage.h;
				}
			}
			return Math.max(wmax1, hmax1) - Math.max(wmax2, hmax2);
		}
	};

	public static void placeAll(ArrayList<Image> images, ArrayList<GridImage> gridImages, boolean[][] grid, int i) {
		if (i == images.size()) {
			if (bestGridImages == null || comparator.compare(gridImages, bestGridImages) < 0) {
				bestGridImages = new ArrayList<>(gridImages);
			}
			return;
		}
		Image image = images.get(i);
		int w = (int) Math.ceil(image.getWidth(null) / 100.0);
		int h = (int) Math.ceil(image.getHeight(null) / 100.0);
		for (int x = 0; x < grid.length; x++) {
			for (int y = 0; y < grid.length; y++) {
				if (place(grid, x, y, w, h, true)) {
					GridImage gi = new GridImage();
					gi.image = image;
					gi.x = x;
					gi.y = y;
					gi.w = w;
					gi.h = h;
					gridImages.add(gi);
					placeAll(images, gridImages, grid, i + 1);
					gridImages.remove(gridImages.size() - 1);
					place(grid, x, y, w, h, false);
				}
			}
		}
	}

	public static void saveImages(ArrayList<Image> images) {
		placeAll(images, new ArrayList<>(), new boolean[15][15], 0);

		// Create a buffered image with transparency
		BufferedImage bimage = new BufferedImage(15 * 100, 15 * 100, BufferedImage.TYPE_INT_RGB);
		Graphics2D g2d = bimage.createGraphics();
		for (GridImage gridImage : bestGridImages) {
			g2d.drawImage(gridImage.image, gridImage.x * 100, gridImage.y * 100, null);
		}
		g2d.dispose();

		bestGridImages = null;

		try {
			ImageIO.write(bimage, "PNG", new File("test-image-" + System.currentTimeMillis() + ".png"));
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	public static void main(String[] args) throws InterruptedException {
		ArrayList<Image> images = new ArrayList<>();
		while (true) {
			try {
				Image imageFromClipboard = getImageFromClipboard();
				if (imageFromClipboard != null) {
					System.out.println("Found image.");
					images.add(imageFromClipboard);
					StringSelection stringSelection = new StringSelection("");
					Toolkit.getDefaultToolkit().getSystemClipboard().setContents(stringSelection, null);
					saveImages(images);
				}
			} catch (Exception e) {
			}
			Thread.sleep(1000);
		}
	}

}

Also, wenn ich mich nicht verrechnen tu, würde es bei
drei Images = 11390625
und bei
vier Images = 2562890625
mal new aufrufen. Das ist glaube ich zu viel. :confused:

Hab’ gerade mal einen Counter eingefügt, der zählt, wie oft new GridImage ausgeführt wird:

Found image.
1 CRAP 196
Found image.
2 CRAP 37012
Found image.
3 CRAP 6657652
Found image.
4 CRAP 1145301028

Offenbar sind die vielen new-Aufrufe wirklich problematisch. Du könntest versuchen, new durch eine andere Funktion zu ersetzen. Oder schreib’ das ganze in C++. C++ ist schneller als Java.

Danke. Könnte man nicht auch eine Art „Top-Down-Pruning“ hinzufügen? Idee: Wenn ein zu untersuchender „Teilbaum“ das Ganze nicht besser machen kann, könnte dieser Teilbaum beschnitten werden, also nicht alles möglichen Lösungen müssen in Betracht gezogen werden?

Oder ist ein anderer grober Schnitzer dabei?

Oh. Jemand der Ironie und Sarkasmus nicht versteht. Na, dann werde ich in Zukunft ganz bestimmt gar nie-nie-nicht noch einmal irgendwas ironisches oder sarkastisches schreiben.

Der Ansatz ist… crap. Das hat nix mit „new“ zu tun, sondern dass du einfach Code hinschreibst, und aus irgendeinem unerklärlichen Grund galubst, der Computer würde daraufhin irgendwas cooles oder sinnvolles machen.

Helf mir doch lieber beim Pruning, anstatt einen destruktiven Kommentar abzugeben.

Warum sei der Ansatz falsch/crap? Das ist ein normales Backtracking.

:man_facepalming:

Es ist sehr simple…

	public static void placeAll(ArrayList<Image> images, ArrayList<GridImage> gridImages, boolean[][] grid, int i) {
		if (i == images.size()) {
			if (bestGridImages == null || comparator.compare(gridImages, bestGridImages) < 0) {
				bestGridImages = new ArrayList<>(gridImages);
			}
			return;
		}
		if (bestGridImages != null && comparator.compare(gridImages, bestGridImages) >= 0) {
			return;
		}
		Image image = images.get(i);
		int w = (int) Math.ceil(image.getWidth(null) / 100.0);
		int h = (int) Math.ceil(image.getHeight(null) / 100.0);
		for (int x = 0; x < grid.length; x++) {
			for (int y = 0; y < grid.length; y++) {
				if (place(grid, x, y, w, h, true)) {
					GridImage gi = new GridImage();
					gi.image = image;
					gi.x = x;
					gi.y = y;
					gi.w = w;
					gi.h = h;
					gridImages.add(gi);
					placeAll(images, gridImages, grid, i + 1);
					gridImages.remove(gridImages.size() - 1);
					place(grid, x, y, w, h, false);
				}
			}
		}
	}

Das Pruning steht in Zeile 8 bis 10. Jetzt können problemlos beliebig viele Bilder zusammengestellt werden:

image

@Marco13 Könnte man die Korrektheit/Optimalität des Algorithmus zeigen?

Dass das Backtracking ist, konnte man erkennen, wenn man den Code gelesen hat. Dass Backracking ~„im einfachsten Fall, meistens“ exponentiell aufwändiger wird, ist bekannt. Dass gutes Pruning der Schlüssel ist, um es noch sinnvoll anwendbar zu machen, ist auch klar (neben Heuristiken, irgendwas mit Greedy, oder indem man akzeptiert, dass man nicht „die optimale“, sondern ggf. nur eine „sehr gute“ Lösung findet). Aber wenn du mal wieder einfach irgendwelchen umokmmentierten Code, ohne konkretes Ziel, ohne z.B. mal „Backtracking“ zu erwähnen, ohne auf technischer Ebene überhaupt erkennbares Optimierungskriterium, und garniert mit solchem absurden Stuss wie „liegt es am new?“ postest, kannst du (allgemein, aber speziell von jemandem, der sich viel zu viel über deine Eskapaden hier ärgern muss) nichts hilfreiches erwarten.

Leider ist die Frage doch noch nicht beantwortet. Wenn ich ca. 10 Bilder hinzugefügt habe, und dann ein etwas größeres hinzufügen möchte, hält das Programm nicht mehr an:

import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Toolkit;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.StringSelection;
import java.awt.datatransfer.Transferable;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.function.Function;

import javax.imageio.ImageIO;

public class PlaceClipboardCollage {

	public static Image getImageFromClipboard() throws Exception {
		Transferable transferable = Toolkit.getDefaultToolkit().getSystemClipboard().getContents(null);
		if (transferable != null && transferable.isDataFlavorSupported(DataFlavor.imageFlavor)) {
			return (Image) transferable.getTransferData(DataFlavor.imageFlavor);
		} else {
			return null;
		}
	}

	public static class GridImage {
		Image image;
		int x, y;
		int w, h;

		public GridImage(Image i, int x, int y, int w, int h) {
			image = i;
			this.x = x;
			this.y = y;
			this.w = w;
			this.h = h;
		}

		public int getAbsX() {
			double w2 = (w - image.getWidth(null) / 100.0) / 2.0 * 100.0;
			return x * 100 + (int) w2;
		}

		public int getAbsY() {
			double h2 = (h - image.getHeight(null) / 100.0) / 2.0 * 100.0;
			return y * 100 + (int) h2;
		}
	}

	public static boolean setGrid(boolean[][] grid, int x, int y, int w, int h, boolean val) {
		for (int i = 0; i < w; i++) {
			for (int j = 0; j < h; j++) {
				if (i + x >= grid.length || j + y >= grid.length || grid[i + x][j + y] == val) {
					return false;
				}
			}
		}
		for (int i = 0; i < w; i++) {
			for (int j = 0; j < h; j++) {
				grid[i + x][j + y] = val;
			}
		}
		return true;
	}

	public static ArrayList<GridImage> bestGridImages = null;

	public static Comparator<ArrayList<GridImage>> comparator = new Comparator<ArrayList<GridImage>>() {
		@Override
		public int compare(ArrayList<GridImage> o1, ArrayList<GridImage> o2) {
			int wmax1 = 0;
			int hmax1 = 0;
			int wmax2 = 0;
			int hmax2 = 0;
			for (GridImage gridImage : o1) {
				if (gridImage.x + gridImage.w > wmax1) {
					wmax1 = gridImage.x + gridImage.w;
				}
				if (gridImage.y + gridImage.h > hmax1) {
					hmax1 = gridImage.y + gridImage.h;
				}
			}
			for (GridImage gridImage : o2) {
				if (gridImage.x + gridImage.w > wmax2) {
					wmax2 = gridImage.x + gridImage.w;
				}
				if (gridImage.y + gridImage.h > hmax2) {
					hmax2 = gridImage.y + gridImage.h;
				}
			}
			return Math.max(wmax1, hmax1) - Math.max(wmax2, hmax2);
		}
	};

	public static Function<ArrayList<GridImage>, Integer> getMaxW = (l) -> l.stream()
			.mapToInt(e -> e.x * 100 + e.w * 100).max().getAsInt();
	public static Function<ArrayList<GridImage>, Integer> getMaxH = (l) -> l.stream()
			.mapToInt(e -> e.y * 100 + e.h * 100).max().getAsInt();

	public static void backtrackingAll(ArrayList<Image> images, ArrayList<GridImage> gridImages, boolean[][] grid,
			int i) {
		if (i == images.size()) {
			if (bestGridImages == null || comparator.compare(gridImages, bestGridImages) < 0) {
				bestGridImages = new ArrayList<>(gridImages);
			}
			return;
		}
		if (bestGridImages != null && comparator.compare(gridImages, bestGridImages) >= 0) {
			return;
		}
		Image image = images.get(i);
		int w = (int) Math.ceil(image.getWidth(null) / 100.0);
		int h = (int) Math.ceil(image.getHeight(null) / 100.0);
		for (int x = 0; x < grid.length; x++) {
			for (int y = 0; y < grid.length; y++) {
				if (setGrid(grid, x, y, w, h, true)) {
					GridImage gi = new GridImage(image, x, y, w, h);
					gridImages.add(gi);
					backtrackingAll(images, gridImages, grid, i + 1);
					gridImages.remove(gridImages.size() - 1);
					setGrid(grid, x, y, w, h, false);
				}
			}
		}
	}

	public static void saveImages(ArrayList<Image> images) {
		backtrackingAll(images, new ArrayList<>(), new boolean[25][25], 0);

		// Create a buffered image without transparency
		BufferedImage bimage = new BufferedImage(getMaxW.apply(bestGridImages), getMaxH.apply(bestGridImages),
				BufferedImage.TYPE_INT_RGB);
		Graphics2D g2d = bimage.createGraphics();
		for (GridImage gridImage : bestGridImages) {
			g2d.drawImage(gridImage.image, gridImage.getAbsX(), gridImage.getAbsY(), null);
		}
		g2d.dispose();

		bestGridImages = null;

		try {
			ImageIO.write(bimage, "PNG", new File("test-image-" + System.currentTimeMillis() + ".png"));
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	public static void main(String[] args) throws InterruptedException {
		ArrayList<Image> images = new ArrayList<>();
		while (true) {
			try {
				Image imageFromClipboard = getImageFromClipboard();
				if (imageFromClipboard != null) {
					System.out.println("Found image.");
					images.add(imageFromClipboard);
					StringSelection stringSelection = new StringSelection("");
					Toolkit.getDefaultToolkit().getSystemClipboard().setContents(stringSelection, null);
					saveImages(images);
				}
			} catch (Exception e) {
			}
			Thread.sleep(1000);
		}
	}

}

Waran liegt das? Ich muss ca. 15 Bilder hinzufügen. Also, mir fällt keine Optimierungsmöglichkeit mehr ein. :confused:

Wenn ich die Eingabe Images sortiere, tritt das nicht mehr Anhalten erst bei ca. 16 Images auf, wobei ich mir aber nicht sicher bin, ob dann kein Platz mehr ist.

@Marco13 Kannst du das Thema bitte löschen? Ich glaub nicht, dass noch input kommt.