Wie gehen Leerlaufspiele mit so großen Zahlen um?

31

Ich frage mich nur, wie Spiele wie Tap Titans und Cookie Clicker mit so großen Zahlen umgehen.

Ich versuche, ein Leerlaufspiel zu implementieren, aber das größte von C # unterstützte Zahlenformat ist dezimal.

Ich möchte bis zu 10 ^ 500 unterstützen. das muss Gleitkomma sein

Wie könnte ich damit umgehen?

PS: Es muss plattformübergreifend sein, dh PC, Mac, iOS, Android und mit Unity kompatibel

MathHelp
quelle
2
Wenn ich mich richtig erinnere, haben Sie Zugriff auf den Code von Cookie Clicker, sodass Sie einfach überprüfen können, ob ...
Vaillancourt
Nein, ich hatte Cookie Clicker bereits für meinen Verstand gesperrt!
Arturo Torres Sánchez
2
Ich habe mir die dekompilierte Quelle für TimeClickers angesehen , ein Unity-Spiel. Sie benutzen nur double. Wenn ich es gewesen wäre, hätte ich allerdings BigInteger verwendet .
BlueRaja - Danny Pflughoeft
1
@VioletGiraffe: Ich stimme dem zu und möchte meine (unsere?) Zweifel konkretisieren (um dem OP möglicherweise zu helfen, die Frage klarer zu machen): Was ist ein "untätiges Spiel" in Bezug auf die Frage (das macht diese Frage aus) Spiel ein Sonderfall für große Zahlen)? Auf welche Arten von Zahlen bezieht sich " so große Zahlen" (Hervorhebung von mir selbst)? Wie groß sollen die Zahlen sein?
ODER Mapper
Sie können immer eine so große Zahl in einer Zeichenfolge speichern und auf einfache Weise eine Funktion zum Aufsummieren einer Zeichenfolge implementieren.
Dev-Masih

Antworten:

40

Wenn Sie nur massive Zahlen ohne vollständige Genauigkeit speichern möchten, zum Beispiel 12.567.000.000.000 als 12.567T (Billionen) anzeigen möchten, können Sie einfach Standard-Gleitkomma- / Dezimalwerte verwenden und die ersten x signifikanten Ziffern mit einem geeigneten Suffix anzeigen so was. Müssen Sie sich wirklich um jedes einzelne ganzzahlige Inkrement kümmern, wenn Sie in den Achtecken sind?

Ross Taylor-Turner
quelle
4
Dies ist wahrscheinlich die sinnvollste Antwort überhaupt, und ich bin mir ziemlich sicher, dass Cookie Clicker nur ein Standard-Float in JavaScript verwendet - ein Beispiel dafür, wann ich es "gehackt" habe (sprich: mit der Konsole durcheinander gebracht) und wann die Zahl 1e308 erreicht hat zu "Infinity" und ich konnte alles kaufen, was ich wollte XD
Niet the Dark Absol
1
Groß! Die Tatsache, dass der Text auf "Unendlich" springt, fühlt sich an, als ob der Entwickler diese Möglichkeit ebenfalls defensiv umschreibt. Einfachheit ist auch König.
Ross Taylor-Turner
19
@ RossTurner - Es war wahrscheinlich keine Kodierung erforderlich, um den Text "Infinity" anzeigen zu lassen - Infinity ist nur ein Wert, den ein Float haben kann, und JavaScript kann ihn wie viele andere Sprachen anzeigen.
Jibb Smart
Standard-Floats (doppelte Genauigkeit, 8 Bytes) erlauben keine Darstellung von Zahlen bis zu 10 ^ 500, wie es die ursprüngliche Frage erfordert, sie gehen bis zu ~ 10 ^ 300. Wenn das nicht ausreicht, sollte man Gleitkommazahlen mit vierfacher Genauigkeit verwenden, die je nach Sprache möglicherweise nicht trivial sind.
Peteris
1
Was jeder Programmierer über Gleitkommazahlen wissen sollte: floating-point-gui.de
Steve
20

Sie könnten so etwas wie BigInteger verwenden , das nur in .net 4.0 verfügbar ist, wenn ich mich nicht irre (nicht von allen Unity-Build-Plattformen unterstützt).

Es gibt einige Bibliotheken, die versuchen, diese Funktionalität ohne die Anforderung von .net4.0 bereitzustellen. Zum Beispiel hier .

Alternativ können Sie kleinere Zahlen verwenden, um eine größere darzustellen, indem Sie den Multiplikator verfolgen. Beispielsweise:

