[PROGRAMAÇÃO] - Desafio - O tabuleiro de Xadrez e os bagos de arroz

Coloquem aqui as vossas dúvidas informáticas, e algumas notícias que achem de interesse para a comunidade DvdManiaca
Samwise
DVD Maníaco
DVD Maníaco
Posts: 6267
Joined: February 19th, 2009, 9:07 pm
Contact:

Re: [PROGRAMAÇÃO] - Desafio - O tabuleiro de Xadrez e os bagos de arroz

Post by Samwise »

Projecto de leitura e escrita para Excel terminado. Aqui fica o código (com comentários ao longo):

Code: Select all

package com.company;

import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;

import java.io.*;

import static com.company.Extenso5.extenso;

public class Main {

    public static void main(String[] args) {

      try{
            // Abrir canal de "leitura/input" e carregar ficheiro
            FileInputStream file = new FileInputStream(new File( "C:\\Temp\\Java\\translate.xlsx"));
          
            // Declaraçao e instanciação de objectos POI, para oe elementos da folha de cálculo
            XSSFWorkbook workbook = new XSSFWorkbook(file);
            Sheet sheet = workbook.getSheetAt(0);
            Cell cell;
            Row row;
          
            // Quantas linhas tem a folha
            int rows = sheet.getPhysicalNumberOfRows();
          
            // Loop pelas linhas com informação    
            String getterS, extVal;

            for(int r = 1; r < rows; r++) {
                row = sheet.getRow(r);
                if(row != null) {
                   cell = row.getCell((short)0);
                   if(cell != null) {
                       // READ da célula
                       getterS = String.valueOf(cell.getNumericCellValue());
                       getterS = getterS.substring(0, getterS.indexOf("."));
                       // Obter o valor por extenso 
                       extVal = extenso(getterS);
                       
                       // WRITE para a célula (col + 1)
                       cell = row.createCell(1);
                       cell.setCellValue(extVal);

                   }
                }
            }
            // Abrir um "canal de output e Gravação", paralelo ao de Leitura
            FileOutputStream output = new FileOutputStream(new File( "C:\\Temp\\Java\\translate.xlsx"));
            // Gravar
            workbook.write(output);
            
            // Fechar operações        
            output.close();
            workbook.close();
            file.close();

            System.out.println("Terminado.");

      }
      catch (FileNotFoundException naoHaFicheiro){
          System.out.println("No file: " + naoHaFicheiro);
      }
      catch (IOException erroIO){
          System.out.println("IO Error: " + erroIO);
      }
    }
}
Com esta ferramenta à mão, vou passar ao debugging mais intensivo e extensivo da Classe Extenso, para poder fechar o projecto.
«The most interesting characters are the ones who lie to themselves.» - Paul Schrader, acerca de Travis Bickle.

«One is starved for Technicolor up there.» - Conductor 71 in A Matter of Life and Death

Câmara Subjectiva
Samwise
DVD Maníaco
DVD Maníaco
Posts: 6267
Joined: February 19th, 2009, 9:07 pm
Contact:

Re: [PROGRAMAÇÃO] - Desafio - O tabuleiro de Xadrez e os bagos de arroz

Post by Samwise »

Ok, nova inflexão não pleaneada no projecto. :-))) (está a começar a ser um hábito...)

Sucede que (também) há uma limitação na extensão que o tipo de dados "numérico" consegue suportar quando estou a fazer a importação de valores de uma célula de Excel através do mecanismo acima descreito. Na prática, isto significa que o programa resulta bem até determinada grandeza, e depois começa a dar valores que não têm nada a ver. Para contornar esta limitação, a solução mais óbvia seria formatar os dados logo na folha de Excel para serem lidos como "String" (implica colocar um apóstrofe antes do número), mas se optar por essa via deixo de conseguir manipular a informação como se fosse um número - deixo de poder incrementar as células facilmente, deixo de poder fazer operações matemáticas sobre os dados e deixo de poder ver os separadores de milhar. Em suma, perco as vantagens que iria ganhar em fazer o debugging por esta via.

