Forum: PC-Programmierung Optimierung von Funktionen


von Chandler B. (chandler)


Lesenswert?

Hallo,

ich habe eine Funktion (in C) geschrieben, welche mir aus einem Array 
alle doppelten Werte Entfernt
1
int *distinct(const int *values, size_t count, size_t *pResultCount)
2
{
3
  int* ptr;
4
  bool valueExisting;
5
  *pResultCount = 0;
6
  ptr = (int*)calloc(count, sizeof(int));
7
  ptr[*pResultCount] = values[0];
8
  (*pResultCount)++;
9
  if (count >= 1)
10
  {
11
    for(size_t x=1; x<count; x++)
12
    {
13
      valueExisting = false;
14
      for (size_t y=0; y<*pResultCount; y++)
15
      {
16
        if(values[x] == ptr[y])
17
        {
18
          valueExisting = true;
19
        }
20
      }
21
      if (false == valueExisting)
22
      {
23
        ptr[*pResultCount] = values[x];
24
        (*pResultCount)++;
25
      }
26
    }
27
  }
28
  
29
  return ptr;
30
}
1
values: 1 1 1 2 3 0
2
count: 6
3
4
-> pResultCount: 4
5
-> return: 1 2 3 0

das ganze funktioniert auch.

Aber wie kann ich jetzt vorgehen um die Funktion zu optimieren?

: Bearbeitet durch User
von Yalu X. (yalu) (Moderator)


Lesenswert?

Der Rechenaufwand deines Algorithmus für ein Array mit n Elementen ist
O(n²). Wenn du das Array erst in O(n·log(n)) sortierst, geht das
anschließende Filtern der Duplikate in O(n) und damit der gesamte
Algorithmus in O(n·log(n)+n) = O(n·log(n)).

Zumindest für sehr große Arrays wird das die Rechenzeit drastisch
reduzieren.

Edit:

Mit einer Hashtabelle geht es evtl. noch schneller, unter günstigen
Bedingungen in O(n).

Wenn der Wertebereich der Array-Elemente beschränkt und nicht allzu groß
ist, kannst du dabei die Elemente selber als Hash-Werte nehmen, dann
geht es sogar noch schneller und garantiert in O(n).

: Bearbeitet durch Moderator
von Motopick (motopick)


Lesenswert?

> Aber wie kann ich jetzt vorgehen um die Funktion zu optimieren?

Geeignete Hardware verwenden. Die schafft das in O(1).
Vom Aufwand war ja keine Rede. :)

von Rolf M. (rmagnus)


Lesenswert?

Chandler B. schrieb:
> for (size_t y=0; y<*pResultCount; y++)
>       {
>         if(values[x] == ptr[y])
>         {
>           valueExisting = true;
>         }
>       }

Eine einfache Optimierung: Nach der Zuweisung ein break einfügen, damit 
die innere Schleife nicht immer komplett bis zum Ende weitersucht, 
obwohl der Wert schon gefunden wurde.

von Rbx (rcx)


Lesenswert?

Habe ich aus der Suchmaschine:
"The compiler unrolls loops automatically at -O3"

Chandler B. schrieb:
> if (count >= 1)

Sollte man sich bei C-Programmierung lieber gleich abgewöhnen.

Asm-Unterfunktionen waren früher einfacher - wenn man aber die Hardware 
kennt, kann sich an Compilerfeinheiten orientieren:
https://dmalcolm.fedorapeople.org/gcc/2015-08-31/rst-experiment/how-to-use-inline-assembly-language-in-c-code.html

von Klaus (feelfree)


Lesenswert?

Auch wenn es nicht gefragt war, in C++ ist es ein Dreizeiler, der 
allerdings das Ergebnis dann gleich noch sortiert zurückgibt.
1
void distinct(std::vector<int> &v)
2
{ 
3
    std::sort(v.begin(), v.end());
4
    auto last = std::unique(v.begin(), v.end());
5
    v.erase(last, v.end());
6
}

von Wilhelm M. (wimalopaan)


Lesenswert?

Klaus schrieb:
> Auch wenn es nicht gefragt war, in C++ ist es ein Dreizeiler, der
> allerdings das Ergebnis dann gleich noch sortiert zurückgibt.
>
>
1
> void distinct(std::vector<int> &v)
2
> {
3
>     std::sort(v.begin(), v.end());
4
>     auto last = std::unique(v.begin(), v.end());
5
>     v.erase(last, v.end());
6
> }
7
>

Wobei Du auch auf das erase verzichten kannst und verwendest stattdessen 
den Ende-Iterator. Das tatsächliche Entfernen ist meisten unnötig.

von Gerald K. (geku)


Lesenswert?

1
void removeDuplicates(int *values, size_t *pResultCount) {
2
    if (*pResultCount <= 1) {
3
        // Keine oder nur ein Element im Array, daher keine Duplikate möglich
4
        return;
5
    }
6
7
    size_t uniqueIndex = 1;
8
9
    for (size_t current = 1; current < *pResultCount; ++current) {
10
        bool valueExisting = false;
11
        for (size_t prev = 0; prev < uniqueIndex; ++prev) {
12
            if (values[current] == values[prev]) {
13
                valueExisting = true;
14
                break;
15
            }
16
        }
17
18
        if (!valueExisting) {
19
            values[uniqueIndex++] = values[current];
20
        }
21
    }
22
23
    *pResultCount = uniqueIndex;
24
}
25
26
int main() {
27
    int values[] = {1, 1, 1, 2, 3, 0, 2};
28
    size_t count = sizeof(values) / sizeof(values[0]);
29
30
    removeDuplicates(values, &count);
31
32
    // Hier können Sie auf die eindeutigen Werte in 'values' zugreifen
33
    // und die Anzahl der eindeutigen Werte ist in 'count' enthalten.
34
    return 0;
35
}

von Peter M. (r2d3)


Lesenswert?

Hallo Yalu,

Yalu X. schrieb:
> Mit einer Hashtabelle geht es evtl. noch schneller, unter günstigen
> Bedingungen in O(n).

ohne weitere Erklärung ist diese Behauptung verwirrend.

Vermutlich willst Du alle Elemente der Reihe nach in einem Bitfeld 
aushaken, dass so groß ist wie der Wertebereich der Hashs, die sich auch 
für Dich in O(1) berechnen lassen müssen.

Dann ist der Bearbeitungsaufwand minimal [ O(1) ], weil Du für jedes 
Element nur nachprüfen musst, ob seine Position im Bitfeld schon gesetzt 
ist.

Du tauschst dann allerdings Rechenzeit gegen Speicherplatz ein.
Allerdings gibt es auch keine Gewähr dafür, dass Dein Bitfeld in einem 
Stück im Speicher liegt und der Aushakvorgang nur O(1) dauert.

Die O-Notation zielt ja auf große n ab.

Von daher bezweifele ich jede Laufzeitangabe, die schneller ist als 
O(n*log(n)).

Der günstigste Fall ist ja eigentlich nicht der interessanteste, sondern 
eher der schlechteste Fall oder der mittlere...

: Bearbeitet durch User
von Oliver S. (oliverso)


Lesenswert?

Rbx schrieb:
> Chandler B. schrieb:
>> if (count >= 1)
>
> Sollte man sich bei C-Programmierung lieber gleich abgewöhnen.

Was wäre deine Alternative?

Oliver

von Apollo M. (Firma: @home) (majortom)


Lesenswert?

Oliver S. schrieb:
> Was wäre deine Alternative?

if (count) ...

Aber nur wichtig zur Vermeidung von Augenkrebs, da der Compiler es 
sowieso wegoptimiert.

von Oliver S. (oliverso)


Lesenswert?

Apollo M. schrieb:
> Oliver S. schrieb:
>> Was wäre deine Alternative?
>
> if (count) ...
>
> Aber nur wichtig zur Vermeidung von Augenkrebs, da der Compiler es
> sowieso wegoptimiert.

Du weißt das, aber ob Chandler das auch weiß?

Oliver

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.