double value = 9;
enum Multiplier {
   Hundred,
   Thousand,
   Million,
   Billion,
   Trillion
}

Auch wenn Sie jetzt einen Wert von 9 haben, können Sie ihn durch Koppeln mit Ihrem Multiplikator effektiv als 9 Billionen darstellen (entweder indem Sie "Billionen" eingeben oder etwas schreiben, das die Nullen am Ende Ihres Werts anfügt ).

jgallant
quelle
Es ist erwähnenswert, dass Klassen wie BigIntegerString oder Byte [] als Repräsentation der Zahl verwenden - man kann also eine eigene Klasse erstellen, die nur zwei "Zahlen als Strings oder Bytes" addiert und subtrahiert - ( normalerweise ist dies bei dieser Art von Spiel nicht der Fall Sie brauchen es nicht, aber Sie könnten auch Multiplikation oder Division oder andere fortgeschrittenere Funktionen unterstützen ) - aber sie müssen bedenken, dass es immer eine physikalische Grenze gibt, an der ihnen möglicherweise der Speicher ausgeht.
DoubleDouble
1
Jedes Byte multipliziert die Größe Ihrer Zahl mit 256. Wenn Sie nicht mehr genügend Speicher haben, um eine Zahl darzustellen, hatte Ihre Zahl ohnehin keinen wirklichen Wert. 256 ^ (2 Milliarden) = 2 ^ 8 ^ (2 Milliarden) = 2 ^ (16 Milliarden) ~ = 10 ^ (5 Milliarden) (Wolframalpha brach bei dem Versuch ab, diese letzte Umwandlung vorzunehmen). Sie können einzelne Planck-Volumes im beobachtbaren Universum mit 150 Stellen (<500 Bit) angeben.
MichaelS
11

Sie müssten wahrscheinlich Ihre eigene Klasse schreiben, um sie in Unity zu verwenden, aber das wäre nicht besonders schwierig.

Intern kann es sich um eine Liste von Ganzzahlen handeln (z. B. a List<int>), wobei jedes Element in der Liste einer Gruppe von 9 Ziffern entspricht. Jede Ganzzahl hätte einen Bereich von 0 bis 999 999 999. Eine Ganzzahl kann etwas mehr als 2 Milliarden unterstützen und das Doppelte, wenn sie nicht vorzeichenbehaftet ist, aber Sie möchten, dass jede "Ziffer" überläuft 1 000 000 000, weil Sie es auch wollen in der Lage sein, Ihre große Zahl in eine Zeichenfolge für die Anzeige leicht umzuwandeln. Es ist einfacher zu verketten, was bereits Dezimalstellen ( return group[i].ToString() + group[i-1].ToString() and so on) sind, als herauszufinden, wie die Summe der Gruppen angezeigt wird, die außerhalb des Bereichs eines regulären Datentyps liegen.

Das erste int in Ihrer Liste würde also 1s darstellen. Das nächste wäre die Anzahl der Milliarden. Das nächste, die Anzahl der Billiarden und so weiter.

Das Addieren und Subtrahieren funktioniert genauso wie das Addieren und Subtrahieren von Stift und Papier, bei dem auf Überlauf geachtet und zur nächsten "Ziffer" übertragen werden muss. Anstelle von Ziffern mit einem Bereich von 0 bis 9 reichen Ihre Ziffern jedoch von 0 bis 999 999 999.

Jibb Smart
quelle
10

Für den Umgang mit großen Zahlen würde ich mir das ansehen, was ich für ein gutes Beispiel wie Tower of Hero halte . Obere linke Ecke:

1

2
(Quelle: mzstatic.com )

Ohne ins Spiel zu kommen, ist der Umgang mit Zahlen relativ einfach: Sie sehen zwei Eimer mit Zahlen. Wenn Sie im Turm höher steigen und mehr "Gold" verdienen, repräsentieren die beiden Eimer einfach größere Zahlen.

120 
120M320K - 120 Million
120B631M - 120 Billion
120T134B - 120 Trillion

Sobald das Spiel vorbei ist, bewegt es sich in a, b, c ... z, aa, ab, ...

56aa608z

Auf diese Weise wissen Sie immer noch, wie viel Gold Sie "verdient" haben, ohne das Spiel im Detail zu ruinieren.

Interessieren Sie sich wirklich für Millions, wenn Ihre Zahl über Trillions liegt?

