Python Zahlen einer Liste addieren um spezifisches Ergebnis zu erhalten?

3 Antworten

Am einfachsten geht das, indem du die Liste zu einem Set umwandelst, und dann nach Duplikaten in einer Liste suchst, in der die benötigten Zahlen stehen:

numbers = [... deine Zahlen ...]
numbers2 = [2021 - x for x in numbers]
result = list(set(numbers).intersection(numbers2))

Laufzeit natürlich O(n).

Oder, alternativ, falls der Overhead von der Set-Konvertierung zu groß ist, könnte man auch einen solchen O(n * log(n)) Algorithmus benutzen:

numbers = [... deine Zahlen ...]

sum = 2021
left = 0; right = len(numbers)-1

numbers.sort()
while left != right:
   if sum - numbers[left] == numbers[right]:
       break
   elif sum - numbers[left] < numbers[right]:
       right-=1
   else:
       left += 1

if left != right:
  print(numbers[left], numbers[right])
else:
  print("Kein geeignetes Paar gefunden")

Zeitkomplexität der Schleife ist n. Zeitkomplexität des sortierens (sort()) ist n * log(n). Folglich ist die Zeitkomplexität der gesamten Funktion das größere von beiden, nämlich n * log(n).

Woher ich das weiß:Hobby – Programmieren ist mein Hobby & Beruf

GuteAntwort2021  16.10.2021, 15:58

Wieso die Zeitkomplexität der gesamten Funktion auf das Sortieren beschränkt ist, erschließt sich mir nicht ganz. Und auch der Vorteil des Sortierens bei dieser speziellen Aufgabe bleibt mir verborgen.

Im Grunde musst du so sogar 2x durch die Liste. Einmal um sie zu sortieren (n * log(n)) und danach musst du trotzdem noch mal potentiell jedes Element mit jedem vergleichen.

Also hast du im Grunde eine Zeitkomplexität erschaffen von:

O((n * log(n)) + (((n-1)² - (n-1)) / 2))

Oder habe ich einen Denkfehler? :)

0
MrAmazing2  16.10.2021, 16:09
@GuteAntwort2021
Wieso die Zeitkomplexität der gesamten Funktion auf das Sortieren beschränkt ist, erschließt sich mir nicht ganz.

O(n) + O(n * log(n)) = O(n * log(n)).

Beim Addieren von Zeitkomplexitäten lässt man die kleineren einfach wegfallen, da sie bei einem hohen n nur einen kleinen unbedeutenden Unterschied machen.

und danach musst du trotzdem noch mal potentiell jedes Element mit jedem vergleichen.

Nein, danach geht man die Liste einfach von Links und Rechts gleichzeitig durch, bis man in der Mitte ist oder ein Match hat. Worst Case O(1*n). Deswegen sortiert man sie vorher, sonst wäre das so nicht möglich.

1
GuteAntwort2021  16.10.2021, 17:57
@MrAmazing2
O(n) + O(n * log(n)) = O(n * log(n)).

Ehm, nein. Diese Gleichung wäre nur erfüllt, wenn O(n) = 0.

Beim Addieren von Zeitkomplexitäten lässt man die kleineren einfach wegfallen, da sie bei einem hohen n nur einen kleinen unbedeutenden Unterschied machen.

Ein Satz den einer meiner Profs gerne von sich gegeben hat: "Da sieht man mal wieder, warum Mathematiker in der Informatik nichts verloren haben." ;-P

Wenn n gegen unendlich geht, dann wird die Teilmenge O(n) natürlich immer kleiner im Verhältnis zur Gesamtmenge.

Aber im Realismus gibt es die Unendlichkeit nicht. Es gibt keine unendlich großen Datensätze. Und nun nimm zwei Algorithmen die das Wetter simulieren sollen. Der eine hat eine Komplexität von O(n) + O(n * log(n)) und der andere von O(n * log(n)) - O(n).

