Optimierung von Funktionen

OP #7460880
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?

Moderator #7460902
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).

#7464953
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

#7464977
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
}
#7464980
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.

#7464981
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
}
#7465030
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...

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren