Como os jogos inativos lidam com números tão grandes?


31

Imaginando como jogos como o Titan tit e o Cookie Clicker lidam com números tão grandes.

Estou tentando implementar um jogo ocioso, no entanto, o maior formato numérico suportado pelo c # é decimal.

Eu estou olhando para suportar até 10 ^ 500; que precisa ser ponto flutuante

Como eu poderia lidar com isso?

PS: ele precisa ser multiplataforma, ou seja, pc, mac, ios, android e compatível com Unity


2
Se bem me lembro, você tem acesso ao código do Cookie Clicker, para verificar se ...
Vaillancourt

Não, eu já tinha bloqueado o Cookie Clicker da minha mente!
Arturo Torres Sánchez

2
Eu olhei a fonte descompilada do TimeClickers , um jogo do Unity. Eles apenas usam double. Se fosse eu, eu teria usado o BigInteger .
precisa saber é o seguinte

1
@VioletGiraffe: Eu meio que concordo, e gostaria de tornar concretas minhas (nossas?) Dúvidas (para possivelmente ajudar o OP a esclarecer a pergunta): O que é um "jogo inativo" com relação à pergunta (que torna esse tipo do jogo um caso especial para grandes números)? A que tipos de números se refere "números tão grandes" (destaque próprio)? Quão grande você deseja que os números sejam?
OR Mapper

você sempre pode armazenar um número tão grande em uma string e implementar facilmente uma função para fazer a soma na string.
precisa saber é o seguinte

Respostas:


40

Se você deseja apenas armazenar números enormes sem precisão total, por exemplo, se você mostrar 12.567.000.000.000 como 12.567T (trilhões), basta usar valores padrão de flutuação / decimal e mostrar os primeiros x números significativos, com um sufixo adequado como isso. Quando você está em mais de um milhão, você realmente precisa se preocupar com cada incremento inteiro individual?


4
Esta é provavelmente a resposta mais sensata, realmente, e tenho certeza de que o Cookie Clicker usa apenas um float padrão no JavaScript - no caso em que eu o "hackeei" (leia-se: mexeu no console) e quando o número atingiu 1e308, ele saltou para "Infinito" e eu pude comprar o que quisesse XD
Niet the Dark Absol

1
Ótimo! O fato de o texto pular para "Infinito" parece que o desenvolvedor também estava codificando defensivamente essa eventualidade. Além disso, a simplicidade é rei.
Ross Taylor-Turner

19
@RossTurner - Provavelmente não havia código envolvido para mostrar o texto "Infinito" - o infinito é apenas um valor que um float pode ter, e o JavaScript sabe como exibi-lo, como muitos outros idiomas.
Jibb Inteligente

Flutuadores padrão (precisão dupla, 8 bytes) não permitirão representar números de até 10 ^ 500, conforme a pergunta original exige, serão de ~ 10 ^ 300. Se isso não for suficiente, deve-se usar flutuadores de precisão quádrupla, que podem não ser triviais, dependendo do idioma.
Peteris 15/01

1
O que todo programador deve saber sobre números de ponto flutuante: floating-point-gui.de
Steve

20

Você pode usar algo como o BigInteger , que só está disponível no .net 4.0 se não me engano (não é suportado por todas as plataformas de criação de unidade).

Existem algumas bibliotecas que tentam trazer essa funcionalidade sem a necessidade de .net4.0. Por exemplo aqui .

Como alternativa, você pode usar números menores para representar um número maior, acompanhando o multiplicador. Por exemplo:

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

Agora, mesmo que você tenha um valor 9, se você o associar ao seu multiplicador, poderá efetivamente representá-lo como 9 trilhões (ou colocando "trilhão" ou escrevendo algo que acrescentará os zeros no final do seu valor) )


Vale a pena notar que classes como BigIntegerusar uma representação String ou Byte [] do número - portanto, é possível criar sua própria classe que apenas adiciona e subtrai dois "números como Strings ou Bytes" - ( geralmente esse tipo de jogo não não é necessário, mas você também pode oferecer suporte à multiplicação ou divisão ou outras funções mais avançadas ) - mas eles devem ter em mente que sempre há um limite físico em que a memória pode ficar sem memória.
precisa saber é o seguinte

1
Cada byte multiplica o tamanho do seu número por 256. Se você ficar sem memória tentando representar um número, seu número não terá nenhum valor real. 256 ^ (2 bilhões) = 2 ^ 8 ^ (2 bilhões) = 2 ^ (16 bilhões) ~ = 10 ^ (5 bilhões) (wolframalpha quebrou tentando fazer essa última conversão). Você pode especificar volumes Planck individuais no universo observável usando 150 dígitos (<500 bits).
precisa saber é

11

Você provavelmente precisaria escrever sua própria classe para usar no Unity, mas não seria particularmente difícil.

Internamente, poderia ser uma lista de números inteiros (como a List<int>), com cada elemento na lista correspondendo a um grupo de 9 dígitos. Cada número inteiro tem um intervalo de 0 a 999 999 999. Um número inteiro pode suportar um pouco mais de 2 bilhões e o dobro se não estiver assinado, mas você deseja que cada "dígito" exceda 1 000 000 000, porque você também deseja para converter facilmente seu grande número em uma string para exibição. É mais fácil concatenar o que já é um dígito decimal ( return group[i].ToString() + group[i-1].ToString() and so on) do que descobrir como exibir a soma dos grupos que estão fora do intervalo de qualquer tipo de dados regular.

Portanto, o primeiro int na sua lista representaria 1s. O próximo seria o número de bilhões. O próximo, o número de quatrilhões e assim por diante.

A adição e subtração funcionariam como a adição e subtração de caneta e papel, onde você deve observar o estouro e transportá-lo para o próximo "dígito", mas, em vez de dígitos com um intervalo de 0 a 9, seus dígitos variam de 0 a 999 999 999.


10

Para lidar com grandes números, eu consideraria um bom exemplo como Tower of Hero . Canto superior esquerdo:

1

2
(fonte: mzstatic.com )

Sem entrar no jogo, a maneira como ele lida com os números é relativamente simples: você vê dois grupos de números. À medida que você sobe na torre e produz mais "ouro", os dois baldes simplesmente representam números maiores.

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

Quando o jogo passa T, ele se move para a, b, c ... z, aa, ab, ...

56aa608z

Fazendo dessa maneira, ele ainda permite saber quanto ouro você "ganhou" ... sem atrapalhar o jogo em detalhes.

Você realmente se importa com milhões quando seu número ultrapassa os trilhões?

Mantém o número em Int, Big Int, Float, Double, Decimal, ...? Matriz personalizada? Quando você está lidando com números de maneira "confusa", não acho que isso importe ...

Tudo o que importa é a parte mais significativa - neste caso, os 6 primeiros ... Depois disso, TALVEZ os próximos 3 ou 6 - já que ganhar algumas centenas de K pode rolar para milhões - mas há um ponto em que ganhar algumas centenas de K não afetarão você quando você acertar T ... muito menos aa e além.

Sua milhagem varia (dependendo do que você deseja / precisa) ... Só pensei em colocar meu 2c no que acho um exemplo bom / simples.

Editar:

Pensamentos adicionais sobre como eu implementaria o sistema de numeração: eu teria um número com três partes significativas: XXXX.YYY (...) xZZZ.

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

Então 120.365x1 seria 120k365 ... 120.365x2 seria 120M365K ... etc. Bata os 4 à esquerda (1200.365x2) e gire os números 1.200365 (...) x3. Bam. Você tem 1B200M.

XY caberia facilmente em um decimal ou flutuante ... com Z sentado próximo a ele como int / unsigned int.

Com um flutuador, você poderá manter um número considerável - mas cada vez mais sem importância - de dígitos após o ponto.

Z representaria facilmente um bloco de números facilmente compreensível:

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

1

E a maneira mais fácil de lidar com números grandes é simplesmente ter mais de um valor INTEGER e carregar qualquer excesso. Se você possui um valor INT de 16 bits (0 a 65535) e deseja ter mais do que isso, use dois valores INT de 16 bits em uma linha. Pense nisso como ter um valor BYTE (0 a 255), mas usando apenas até 99 dígitos de valor. Quando atingir 100, passe-o para o próximo valor mais alto de BYTE, com o número de dígitos que achar útil. Com os computadores GHZ de hoje, até uma codificação desleixada é boa.

Obviamente, existe uma alternativa um pouco mais rápida.
Se você estiver adicionando 18 repetidamente a um número, é mais rápido subtrair 2 e adicionar 20. 18, 36, 54, 72, 90, 108, ... 18 = 20 + (- 2).
Também funciona em binário. (Mesmos valores decimais em binário) 10010, 100100, 110110, 1001000
DEC (18) = BIN (10010)
Exceto para uma análise binária mais fácil, você precisa pensar em 18 = 16 + 2
DEC (16 + 2) = BIN (10000 + 00010). Se o valor for 15, pense nele como adicionando 16 no binário e subtraindo 1 (10000-00001).

