Dass ich erst so spät antworten konnte, liegt daran, dass ich mit diesem Kram, mangels klarer und vollständiger Definitionen (nun ist es besser, aber es fehlt noch etwas) schon viel Zeit verbraten und trotzdem etwas mit C++ gebastelt habe (meine Schuld, es war sozusagen eine Zwangshandlung), unter der Gefahr, dass ich wegen Definitionslücken (kann ich nichts dafür) in falsche Richtungen galoppiert sein könnte (meine Schuld, das Pferd zu reiten ).
Zitat von
Multithread
hmm, Datenstruktur in Eindeutig?
JSON "export" von Theoretischen Daten:
[...]
Das ist schon viel besser, und danke dafür, aber es war auch nötig, denn JSON (worauf ich nicht gekommen war) ist so offen ausgelegt, dass man nur werten kann, was da ist und nicht, was noch kommen könnte. Ich habe die unterschiedlich langen Listen erst mal für bare Münze genommen (und nicht als Abkürzung deinerseits) und auch eine Implementierung, die damit umgehen kann, vorgenommen. Also frage ich doch einfach mal:
Ist das eine Anforderung?
Wie ist sie begründet?
Wie soll mit den Längenunterschieden umgegangen werden?
Meine Arbeitshypothese (wegen Längenunterschieden) fügt keine neutralen Elemente hinzu, denn dazu müsste man erst mal wissen, wie sie sich neutral auswirken (0 für Summen und 1 für Produkte? Oder 100 für 100 % oder nach einer obskuren Gewichtung, die wir hier nicht sehen können? Oder nach einer praktisch unlösbaren Gleichung?) und ob sie neutral sein sollen. Es gibt also nur solche Kombinationen, die über alle R gehen, wobei aus jedem R ein S verwendet wird, ohne dass es zu Doppelungen käme. Gut, Doppelungen könnte man nachher umständlich rausschmeißen, aber das kann ganz schön dauern. Ob man sie rausschmeißen darf, liegt natürlich daran, wie die Ergebnismenge auszusehen hat, weswegen das gar nicht so selbstverständlich ist.
Vielleicht kannst du dir jetzt denken, wie man mit dem Berücksichtigen des ganzen Variantenreichtums seine Freizeit, teils sinnlos, verplempern kann? Da ist dann schon mal schnell die Restenergie, die man besser auf das eigentliche Problem verwendet hätte, aufgebraucht. Dieses ist kein Vorwurf, sondern eine Erklärung, warum ich es nicht geschafft habe, schneller einigermaßen brauchbare Antworten zu geben.
Man möchte das Problem zuerst auf den Punkt gebracht haben, mitsamt Randbedingungen, da fehlt noch einiges. Code ist erst mal egal. Meiner ist nicht schön (aber praktisch, da Typen leicht an zentraler Stelle ausgetauscht werden können) und auch nur dazu da, um die Sache etwas griffiger zu bekommen und um dir mal ein paar gemessene Zeiten anbieten zu können. Aber dazu sollte der auch nicht einfach hingeschludert sein, damit die Ergebnisse aussagekräftig sind, was mindestens bedeutet, dass sie korrekt sein müssen (Es fehlen doch wohl keine Berechnungen? Es wurde doch wohl nichts Nötiges wegoptimiert (wäre erlaubt, wo Ergebnisse keine beobachtbaren Effekte bringen)? Was messe ich da eigentlich?) und dass der Kram bereits einigermaßen effizient ist (um das Potenzial zu sehen). Das alles zusammen, auch wenn das eigentliche Problem nicht so schwierig ist, kann einen, erst recht, wenn es auch noch komfortabel benutzbar sein soll, ganz schön lange beschäftigen.
Schon die normalen 64-Bit-Typen können bekanntlich, je nach gesetzten Parametern, die RAM-Grenzen sprengen. Diese gewöhnliche Erkenntnis kann leicht davon ablenken, dass es schon innerhalb von Grenzen, wie du sie hier am Anfang genannt hast, leicht passieren kann, dass die Erde weniger Atome hat, als du Bits zum Speichern bräuchtest. Ich habe mir erspart, extra auszurechnen, ob die Sonne verglüht ist, bevor ein ganz normaler Computer mit den Berechnungen fertig wäre.
Man braucht also sehr eng gesteckte Grenzen. Die Anzahl der Möglichkeiten kann man vorher schnell ausrechnen lassen, um die Sache notfalls, z.B. damit die Kiste nicht endlos in der Auslagerungsdatei herumrödelt, vorher abbrechen zu können. Harte Allokationsfehler lassen sich über Exceptions abfangen und sind daher eher leicht zu handhaben und eher kein Problem, wenn man sich darum kümmert. Aber wehe, der Speicher wird zugesichert, obwohl der RAM nicht genügt, dann kann es passieren, dass die Kiste lange nicht mehr antwortet. Ob der RAM aktuell genügen würde, ist gar nicht so leicht herauszufinden. Man bräuchte dazu schon probeweise Allokationen, evtl. Preallokation, und auch damit kann man noch auf die Fresse fallen.
Nach meiner derzeitigen Arbeitshypothese, welche die Regelmäßigkeit bei deinem matrixartigen Gebilde (die ich vor ein paar Tagen wegen des sich abzeichnenden Wahnsinns so nicht wahrhaben wollte) befolgt, könnte man sagen, dass man sich dazu ein Zahlenschloss mit Ziffernrädchen vorstellen kann, bei dem aber unterschiedlich viele Ziffern je Rädchen erlaubt sind. Vielleicht kannst du mir bestätigen, ob ich damit richtig liege. Nicht dass mein Modell da schon hinkt. Es sollte leicht sein, das zu überprüfen, Zahlenschlösser, wie z.B. an Koffern oder Fahrradschlössern gebräuchlich, nehme ich mal als bekannt an.
Also dreht in meiner Anwendung das erste "Rädchen" immer eine Position weiter, wobei es bei jedem Überlauf in die Anfangsposition zurückgesetzt wird und einen Übertrag generiert und damit das nächste Rädchen um eine Position weiterdreht, und so setzt sich das über alle Rädchen fort. Dabei stehen die S-Listen (je R eine) für ein Rädchen und die Einträge der S-Listen für die Ziffern darauf. Vielleicht kannst du mir kurz bestätigen, ob das passt.
Das spuckt meine Anwendung dazu z.B. aus (speziell bei Zufallsgenerierung haben die Listen immer dieselbe Länge, funktioniert aber auch variabel):
Das Programm ist sozusagen sein eigener Test, um alles beieinander zu dokumentieren (ein Überlauftest ist jedoch nicht implementiert). Die Produkte fehlen nicht, sie werden bloß in einer alten Textzeile nicht miterwähnt. Während der Ausführung war noch der Editor im FF offen, mitsamt zappelnden GIFs. Es war also kein ganz ruhiges System, aber ich denke, dass das für einen Ersteindruck hier noch vertretbar gewesen ist.
Die Cache-Misses sind mal so und mal so (Zeiten schwanken, nach einigen Bemühungen, bei kleinen Datensätzen fast gar nicht mehr, wogegen man bei großen, wie z.B. beim letzten, mit etwas Pech, schon mal sporadisch ungefähr ein Drittel an Ausführungszeit hinzurechnen darf, was immer noch ein guter Wert ist, wenn man mitberücksichtigt, dass das verwendete System kurz vorm Anschlag ist und einiges vermutlich eher daran liegt), und ich habe nicht extra herumprobiert, um das schönzubiegen, das ist schon typisch.
Es kommt hier, wie man leicht erahnen kann, bereits zum Überlauf bei der Multiplikation mit 64-Bit-Werten. Varianten mit 128, 256, 512 sowie 1024 Bit lassen sich, da sie an zentraler Stelle verwaltet werden, im Handumdrehen (aber nicht ganz ohne Überlegung bezüglich der anderen, mit ihnen zu verrechnenden, Typen, da feine Anpassungen möglich, aber normalerweise nicht unbedingt erforderlich, sind) einkompilieren.
Nur kurz zur Implementierung, (weiter die Zahlenschlossanalogie bemühend):
Die richtigen Ziffern auf den Rädchen werden ermittelt, indem in so etwas wie einem dynamischen Array je Rädchen ("R" hihi) ein Positionsidex abgelegt ist, welcher für jedes Ergebnis um 1 inkrementiert wird und am Schluss (der durch ebenso zwischengespeicherte Listengrößen ermittelt wird) wieder auf 0 zurückgesetzt wird, wobei ein Übertrag in das nächste Rädchen entsteht, sodass auch dieses um 1 inkrementiert wird, was natürlich nur bei Überträgen passiert. Anhand dieser, während der Abarbeitung je Ergebnis berechneten, Indizesfolge wird auf die Summanden und auch auf die Faktoren zugegriffen, um diese sofort aufzusummieren bzw. auszumultiplizieren. Obwohl das im Prinzip einfach ist und der Code einfach aussieht, war der Vorgang, um das gutqualitativ zu erreichen, doch nicht ganz so einfach.
Ganzzahlen von unendlicher Genauigkeit gibt es unter C++ nicht (und woanders muss man aufpassen, dass sie kein Fake sind oder etwas anderes bezeichnen, JavaScript hat, wie ich gesehen habe, neuerdings wohl echte BigInts, wogegen "BigInt" unter C#-SQL nur eine 64-Bit-Struktur ist), weswegen ich Libs probiert habe. Damit bin ich vorerst gescheitert, weil ich wegen zu langer Ausführungs- und Wartezeiten die Geduld verloren habe. Letztendlich bin ich bei der Boost-Library für C++ und deren Typen mit fester Genauigkeit gelandet, was mir als ein sinnvoller Kompromiss erscheint, wenn man sowieso an Machbarkeitsgrenzen ankommt, z.B. uint128_t, mit dem ich gute Erfahrungen gemacht habe, wobei sich, falls nur zum Multiplizieren gebraucht (was der teuerste Teil ist), nur ungefähr eine Verdreifachung der Ausführungszeiten ergibt. Zeiten für verschiedene Typen, auch größere, könnte ich später mal posten, aber heute wohl eher nicht mehr.
Die für alles verwendete CPU ist ein Mobile Core2Duo @1,8 GHz konstant, also ein schwachbrüstiger Zeitzeuge aus der Steinzeit (sollte man trotzdem nicht unterschätzen, übliche aktuelle CPU-Cores sind bei solchen Sachen nur ungefähr dreimal so schnell, taktbereinigt eher weniger) und das mit eher langsamem RAM.
1. Nein, die "Wiederhohlung" muss so sein
Dann ist das wirklich wie bei einem Zahlenschloss, richtig? So habe ich es implementiert.
2. Die Anzahl der Elemente der Listen R und Der Liste S (in jedem einzelnen R-object eine eigene vorhanden) sind variable. Meist 3-5 S und zwischen 15 und 100 R
Mal beim absoluten Minimum angesetzt (natürlich wieder mit Überlauf bei der Multiplikation, ließe sich aber ganz leicht ändern, nur eben mit Vervielfachung der Ausführungszeit):
Ja, das mit Bigint vs double/decimal habe Ich mir auch überlegt, am ende hat Bigint gewonnen, wegen den Genauigkeitsverlusten.
Bist du sicher, dass die, speziell hier, wirklich relevant sind? Welches Bigint meinst du jetzt genau? Mich wundert etwas, dass das bei dir nicht so lahm ist, dass du den Typ aufgegeben hast. double (was man darunter landläufig versteht) währe echt toll, dann bliebe es, zumindest bei mir, bei der Größenordnung wie in meinen hier gezeigten Durchläufen, aber das könnte ich auch mal in einem Vergleich zeigen.
Eventuell kann man da aber auch was drehen mit einer anderen Art der Rechnung.
Ja, wobei ich mich die ganze Zeit über schon frage, ob die Mathematik so richtig ist. Was passiert denn, wenn eine Wahrscheinlichkeit 0 ist? Dann ist das gesamte Produkt 0. Bei allen meinen Arbeitshypothesen bezüglich des eigentlichen Problems ergibt das keinen Sinn (sondern etwas andere Verfahrensweisen, bei denen die Bezüge anders sind, sodass eher eine Summe aus Produkten entstünde, wobei verschiedene Varianten denkbar sind, mit verschiedener Aussagekraft), aber ich weiß natürlich nicht, was ausgerechnet werden soll, und ihr werdet schon eure Gründe haben. Ich will meinen Mund also nicht zu voll nehmen.
Also wäre dein Vorschlag Bigint soweit möglich zu "Entfernen" und Die Multiplikation via long durchzuführen, bis zur grenze. Die Wahrscheindlichkeit kann maximal 100 sein, ergo weiss Ich wie viele Ich pro long maximal Multiplizieren kann ohne Overflow, und Ich kann am ende die Long mit BigInt Multiplizieren.
Sollte es stimmen das BIgInt ziermlich langsam ist, würde das schon einiges bringen.
Ich habe noch mittlere Zweifel (zumindest bin ich neugierig), dass dein BigInt das ist, was es sein soll, wenn du damit zurechtkommst. Oder man hätte bei dessen Konstruktion einen verdammt guten Job gemacht (evtl. sogar unter Ausnutzung hardwarespezifischer Features?).
Und dazu noch einen Schnelleren RND Generator und dann könnte die Performance massiv ansteigen....
Sind diese Zufallszahlen denn sicherheitskritisch? Sonst dürfte man eigentlich nicht viel von deren Generierung merken. Bei mir sind die sogar gleichverteilt und fallen trotzdem kaum ins Gewicht. Notfalls ließen sie sich bevorraten (in sicherheitskritischen Bereichen eher nicht, aber dazu müsste man Details wissen). Oder geht es nur, wie bei mir, um das Generieren von Testdaten? Dann sollte das echt kein Problem sein. Falls die Zufallsdaten relevant sind, wäre das mal ein interessanter Ansatzpunkt für eine Erklärung.
Ich kann immer noch nicht ganz glauben, was da berechnet werden soll (bzw. warum ausgerechnet das, um zu brauchbaren Aussagen zu gelangen). Vielleicht liegt dort schon das größte Problem.
Hoffentlich habe ich jetzt nicht allzu viel übergangen, sorry dafür.