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.