Bleibt die Zahl in Int, Big Int, Float, Double, Decimal, ...? Benutzerdefiniertes Array? Wenn du mit Zahlen so "verschwommen" umgehst, denke ich nicht, dass es wichtig ist ...

Alles, was wahrscheinlich wichtig ist, sind die wichtigsten Teile - in diesem Fall die ersten 6 ... Danach MÖGLICHERWEISE die nächsten 3 oder 6 - da das Verdienen von ein paar hundert Kilometern in Millionen übergehen kann - aber es gibt einen Punkt, an dem das Verdienen beginnt Ein paar Hundert K werden Sie nicht beeinträchtigen, wenn Sie T drücken, geschweige denn aa und mehr.

Ihre Laufleistung variiert (je nachdem, was Sie wollen / brauchen) ... Ich dachte nur, ich würde meine 2c auf das setzen, was ich für ein gutes / einfaches Beispiel halte.

Bearbeiten:

Weitere Gedanken darüber, wie ich das Nummerierungssystem implementieren würde: Ich hätte eine Nummer mit 3 signifikanten Teilen: XXXX.YYY (...) xZZZ.

X is the most significant digits, 
Y trailing 
Z the multiplier (multiple of 3).

120.365x1 wäre also 120k365 ... 120.365x2 wäre 120M365K ... usw. Schlagen Sie die 4 führenden (1200.365x2) und drehen Sie dann einfach die Zahlen 1.200365 (...) x3. Bam. Sie haben 1B200M.

XY würde leicht in eine Dezimalzahl oder ein Float passen ... mit Z daneben als int / unsigned int.

Mit einem Float können Sie eine beträchtliche, aber zunehmend unwichtige Anzahl von Ziffern nach dem Punkt beibehalten.

Z würde den leicht verständlichen Zahlenblock darstellen:

K = 1
M = 2
B = 3
T = 4
a = 5 
...
z = 31 (I may be off on this)
aa = 32
...
az = 58
ba = 59
...
...
WernerCD
quelle
1

Ein einfacher Weg, mit großen Zahlen umzugehen, besteht darin, mehr als einen INTEGER-Wert zu haben und dann einen eventuellen Überlauf zu CARRY. Wenn Sie einen 16-Bit-INT-Wert (0 bis 65535) haben und mehr möchten, verwenden Sie zwei 16-Bit-INT-Werte hintereinander. Stellen Sie sich vor, Sie hätten einen BYTE-Wert (0 bis 255), verwenden ihn jedoch nur bis zu 99 Stellen. Sobald der Wert 100 erreicht hat, rollen Sie ihn auf den nächsthöheren BYTE-Wert, und zwar für so viele Stellen, wie Sie für nützlich halten. Mit den heutigen GHZ-Computern ist auch eine solche schlampige Codierung in Ordnung.

Natürlich gibt es eine Alternative, die etwas schneller ist.
Wenn Sie einer Zahl wiederholt 18 hinzufügen, können Sie einfach 2 subtrahieren und 20 hinzufügen. 18, 36, 54, 72, 90, 108, ... 18 = 20 + (- 2).
Es funktioniert auch in Binary. (Gleiche Dezimalwerte in Binär) 10010, 100100, 110110, 1001000
DEC (18) = BIN (10010) Mit
Ausnahme der einfacheren Binäranalyse müssen Sie an 18 = 16 + 2
DEC (16 + 2) = BIN (10000 + denken 00010). Wenn der Wert 15 war, stellen Sie sich vor, Sie addieren 16 in binär und subtrahieren 1 (10000-00001).

Auf diese Weise können Sie die Anzahl der Chunks pro Wert auf ein überschaubares Maß beschränken.
Wenn Sie die schlampige Codierungsmethode zum Begrenzen eines 16-Bit-INT-Grundwerts (0 bis 65535) auf einen 4-stelligen Dezimalgrenzwert (0 bis 9999) verwenden, müssen Sie nur den Grenzwert 9999 überschreiten. subtrahiere 9999 davon und trage es zum nächsten Wert-Chunk (da du im Grunde genommen "addiere & subs" mit Zahlen gegenüber einer reellen Binärberechnung machst).