Portanto, novo mini-projecto à vista. Desta vez vou mesmo correr TODOS os números de um a um até 1 Decilião e obter para cada um o respectivo extenso, só que vou mandar os dados para um ficheiro de texto. Se à partida esta ideia até parece ser simples e descomplexada ( :lol: ), se começarmos a pensar nas implicaçõe práticas a coisa toma dimensões sobre-humanas, porque o ficheiro de output será, previsivelmente enormérrimo... 1 decilião de linhas, com números e texto à mistura, é muita fruta.

Aceitam-se apostas para prever o espaço que vai ocupar em disco, e o tempo que vai demorar ao programa construir um ficheiro assim. nails-) :?: 8) (isto é giro até para fazer benchmarks... :wink: *** para além de que devem registar-se variações consideraveis de sistema para sistema...)

Programaticamente parece não ser nada complicado. Faz-se um loop que incrementa uma variável de um a um até 1 decilião, e a cada pass manda o resultado do extenso para o ficheiro.

Pretende-se isto, dentro de um ficheiro .txt:

1 - um
2 - dois
3 - três
4 - quatro
...
1.000... 000 - um decilião
«The most interesting characters are the ones who lie to themselves.» - Paul Schrader, acerca de Travis Bickle.

«One is starved for Technicolor up there.» - Conductor 71 in A Matter of Life and Death

Câmara Subjectiva
Samwise
DVD Maníaco
DVD Maníaco
Posts: 6267
Joined: February 19th, 2009, 9:07 pm
Contact:

Re: [PROGRAMAÇÃO] - Desafio - O tabuleiro de Xadrez e os bagos de arroz

Post by Samwise »

Novo choque de frente entre conceito intencionado e concretização. teimoso-) :lol:

Desenvolvi o tal programa para mandar resultados para ficheiros .txt ...

Code: Select all

import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintStream;
import java.math.BigInteger;

import com.sun.management.OperatingSystemMXBean;
import java.lang.management.ManagementFactory;

import static com.company.Extenso.extenso;

class Main {

    public static void main(String[] args) {

        OperatingSystemMXBean osBean = (com.sun.management.OperatingSystemMXBean)
                                        ManagementFactory.getOperatingSystemMXBean();
        String osName = osBean.getName();
        String osVersion = osBean.getVersion();
        int cores = osBean.getAvailableProcessors();
        String cpuName = System.getenv("PROCESSOR_IDENTIFIER");
        Long memSize = osBean.getTotalPhysicalMemorySize();

        try {

            BigInteger limite = new BigInteger("10000000");

            double init, finit;

            init = System.currentTimeMillis();

            PrintStream out = new PrintStream(new FileOutputStream(
                    "c:\\temp\\java\\coding\\DebuggerExt\\output.txt", false), false);
            System.setOut(out);

            for (BigInteger loopVar = new BigInteger("1");
                 loopVar.compareTo(limite) <= 0;
                 loopVar = loopVar.add(BigInteger.valueOf(1))) {

                String sepMilhares = String.format("%,d", loopVar);
                System.out.println(sepMilhares + " - " + extenso(loopVar.toString()));

            }

            finit = System.currentTimeMillis();

            System.out.println("Tempo decorrido: " + ((finit - init)/1000D )+ " segundos");
            System.out.println();
            System.out.println("SO: " + osName);
            System.out.println("Versao SO: " + osVersion);
            System.out.println("Tipo CPU: " + cpuName);
            System.out.println("\"Cores\" CPU: " + cores);
            System.out.println("Memoria (MBytes arred.): " + memSize/1024/1024);

        }
        catch(IOException e1) {
            System.out.println("Error during reading/writing");
        }
    }
}
... e comecei a testar progressivamente. Primeiro com 1.000 números, depois com 10.000, e só segui até aos 10.000.000, devido ao espaço e tempo que demoram a criar.

Pela imagem seguinte dá para ter uma ideia:

Image