Dessa forma, você pode manter os blocos de números em limites gerenciáveis ​​por valor.
Se você estiver usando o método de codificação superficial de limitar um valor INT básico de 16 bits (0 a 65535) a um limite decimal de 4 dígitos (0 a 9999), tudo o que você precisa fazer é quando o valor exceder o limite 9999, subtraia 9999 e leve-o para o próximo pedaço de valor (já que você está fazendo basicamente "adiciona e sub" com números versus um cálculo binário real).

Idealmente, você usaria a MATEMÁTICA SIMBÓLICA que aprendeu na escola. Como é que isso funciona. Se você possui o símbolo decimal de 1 e o adiciona a 1, obtém o símbolo decimal de 2. O motivo pelo qual eu chamo isso de símbolo é que você poderia usar qualquer série de símbolos com uma tabela de pesquisa "SE símbolo X (1) é adicionado ao símbolo X (4) ENTÃO, símbolo de saída X (5) ". O símbolo X (1) pode ser uma imagem de um gato e o símbolo X (4) pode ser o sinal PERCENT, mas isso não importa. Você tem seus símbolos, um livro de regras básico do que acontece e depois esses símbolos são combinados (como as tabelas de multiplicação que você memorizou quando criança) e qual símbolo deve resultar como parte dessa operação. Com o uso da Symbolic Math, você pode adicionar um número infinito de dígitos sem nunca chamar os limites numéricos do seu processador.

Uma maneira de fazer isso na codificação fácil é ter cada dígito representado com o qual você deseja trabalhar como uma única unidade em uma grande matriz dimensionada. Se você deseja representar 4096 dígitos decimais (não excedendo 2 dígitos por caixa de unidade), aloque 2 conjuntos de locais da matriz 4096 BYTE. Armazenar o valor de 1098874 utilizaria a matriz como (1) (0) (9) (8) (8) (7) (4). Se você adicionou o valor de 7756 a ele, você o converteria em (7) (7) (5) (6) e depois o adicionaria. O resultado seria (1) (0) (9) (15) (15) (12) (10) que você subtrairia da direita para a esquerda até que todas as caixas de dígitos fossem normalizadas para serem o valor de (0 a 9) O valor mais à direita (10) teria 10 subtraído e o valor resultante seria zero (0) e isso levaria o valor (1) para a próxima caixa à esquerda de (12) para torná-lo (13), que então teria 10 subtraído para transformá-lo em (3).


7
-1 para MUITO GRITAR
Slipp D. Thompson 17/01

5
Hey @Gridlock, acabei de perceber que você é um usuário completamente novo - bem-vindo. Devo esclarecer que meu voto negativo não é permanente; é uma crítica construtiva. Há valor para a resposta que você forneceu, e ficarei feliz em alterá-la para um voto positivo depois de fazer a correção que estou solicitando. Além disso, lembre-se de que o SE não é um fórum; uma boa resposta não é lida e esquecida, mas paga dividendos ao longo do tempo. Revisar suas respostas aqui e ali ajuda a comunidade e seu representante. Espero não ter assustado você; novamente, seja bem-vindo e espero que você siga meus conselhos.
precisa saber é o seguinte

1
A outra sugestão que oferecerei é usar alguma formatação básica do Markdown em vez de letras maiúsculas. Quebrar texto em backticks ( `text`text) o tornará espaçado em mono, apropriado para blocos de código, nomes de funções, dados, etc. Quebrar texto em sublinhados ou asteriscos o tornará em itálico ( _text_ou *text*texto ) e em sublinhado duplo ou duplo os asteriscos ficarão em negrito ( __text__ou **text**texto ).
Slipp D. Thompson

0

O que você faz é ter várias variáveis ​​combinadas e, em seguida, você controla a adição e o excesso entre elas. Você pode ter qualquer número de dígitos dessa maneira. Combine 10.000 números inteiros de 32 bits e você terá um número com 32.000 bits, se é isso que você precisa. Não há limite em nenhuma linguagem de programação. O único limite é o que você é capaz de descobrir.


Para aqueles que deram votos negativos, você poderia explicar o porquê? Mesmo que essa não seja uma solução popular, ou que não exija esforço, ainda é uma solução, e que pode permitir que você faça o mesmo, mesmo em idiomas / plataformas que não oferecem suporte doubleou BigInteger.
TomTsagk 19/03
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.