Array

Ich habe für’s Studium folgende Programmieraufgabe zu lösen und benötige einen Denkanstoß, wie ich ein bestimmtes Problem lösen kann.

Ich habe folgendes Array als Vorgabe:
34 255 255 56
127 204 11 34
123 98 127 34
34 34 127 17

Ich soll eine Tabelle ausgeben, aus der einerseits der Wert, andererseits auch die Häufigkeit des Werts ausgegeben wird. Ergebnis also so:
Wert / Häufigkeit
34 / 5
255 / 2
56 / 1
usw.

Grundsätzlich habe ich das Programm fertig, ABER: meine jeweiligen for-Schleifen führen dazu, dass auch Werte ausgegeben werden, die bereits angezeigt wurden:
Wert / Häufigkeit
34 / 5
255 / 2
255 / 2
56 / 1
usw.

Ich habe keine Idee, wie ich das lösen kann. Bitte helft mir auf die Sprünge, wie ich an die Lösung herangehen soll.

{
  public static void main (String args[])
  {
    int[][]bild = {{34,255,255,56},{127,204,11,34},{123,98,127,34},{34,34,127,17}};
    int farbwert, anzahl;
    System.out.println("Wert	Anzahl");
    for (int i = 0; i < bild.length; i++)
    {
      for (int j = 0; j < bild**.length; j++)
      {
        farbwert = bild**[j];
    	anzahl = 0;
        for (int x = 0; x < bild.length; x++)
        {
          for (int y = 0; y < bild[x].length; y++)
          {
            if (farbwert == bild[x][y]) anzahl = anzahl + 1;
          }
        }
    	  System.out.println (farbwert + "	" + anzahl);
      }
    }
  }
}```

So zum beispiel:

Verdeckter Inhalt
[spoiler]```import java.util.ArrayList;

public class Test {
public static void main (String args[]) {
int[][]bild = {
{34,255,255,56},
{127,204,11,34},
{123,98,127,34},
{34,34,127,17}
};
int farbwert, anzahl;
ArrayList gehabt = new ArrayList();
System.out.println(“Wert Anzahl”);
for(int i = 0; i < bild.length; i++) {
for(int j = 0; j < bild**.length; j++){
farbwert = bild**[j];
anzahl = 0;
if(!gehabt.contains(farbwert)){
gehabt.add(farbwert);
for(int x = 0; x < bild.length; x++){
for(int y = 0; y < bild[x].length; y++){
if (farbwert == bild[x][y]){
anzahl = anzahl + 1;
}
}
}
System.out.println (farbwert + " " + anzahl);
}
}
}
}
}```[/spoiler]

Wäre zwar bei grösseren Mengen wahrscheinlich unperformant, (Der beitrag war übrigens von mir) Aber bei einem kleinen Bild geht das glaube ich.

Wobei ich mich gerade Frage warum meine lösung eigentlich funktioniert…

Effizienter gehts so:

Code
[spoiler]public static void countColors(int[][] picture) { int[] amount = new int[256]; for (int[] row : picture) for (int pixel : row) amount[pixel & 0xFF]++; for (int i = 0; i < amount.length; i++) if (amount** > 0) System.out.println(i + ": " + amount**); }[/spoiler]

Voraussetzung ist aber, dass nur 256 Farben im Bild enthalten sind. Sicherheitshalber habe ich daher & 0xFF mit eingebaut, um eine ArrayIndexOutOfBoundsException zu vermeiden.

Um dir nicht nur eine „Friss und Stirb“-Lösung hinzuwerfen: das Problem ist, dass du für jedes Pixel erneut das gesamte Bild durchzählst. Das ist nicht nur schrecklich ineffizient, sondern sorgt, wie dir ja bereits aufgefallen ist, dafür, dass du für jedes Pixel eine Ausgabe erzeugst.

Die Lösung heißt also: iteriere nur einmal über alle Pixel und speichere für jeden Farbwert die Häufigkeit in einem Array oder einer Map.
Dann iterierst du noch einmal über deine gespeicherten Werte und gibst die Häufigkeiten aus.

Das funktioniert, weil du in dem Array die bereits vorgefundenen Farbwerte speicherst. Wenn du die Anzahl für einen bestimmten Farbwert schon ermittelt hast, dann überspringst du das Pixel einfach.

So hätte ich das/es gemacht: ```/*

  • To change this template, choose Tools | Templates
  • and open the template in the editor.
    */
    package javaapplication1;

import java.util.Arrays;
import java.util.Comparator;
import java.util.Random;