Se com 10.000.000 de registos demora quase 5 minutos a criar um ficheiro de quase 800MB's, e se os ficheiros crescem na razão e proporção directa da grandeza dos números (multiplicando por 10 de um lado, o ficheiro gerada passa a ocupar 10x mais em disco), rapidamente esgotaria o espaço disponível em disco.

Assim por contas rápidas, cem milhões daria 8GB, mil milhões daria 80GB, dez mil milhões daria 900GB, cem mil milões daria 9PB, e um bilião daria 90PB... :-)))

Não deve haver sistema no mundo com espaço suficiente em disco para guardar um ficheiro com os extensos dos números todos entre 1 e 1 decilião. como?-)
«The most interesting characters are the ones who lie to themselves.» - Paul Schrader, acerca de Travis Bickle.

«One is starved for Technicolor up there.» - Conductor 71 in A Matter of Life and Death

Câmara Subjectiva
User avatar
Rui Santos
Site Admin
Posts: 6161
Joined: June 4th, 2001, 11:42 pm
Location: Portugal - Lisboa / MAC
Contact:

Re: [PROGRAMAÇÃO] - Desafio - O tabuleiro de Xadrez e os bagos de arroz

Post by Rui Santos »

Podes implementar uma classe de zip :), que te permitirá graver o ficheiro comprimido que nenhum PC descomprimirá :-)))
Rui Santos - 54 Anos | 22 Anos DVDMania
DVD/BR | Jogos | Life is Short, Play More | FB Collectors HV-PT
Samwise
DVD Maníaco
DVD Maníaco
Posts: 6267
Joined: February 19th, 2009, 9:07 pm
Contact:

Re: [PROGRAMAÇÃO] - Desafio - O tabuleiro de Xadrez e os bagos de arroz

Post by Samwise »

Rui Santos wrote: March 11th, 2019, 11:03 pm Podes implementar uma classe de zip :), que te permitirá graver o ficheiro comprimido que nenhum PC descomprimirá :-)))
Sim, e depois colocar um ficheiro numa dessas "time-capsules", para abrir daqui a 50 anos. Pode ser que então já se consiga descomprimir. :lol:

Só para efeitos de Benchmarking, ainda vou testar a mesma operação num PC mais potente, e com um disco SDD (que aqui vai fazer, supostamente, muita diferença, pois a operação mais morosa é a do registo no ficheiro).

O post anterior tem um ... bug... :-))) . A seguir ao GB não é o PB, é o TB.

Já agora:

https://whatsabyte.com/
«The most interesting characters are the ones who lie to themselves.» - Paul Schrader, acerca de Travis Bickle.

«One is starved for Technicolor up there.» - Conductor 71 in A Matter of Life and Death

Câmara Subjectiva
Samwise
DVD Maníaco
DVD Maníaco
Posts: 6267
Joined: February 19th, 2009, 9:07 pm
Contact:

Re: [PROGRAMAÇÃO] - Desafio - O tabuleiro de Xadrez e os bagos de arroz

Post by Samwise »

Vou para já fechar este projecto.

Fiz uma bateria de testes mais ou menos aleatórios e os resultados pareceram-me certos e consistentes.

O código final já optimizado, devidamente encapsulado e facilmente escalável, para a classe Extenso (permitindo obter resultados até à ordem do Decilião = inteiros com 66 algarismos), é o seguinte:

(código em Java - compila em JDK8 e versões posteriores)

Code: Select all

import java.math.BigInteger;

public class Extenso {

   public static String extenso(String inputVal) {

      boolean isNeg = false;
      if (inputVal.startsWith("-")) {
         isNeg = true;
         inputVal = inputVal.substring(1);
      }

      // Caçar caracteres não permitidos:
      char[] diagS = inputVal.toCharArray();
      for (char algarismo : diagS) {

         if (!Character.isDigit(algarismo)) {
            return "*** EXITING *** A express\u00E3o cont\u00E9m caracteres n\u00E3o num\u00E9ricos.";
         }
      }

      // Chamada ao método "nuclear": para triagem da grandeza e seguimento do fluxo
      return (isNeg ? "menos " : "") + triagemInicial(inputVal);
   }