Mit n gegen unendlich wären beide gleichwertig. Im Realismus würde man sich bei 1*10^9 Daten aber mit dem einen Beispiel gegenüber dem anderen 2*10^9 Durchläufe ersparen. Wenn der Computer pro Sekunde 250 000 Datensätze schafft, sprechen wir hier über einen Zeitunterschied von ~2 1/4 Stunden.

Deswegen wäre sowas an meiner Hochschule ein Verbrechen gewesen.

Nein, danach geht man die Liste einfach von Links und Rechts gleichzeitig durch, bis man in der Mitte ist oder ein Match hat.

Okay, wenn man einen Mergesort anwendet, dann dürfte die Variante tatsächlich die optimalste sein. Allerdings hätte er selbst darauf kommen sollen. Oder zumindest sich Gedanken dazu machen, wie man es lösen kann und dann entsprechend die Zeitkomplexität aufstellen. :)

0
MrAmazing2  16.10.2021, 21:24
@GuteAntwort2021

Wir habens in der Hochschule im Fach Algorithmen und Datenstrukturen so gelernt, dass man die kleineren vernachlässigen kann und soll. Wenn ihr als Mathematiker das anders macht, von mir aus, aber ich werds weiterhin so machen wie es mir beigebracht wurde. :D

1
MrAmazing2  16.10.2021, 21:27
@GuteAntwort2021

Eine Komplexität wie

3n^2 + 2

wird bei uns auch einfach zu O(n^2) vereinfacht.

Und ich denke das ist auch richtig so wie wir es gelernt haben, hab noch nie jemanden gesehen der es so macht wie du.

1
GuteAntwort2021  16.10.2021, 21:31
@MrAmazing2
Wenn ihr als Mathematiker

Ich bin keine Mathematiker, ich bin Wirtschaftsinformatiker. Deine Interpretation wäre die mathematische, denn die "unbedeutende" Differenz tritt nur ein, wenn n gegen unendlich geht. Was macht es da schon, ob es nun 1.000.000.001 Jahre zur Berechnung sind oder nur 999.999.999 Jahre. ;-)

In der Praxis spielt das aber eben schon eine Rolle, weil der Zeitfaktor da alles ist! Wenn ein Programm das Wetter von morgen erst übermorgen errechnet hat, macht das schon einen Unterschied, wenn ein anderes es bereits heute Abend errechnet hat, denkst du nicht? ;-)

Eben deswegen sagte mein Prof ja immer, dass Mathematiker in der Informatik nichts verloren haben.

0
KarlRanseierIII  16.10.2021, 21:28

Variante 1 ist defunct, sofern es mehrere Paarungen gibt, weil keine Paare erzeugt werden. Du müßtest daraus dann noch ne sortierte Liste erzeugen, um die Paarungen zu identifizieren.

Ansatz zwei wäre wohl der rationale Weg.

1

Ich denke, es ist essentiell, dass du diese Aufgabe selbst bewerkstelligst. Ich gebe dir nur ein paar Gedankenstützen.

  • Was muss man tun, um jedes Element in einer List mit allen anderen Elementen zu vergleichen?
  • Wie viele Durchläufe sind demnach maximal notwendig? Verallgemeinert ausgedrückt.
  • Unterscheiden sich die minimal notwendigen Durchläufe von den maximal notwendigen? Sprich, was passiert wenn du direkt beim ersten Vergleich zwei Zahlen gefunden hast, die addiert 2021 ergeben?

MrAmazing2  16.10.2021, 15:26

Das wäre ja O(n^2), nicht zu empfehlen

0
GuteAntwort2021  16.10.2021, 15:29
@MrAmazing2

Wenn du das erste Element in der Liste mit allen anderen verglichen hast und du gehst über zum zweiten Element, vergleichst du das dann auch wieder mit dem ersten Element?

Natürlich nicht. Aber diese Denkaufgabe soll er ja selbst übernehmen...

1
GuteAntwort2021  16.10.2021, 21:39
@KarlRanseierIII

Ist das so?

Bei 10 Daten müssten das 100 sein. Wenn er nur noch die Folgeglieder berücksichtigt, sind es O(((n-1)² - (n-1))/2) = 36.