/**

  • @author ikwudls
    */
    public class ZähleFürMichComp {

    private int[] z = new int[256];

    public void zähle(int i) {
    if (i < 0) {
    throw new IllegalArgumentException("" + i);
    }
    if (i >= z.length) {
    int tmp = z.length;
    do {
    tmp = 1.5;
    System.out.println("tmp = " + tmp);
    } while (i >= tmp);
    int[] y = new int[tmp];
    System.arraycopy(z, 0, y, 0, z.length);
    z = y;
    }
    z
    *++;
    }

    public int[][] ergebnis() {
    int[][] res = new int[z.length][2];
    for (int i = 0; i < z.length; i++) {
    res**[0] = i;
    res**[1] = z**;
    }
    Arrays.sort(res, new Comparator<int[]>() {
    @Override
    public int compare(int[] o1, int[] o2) {
    return -((Integer) o1[1]).compareTo(o2[1]);
    }
    });
    return res;
    }

    public static void drucke(int[][] res) {
    int i = (int) Math.ceil(Math.log10(res.length)) + 1;
    for (int[] is : res) {
    System.out.format("% “+i+“d”+”% "+i+“d%n”, is[0], is[1]);
    }
    }

    public static void main(String[] args) {
    ZähleFürMichComp zfmc = new ZähleFürMichComp();
    Random r = new Random();
    for (int i = 0; i < 100; i++) {
    zfmc.zähle(r.nextInt(10));
    }
    javaapplication1.ZähleFürMichComp.drucke(zfmc.ergebnis());
    }
    }```

Jetzt lässt sich darüber streiten, ob ein Konstruktor sinnvoll wäre, ob der Faktor 1,5 sinnvoll ist oder was bei negativen int’s geschehen könnte.

Hier noch eine weitere Lösung, mit einer Iteration durch das Array:


	
	public static void main(String[] args) {
		int[][] bild = { { 34, 255, 255, 56 }, { 127, 204, 11, 34 },
				{ 123, 98, 127, 34 }, { 34, 34, 127, 17 } };
		
		for(int[] arr : bild){
			for(int zahl : arr){
				zaehle(zahl);
			}
		}
		gebeAus();
	}
	
	private static void gebeAus() {
		System.out.println("Wert	Anzahl");
		for(Entry<Integer, Integer> entry : zaehleMap.entrySet()){
			System.out.println(entry.getKey()+"	"+entry.getValue());
		}
	}

	private static void zaehle(int zahl){
		Integer anzahl;
		if((anzahl = zaehleMap.get(zahl)) != null){
			zaehleMap.put(zahl, ++anzahl);
		}else{
			zaehleMap.put(zahl, 1);
		}
	}```

Konform gehend mit dem Titel, auch nur zwei Stichworte: http://de.wikipedia.org/wiki/Häufigkeitsverteilung bzw. http://de.wikipedia.org/wiki/Histogramm

die Lösung von @pl4gu33 hatte ich auch im Kopf, als ich meinen Beitrag geschrieben habe (die Lösung mit der Map). Allerdings habe ich da einen kleinen Kritikpunkt: die Zeilen 25 und 26 finde ich zu “unübersichtlich”.
Wenn man schon auf eine Lösung mit Collections setzt, dann kommt es auf das letzte Quentchen Performance wohl nicht mehr an, sodass man das auch lesbarer formulieren kann:

            anzahl = zaehleMap.get(zahl);
            zaehleMap.put(zahl, anzahl + 1);```

Das funktioniert, weil du in dem Array die bereits vorgefundenen Farbwerte speicherst. Wenn du die Anzahl für einen bestimmten Farbwert schon ermittelt hast, dann überspringst du das Pixel einfach.

Ich meinte eignetlich das ich ja eigentlich nicht mehr hochzähle, wenn die zahl schon im array ist… :eek:

Mein Reden…

Das wiederum liegt daran, dass es die innere Schleife gibt, in der die eigentliche Arbeit (unabhängig von irgendwelchen Bedingungen) gemacht wird.

Danke einmal für die vielen Antworten. Ich werde versuchen, die Lösung umzusetzen. Werde dann den Code posten.

uups - war wohl nicht eingeloggt beim posten :slight_smile:

[QUOTE=cmrudolph]die Lösung von @pl4gu33 hatte ich auch im Kopf, als ich meinen Beitrag geschrieben habe (die Lösung mit der Map). Allerdings habe ich da einen kleinen Kritikpunkt: die Zeilen 25 und 26 finde ich zu “unübersichtlich”.
Wenn man schon auf eine Lösung mit Collections setzt, dann kommt es auf das letzte Quentchen Performance wohl nicht mehr an, sodass man das auch lesbarer formulieren kann:

            anzahl = zaehleMap.get(zahl);
            zaehleMap.put(zahl, anzahl + 1);```[/QUOTE]

Bei sowas kräuseln sich mit immer die Fußnägel. Ist sicher für eine Aufgabe eine gute Lösungsmöglichkeit, aber im richtigen Leben schreit das geradezu nach einem Multiset (z.B. von Guava).

Habe mir kurz die Interfacedoku vom Multiset angeschaut. Hatte das zwar schonmal durchgelesen, aber noch nicht verwendet und daher wieder vergessen.
Man kann immer nur das benutzen, was man kennt :wink:
Insofern: danke nochmal für den Hinweis!

Dann hier nochmal “in schön”:


import com.google.common.collect.HashMultiset;
import com.google.common.collect.Multiset;

class Bildverarbeitung {
    public static void main(String args[]) {
        int[][] bild = {{34, 255, 255, 56}, {127, 204, 11, 34}, {123, 98, 127, 34}, {34, 34, 127, 17}};

        System.out.println("Wert	Anzahl");

        Multiset<Integer> colorCounts = countColors(bild);
        for (Multiset.Entry<Integer> colorEntry : colorCounts.entrySet())
            System.out.println(colorEntry.getElement() + "	" + colorEntry.getCount());
    }

    private static Multiset<Integer> countColors(int[][] picture) {
        Multiset<Integer> result = HashMultiset.create();
        for (int[] row : picture)
            for (int pixel : row)
                result.add(pixel);

        return result;
    }
}```

Wenn die Ausgabe nach Farbwerten sortiert erfolgen soll, muss einfach nur in Zeile 18 `TreeMultiset.create();` aufgerufen werden.

@cmrudolph ich möchte dich nur darauf hinweisen, dass als Package Name package net.byte_welt besser eignet. Siehe http://docs.oracle.com/javase/tutorial/java/package/namingpkgs.html