Idealerweise verwenden Sie nur SYMBOLISCHE MATHE, die Sie in der Grundschule gelernt haben. Wie funktioniert das. Wenn Sie das Dezimalsymbol von 1 haben und es zu 1 addieren, erhalten Sie das Dezimalsymbol von 2. Der Grund, warum ich dies als Symbol bezeichne, ist, dass Sie jede Symbolserie mit einer Nachschlagetabelle von "IF symbol X (1)" verwenden können. wird zum Symbol X (4) DANN zum Ausgangssymbol X (5) "hinzugefügt. Symbol X (1) könnte ein Bild einer Katze sein, und Symbol X (4) könnte das Prozentzeichen sein, aber es spielt keine Rolle. Sie haben Ihre Symbole, ein grundlegendes Regelwerk darüber, was passiert, wenn diese Symbole kombiniert werden (ähnlich wie die Multiplikationstabellen, die Sie als Kind gespeichert haben) und welches Symbol als Teil dieser Operation resultieren muss. Mit Symbolic Math können Sie eine unbegrenzte Anzahl von Ziffern hinzufügen, ohne die numerischen Grenzen Ihres Prozessors wirklich zu überschreiten.

Eine Möglichkeit, dies bei der einfachen Codierung zu tun, besteht darin, jede dargestellte Ziffer, mit der Sie arbeiten möchten, als einzelne Einheit in einem Array mit großen Abmessungen darzustellen. Wenn Sie 4096 Dezimalstellen darstellen möchten (maximal 2 Stellen pro Einheit), weisen Sie 2 Sätze mit 4096 BYTE-Array-Positionen zu. Das Speichern des Wertes von 1098874 würde das Array als (1) (0) (9) (8) (8) (7) (4) verwenden. Wenn Sie den Wert 7756 dazu addieren, konvertieren Sie ihn in (7) (7) (5) (6) und addieren dann. Das Ergebnis wäre (1) (0) (9) (15) (15) (12) (10), und Sie würden dann die Verschiebung von rechts nach links subtrahieren, bis alle Ziffernfelder auf den Wert von (0 bis) normiert wurden 9). Der am weitesten rechts stehende Wert (10) würde 10 subtrahieren und der resultierende Wert wäre Null (0), und das würde den Wert (1) in das nächste linke Kästchen von (12) tragen, um daraus (13) zu machen, das dann 10 hätte subtrahiert, um es in (3) zu machen.

Gridlock
quelle
7
-1 für ZU VIEL SCHREIEN
Slipp D. Thompson
5
Hey @Gridlock, ich habe gerade bemerkt, dass Sie ein völlig neuer Benutzer sind - willkommen. Ich sollte klarstellen, dass meine Ablehnung nicht dauerhaft ist. es ist als konstruktive kritik gedacht. Die von Ihnen bereitgestellte Antwort ist wertvoll, und ich werde sie gerne in eine positive Bewertung ändern, sobald Sie die von mir angeforderte Korrektur vorgenommen haben. Denken Sie auch daran, dass SE kein Forum ist. Eine gute Antwort wird nicht gelesen und dann vergessen, sondern zahlt sich im Laufe der Zeit aus. Das Überarbeiten Ihrer Antworten hier und da hilft der Community und Ihrem Repräsentanten. Ich hoffe, ich habe dich nicht verscheucht. Nochmals herzlich willkommen und ich hoffe, Sie werden meine Ratschläge nutzen.
Slipp D. Thompson
1
Der andere Vorschlag, den ich vorschlage, ist die Verwendung grundlegender Markdown-Formatierungen anstelle von Großbuchstaben. Wenn Sie Text in Backticks ( `text`text) einschließen, wird er in einem einheitlichen Abstand angezeigt, der für Codestücke , Funktionsnamen, Daten usw. geeignet ist. Wenn Sie Text in Unterstriche oder Sternchen einschließen, wird er kursiv ( _text_oder *text*Text ) und doppelt unterstrichen oder doppelt unterstrichen. Sternchen machen es fett ( __text__oder **text**Text ).
Slipp D. Thompson
0

Sie können mehrere Variablen kombinieren und dann die Addition und den Überlauf zwischen ihnen selbst steuern. Sie können auf diese Weise eine beliebige Anzahl von Ziffern eingeben. Kombinieren Sie 10.000 32-Bit-Ganzzahlen, und Sie haben eine Zahl mit 32.000 Bits, wenn Sie dies benötigen. In jeder Programmiersprache gibt es keine Grenzen. Die einzige Grenze ist, was Sie herausfinden können.

Frank
quelle
Können Sie denjenigen, die Ablehnungen gemacht haben, erklären, warum? Auch wenn dies keine beliebte Lösung ist oder keine Anstrengung erfordert, ist es dennoch eine Lösung, die es Ihnen ermöglichen könnte, dasselbe auch auf Sprachen / Plattformen zu tun, die doubleoder nicht unterstützen BigInteger.
TomTsagk