   private static String triagemInicial(String i) {

      String valorFinal;

      BigInteger inputVal = new BigInteger(i);

      if (inputVal.compareTo(BigInteger.valueOf(1_000_000)) < 0) {

         int inputValNum = Integer.parseInt(i);

         if (inputValNum < 20) {

            valorFinal = deZeroaDezanove(inputValNum);
         } else if (inputValNum <= 100) {

            valorFinal = menorQueCem(inputValNum);
         } else if (inputValNum <= 1000) {

            valorFinal = menorQueMil(inputValNum);
         } else {  // <1_000_000

            valorFinal = menorQueUmMilhao(inputValNum);
         }
      } else {  // >= 1_000_000

         BigInteger milhaoComparador = new BigInteger("1" + "000000");
         BigInteger iterador = inputVal;
         int tamanhoGrandeza = 0;
         String extensoGrandeza = "1";

         while (iterador.compareTo(milhaoComparador) >= 0) {
            tamanhoGrandeza += 1;
            extensoGrandeza += "000000";
            iterador = iterador.divide(milhaoComparador);
         }
         String preExt;

         switch (tamanhoGrandeza) {

            case 1:    // < 1_000_000_000_000, menor do que 1 Bilião
               preExt = "milh";
               break;
            case 2:   // < 1_000_000_000_000_000_000, menor do que 1 Trilião
               preExt = "bili";
               break;
            case 3:   // < 1_000_000_000_000_000_000, menor do que 1 Quatrilião
               preExt = "trili";
               break;
            case 4:   // < 1 Quintilião ....
               preExt = "quatrili";
               break;
            case 5:   // < 1 Sextilião
               preExt = "quintili";
               break;
            case 6:   // < 1 Setptilião
               preExt = "sextili";
               break;
            case 7:   // < 1 Octilião
               preExt = "septili";
               break;
            case 8:   // < 1 Nonilião
               preExt = "octili";
               break;
            case 9:   // < 1 Decilião
               preExt = "nonili";
               break;
            case 10:   // < 1 Undecilião
               preExt = "decili";
               break;
         //  Acrescentar aqui novas grandezas necessárias
            default:
               preExt = "nulo";
         }

         valorFinal = (preExt != "nulo"
               ? metodoMilionario(inputVal.toString(), preExt, extensoGrandeza)
               : "Inteiro demasiado grande - ainda n\u00E3o suportado.");
      }
      return valorFinal;
   }

   private static String deZeroaDezanove(int i) {

      String valorFinal;

      switch (i) {
         case 0:
            valorFinal = "zero";
            break;
         case 1:
            valorFinal = "um";
            break;
         case 2:
            valorFinal = "dois";
            break;
         case 3:
            valorFinal = "tr\u00EAs";
            break;
         case 4:
            valorFinal = "quatro";
            break;
         case 5:
            valorFinal = "cinco";
            break;
         case 6:
            valorFinal = "seis";
            break;
         case 7:
            valorFinal = "sete";
            break;
         case 8:
            valorFinal = "oito";
            break;
         case 9:
            valorFinal = "nove";
            break;
         case 10:
            valorFinal = "dez";
            break;
         case 11:
            valorFinal = "onze";
            break;
         case 12:
            valorFinal = "doze";
            break;
         case 13:
            valorFinal = "treze";
            break;
         case 14:
            valorFinal = "catorze";
            break;
         case 15:
            valorFinal = "quinze";
            break;
         case 16:
            valorFinal = "dezasseis";
            break;
         case 17:
            valorFinal = "dezassete";
            break;
         case 18:
            valorFinal = "dezoito";
            break;
         case 19:
            valorFinal = "dezanove";
            break;
         default:
            valorFinal = "BUG eating method deZeroaVinte";
      }
      return valorFinal;
   }