Wenn n gegen Unendlich geht, wird es also schlimmstenfalls 0,5n²

Und ich habe nirgends gesagt, dass man es nicht noch weiter verbessern könnte. Es geht nur darum, dass er selbst auf diese Gedankengänge kommt, denn das ist Zweck der Übung! Es durchzuspielen wie viel Durchgänge man benötigt, um zu bewerten, wie performant ein Algorithmus ist und dieses Denkschema zu verinnerlichen.

0
KarlRanseierIII  16.10.2021, 21:49
@GuteAntwort2021
O(((n-1)² - (n-1))/2)

Ist das so?

Du vergleichst das 1. Element mit allen n-1 anderen, das 2. mit allen n-2 verbleibenden usw.

Das widerholst Du, bis Du beim vorletzen Element angekommen bist.

0
GuteAntwort2021  16.10.2021, 22:07
@KarlRanseierIII
Ist das so?

Fast, es sind ((n² - n)/2), trotzdem wird es bei n gegen unendlich maximal auf 0,5n² hinauslaufen.

0
KarlRanseierIII  16.10.2021, 22:26
@GuteAntwort2021

Und 0.5 n^2 liegt in O(n^2) mit dem konstanten Faktor c=0.5.

Sofern Du mir ein g(n) nennen kannst, sodaß c*n*g(n) 0.5n^2 deckelt, lasse ich mich auf O(n*g(n)) ein.

Bis dahin aber bleibe ich bei g(n)=n.

--------

Bei Vorsortierung klappt O(n log(n)), das betrachten wir aber derzeit ja nicht.

0
GuteAntwort2021  16.10.2021, 22:43
@KarlRanseierIII
Und 0.5 n^2 liegt in O(n^2)

Alles <= n² liegt in O(n²). Also was ist dein Punkt?

Es nimmt quadratisch zu, aber eben halbiert. Das macht es immer noch nicht gut und sollte vermieden werden wenn möglich, aber es diente auch nur als Beispiel dafür, welches Denkschema er braucht.

Darüber hinaus hat der Code nachgewiesen, dass die Formel stimmt. Und diese ist ziemlich eindeutig:

  • best case = 1
  • average case = (n²-n)/4
  • worst case = (n²-n)/2

Von daher bleibe ich bei < 0,5n² bis du mir das Gegenteil bewiesen hast. :)

0
KarlRanseierIII  16.10.2021, 23:07
@GuteAntwort2021

auch alles <= c*n^2 liegt in O(n^2).

Und nein, Du hast lediglich gezeigt, daß Du 0.5n^2 mal eine bestimmte Anweisung ausführst ;-).

Daß dem linearen Faktor keine besondere Beachtung zuteil wird, machen wir nicht aus Spaß.

1
GuteAntwort2021  16.10.2021, 23:47
@KarlRanseierIII

Ich habe gezeigt, dass ich in weniger als 0,5n² Durchläufen jede mögliche Summe mit 2 Zahlenpärchen aus dem Array gebildet habe.

Das war die Aufgabenstellung, wenn ich mich nicht täusche. Und egal wie groß das Array wird, es bleibt trotzdem weniger als 0,5n².

Und selbst wenn n gegen unendlich geht, dann wäre es immer noch mehr als doppelt so wie n². Als Mathematiker mag man da keinen Unterschied sehen, als Informatiker aber schon. Denn für uns ist Zeit etwas reales und daher Unendlichkeit keine Option!

Gut, dass wir drüber gesprochen haben...

0
KarlRanseierIII  17.10.2021, 00:43
@GuteAntwort2021

Die Aufgabe war die Zeitkomplexität des Algorithmus zu bestimmen. Nicht die Anzahl der Durchläufe der inneren Schleife.

Du hast a+b*(n-1)+c*n*(n-1)/2. als Komplexität.

Oder eben O(n^2). Daß wir die 0.5 ignorieren, hat den gleichen Grund aus dem wir a,c und d ignorieren.

Machen wir es exemplarisch:

2^24=n

vs

3*2^27 = n log n

vs

2^48 = n^2

vs

5*2^47 = 0.5 n^2

----------

Bei der Komplexität soll die Güte bewertet werden, lineare Faktoren sind da nunmal eher belanglos. (Nicht umsonst ist es theoretische Informatik).

Übrigens liegt O(n^2) auch in O(0.5n^2).

1
GuteAntwort2021  17.10.2021, 01:10
@KarlRanseierIII
Die Aufgabe war die Zeitkomplexität des Algorithmus zu bestimmen. Nicht die Anzahl der Durchläufe der inneren Schleife.

Wir bestimmen Zeitkomplexität immer mit der Anzahl der Durchläufe bzw. Operationen. O(n*log(n)) steht auch nicht für eine bestimmte Zeit, sondern für eine Anzahl, die man ggf. in Zeit umrechnen kann.

Und ich habe nicht nur die Durchläufe der inneren Schleife dargestellt, sondern die Anzahl der Operationen die es auf diese Art braucht, um jedes einzelne Element innerhalb einer Liste mit allen anderen zu addieren.

Und für dich mag es zwischen kleiner 0,5n² und n² keinen Unterschied geben. Aber wenn du gerne ein praxisrelevantes Beispiel haben möchtest:

Die Formel für den Anhalteweg wie wir in aus der Fahrschule kennen beträgt ca:

Anhalteweg in m = (0,1 * Geschwindigkeit in km/h) * (0,1 Geschwindigkeit in km/h)

Bei einer Geschwindigkeit von 50km/h also ca 25 m.

Nun kommt ein Informatiker daher und programmiert eine KI, wo durch der Anhalteweg nur noch halb so lange dauert.

Wenn dir bei der Geschwindigkeit ein Kind in 15 Meter Entfernung auf die Straße läuft, du also mit KI 2,5 Meter vor dem Kind zum Stehen kommst und ohne KI auch ein Notarzt nicht mehr rechtzeitig ankommt, dann würde ein Satz wie

Aber in der Mathematik macht das aber keinen Unterschied!

eher auf Unverständnis sorgen, denn in der Realität ist der Unterschied in diesem Beispiel kaum drastischer darzustellen!

Daher erneut: Nur Mathematiker vernachlässigen Faktoren mit der Begründung, dass ihr Einfluss in Richtung Unendlichkeit schwindet! Die Unendlichkeit hat nichts mit der Realität zu tun...!

0
KarlRanseierIII  17.10.2021, 03:07
@GuteAntwort2021

Ich sehe schon, das bringt nichts.

Wir Informatiker bestimmen die Zeitkomplexität von Algorithmen indem wir uns die benötigten Rechenschritte anschauen, deren Komplexität wir wiederum nicht kennen. Vielmehr interessiert uns die Größenordnung des Wachstums in der Eingabegröße und dabei spielen lineare Faktoren eben keine Rolle. Punkt.

1
MrAmazing2  17.10.2021, 04:04
@GuteAntwort2021
Nur Mathematiker vernachlässigen Faktoren mit der Begründung, dass ihr Einfluss in Richtung Unendlichkeit schwindet!

Nein, auch jeder Informatik-Professor, der das Thema Zeitkomplexität unterrichtet, wird dir da dasselbe sagen wie ich und KarlRanseierIII. Das machen nicht nur Mathematiker so. Dir wurde da wohl einfach Quatsch beigebracht, oder du hast nicht aufgepasst.

1
from itertools import combinations as comb

numbers = [1, 2, 3]
summed = 6

result = [(a, b) for a, b in comb(numbers) if a * b == summed]

print(result) # (2, 6)

Das liefert dir alle Zweier-Zahlenkombinationen aus der Liste "numbers", die in der Summe "6" ergeben.

In deinem Falle musst du also einfach nur die Variablen "numbers" und "summed" anpassen. In "result" stehen dann alle entspr. Kombinationen, und zwar ohne Doubletten! :)

Woher ich das weiß:Studium / Ausbildung