   private static String menorQueCem(int i) {

      String valorFinal;

      if (i % 10 == 0) {
         valorFinal = deVinteaNoventaeNove(i);
      } else {
         valorFinal = deVinteaNoventaeNove(i - (i % 10)) + " e "
               + deZeroaDezanove(i % 10);
      }

      return valorFinal;
   }

   private static String deVinteaNoventaeNove(int i) {

      String valorFinal;

      switch (i) {
         case 20:
            valorFinal = "vinte";
            break;
         case 30:
            valorFinal = "trinta";
            break;
         case 40:
            valorFinal = "quarenta";
            break;
         case 50:
            valorFinal = "cinquenta";
            break;
         case 60:
            valorFinal = "sessenta";
            break;
         case 70:
            valorFinal = "setenta";
            break;
         case 80:
            valorFinal = "oitenta";
            break;
         case 90:
            valorFinal = "noventa";
            break;
         case 100:
            valorFinal = "cem";
            break;
         default:
            valorFinal = "A BUG is chewing method deVinteaNoventaeNove";
      }
      return valorFinal;
   }

   private static String menorQueMil(int i) {

      String valorFinal;

      if (i % 100 == 0)
         valorFinal = deCemaNoveNoveNove(i);
      else if (i < 200)
         valorFinal = "cento e " + triagemInicial(String.valueOf(i % 100));
      else
         valorFinal = deCemaNoveNoveNove(i - i % 100) + " e " + triagemInicial(String.valueOf(i % 100));

      return valorFinal;
   }

   private static String deCemaNoveNoveNove(int i) {

      String valorFinal;

      switch (i) {

         case 200:
            valorFinal = "duzentos";
            break;
         case 300:
            valorFinal = "trezentos";
            break;
         case 400:
            valorFinal = "quatrocentos";
            break;
         case 500:
            valorFinal = "quinhentos";
            break;
         case 600:
            valorFinal = "seiscentos";
            break;
         case 700:
            valorFinal = "setecentos";
            break;
         case 800:
            valorFinal = "oitocentos";
            break;
         case 900:
            valorFinal = "novecentos";
            break;
         case 1000:
            valorFinal = "mil";
            break;
         default:
            valorFinal = "A BUG is chewing method deCemaNoveNoveNove";
      }
      return valorFinal;
   }

   private static String menorQueUmMilhao(int i) {

      String valorFinal;

      if (i % 1000 == 0) {
         valorFinal = triagemInicial(String.valueOf(i / 1000)) + " mil";
      } else if (i % 1000 <= 100 || ((i % 1000) % 100) == 0) {
         if (i / 1000 == 1) {
            valorFinal = "mil e " + triagemInicial(String.valueOf(i % 1000));
         } else {
            valorFinal = triagemInicial(String.valueOf(i / 1000)) + " mil e " + triagemInicial(String.valueOf(i % 1000));
         }
      } else {
         if (i / 1000 == 1) {
            valorFinal = "mil " + triagemInicial(String.valueOf(i % 1000));
         } else {
            valorFinal = triagemInicial(String.valueOf(i / 1000)) + " mil " + triagemInicial(String.valueOf(i % 1000));
         }
      }
      return valorFinal;
   }

   private static boolean levaE(String grandeza) {
      // Método para verificar a necessidade de
      //  utilização de um "e" logo após a grandeza maior do inteiro.
      // Exs:
      // 1.000.001 = "um milhão e um" (verifica as condições) - devolve TRUE
      // 1.000.200 = "um milhão e duzentos" (verifica as condições) - devolve TRUE
      // 1.200.000 = "um milhão e duzentos mil" (verifica as condições) - devolve TRUE
      // 1.000.101 = "um milhão, cento e um" (NÃO verifica as condições - logo a seguir a milhão, leva uma vírgula) - devolve FALSE

      boolean comE = false;

      BigInteger grandezaA = new BigInteger(grandeza);
      BigInteger grandezaB = new BigInteger(grandeza);

      int powerVal = 0;
      while (grandezaB.compareTo(BigInteger.valueOf(1_000_000)) >= 0) {
         grandezaB = grandezaB.divide(BigInteger.valueOf(1_000_000));
         powerVal++;
      }

      BigInteger resto = new BigInteger("1");
      BigInteger comparador = new BigInteger("1000000"); // Um milhão
      comparador = comparador.pow(powerVal);
      resto = grandezaA.mod(comparador);

      // Teste Condição 1:  Resto da grandeza a dividir pelo comparador é menor ou igual a 100 (ex input: 1.0000.100)

      if (resto.compareTo(BigInteger.valueOf(100)) <= 0) {
         comE = true;
         return comE;
      } else {
         comE = false;
      }

      //  Condição 2

      BigInteger resto2 = new BigInteger("1");
      BigInteger comparador2 = new BigInteger("1");
      comparador2 = comparador.divide(BigInteger.valueOf(10));

      for (BigInteger z = new BigInteger("100000"), x = new BigInteger("1000");
           z.compareTo(comparador2) <= 0;
           z = z.multiply(BigInteger.valueOf(1_000)), x = x.multiply(BigInteger.valueOf(10))) {

         resto2 = resto.mod(x);

         if (resto.compareTo(z) <= 0 && resto2.compareTo(BigInteger.valueOf(0)) == 0) {
            comE = true;
            return comE;
         } else {
            comE = false;
         }
      }
      //  Condição 3

      BigInteger resto3 = new BigInteger("1");

      for (BigInteger w = new BigInteger("1000"), y = new BigInteger("100");
           w.compareTo(comparador) <= 0;
           w = w.multiply(BigInteger.valueOf(1_000)), y = y.multiply(BigInteger.valueOf(1000))) {

         resto3 = resto.mod(y);

         if (resto.compareTo(w) <= 0 && resto3.compareTo(BigInteger.valueOf(0)) == 0) {
            comE = true;
            return comE;
         } else {
            comE = false;
         }
      }
      return comE;
   }

   private static String metodoMilionario(String i, String preExt, String grand) {  
      // Médodo para trabalhar todas as grandezas acima de 1 milhão.
      // a uniformidade de critérios na terminologia dos número acima desta grandeza 
      // permite a recursividade deste método.

      BigInteger grandeza = new BigInteger(grand);
      String singular = preExt + "\u00E3o", plural = preExt + "\u00F5es";

      String valorFinal;
      BigInteger inputVal = new BigInteger(i);

      BigInteger i2 = inputVal.divide(grandeza);
      BigInteger i3 = inputVal.mod(grandeza);

      if (i3.compareTo(BigInteger.valueOf(0)) == 0) {
         if (inputVal.compareTo(grandeza) == 0) {
            valorFinal = "um " + singular;
         } else {
            inputVal = inputVal.divide(grandeza);
            valorFinal = triagemInicial(inputVal.toString()) + " " + plural;
         }
      } else if (levaE(i)) {
         if (i2.compareTo(BigInteger.valueOf(1)) == 0) {
            valorFinal = "um " + singular + " e " + triagemInicial(i3.toString());
         } else {
            valorFinal = triagemInicial(i2.toString()) + " " + plural + " e " + triagemInicial(i3.toString());
         }
      } else {
         if (i2.compareTo(BigInteger.valueOf(1)) == 0) {
            valorFinal = "um " + singular + ", " + triagemInicial(i3.toString());
         } else {
            valorFinal = triagemInicial(i2.toString()) + " " + plural + ", " + triagemInicial(i3.toString());
         }
      }
      return valorFinal;
   }
}
A classe como está já não permite utilização directa a partir da linha de comando (como em alguns exemplos acima) e tem mesmo de ser chamada a partir de outra classe, ou, melhor maneira, utilizada através do único método de entrada, que tem por nome "extenso", e que aceita uma String como argumento.
«The most interesting characters are the ones who lie to themselves.» - Paul Schrader, acerca de Travis Bickle.

«One is starved for Technicolor up there.» - Conductor 71 in A Matter of Life and Death

Câmara Subjectiva
Post Reply