Jump to content
Carlos Rocha

Erro em array dinâmico

Recommended Posts

Carlos Rocha

Bom, agora tive um tempo para retomar os estudos.

Fiz uma mudança aqui e me compliquei em uma coisa;

O código abaixo faz a contabilidade certinho. Mas o método setLenth esta dando erro.

function TForm1.primos(n: integer): MatrizInteira;
var
 conta_indice, contador, i, j: integer;
 array_primos: MatrizInteira;
begin
 conta_indice := -1;
 for i := 2 to n do
 begin
   contador := 0;
   for j := 2 to i-1 do //aqui, o j nunca será igual à i
     begin
       if (i mod j = 0) then inc(contador);
     end;
   if contador = 0 then
      begin
        inc(conta_indice);
        ShowMessage(intTostr(i));
        setLength(array_primos, conta_indice);
        array_primos[conta_indice] := i;
      end;
 end;
 primos := array_primos;
end;

Onde será que esta o erro?

Share this post


Link to post
Share on other sites
Kline777

Acho que nao estás a usar muito bem esse SetLength aí... senao me engano ele destroi a informaçao do array quando o chamas... mas posso estar enganado.

Share this post


Link to post
Share on other sites
Carlos Rocha

Será que ele só poderá ser chamado uma só vez?

Será que não pode receber incremento de tamanho?

Share this post


Link to post
Share on other sites
nunopicado

O SetLength pode ser usado. Se o tamanho especificado for menor, há perda de dados, é claro.

Se for maior, tudo OK.

Qual é a declaração do tipo matrizinteira?


"A humanidade está a perder os seus génios... Aristóteles morreu, Newton já lá está, Einstein finou-se, e eu hoje não me estou a sentir bem!"

> Não esclareço dúvidas por PM: Indica a tua dúvida no quadro correcto do forum.

Share this post


Link to post
Share on other sites
Carlos Rocha

array of integer;

Mas não tem como o conta_indice zerar. Ele só zero quando terminam os dois for.

Então, uma vez incrementado, só aumenta.

Obrigado ao moderador por ter criado novo tópico para o assunto.

Edited by carcleo

Share this post


Link to post
Share on other sites
thoga31

Posso estar a ver mal, mas conta_indice começa com -1, depois incrementa para 0, fazes um SetLength, e atribuis ao índice 0 um valor. Pois está claro que está mal.

SetLength define o comprimento do array, e não os seus índices. Exemplo:

var arr : array of integer;

SetLength(arr, 0); // a array não tem qualquer dimensão, tem 0 elementos!
arr[0] := valor    // Erro do tipo EAccessViolation.

SetLength(arr, 1); // a array tem agora 1 elemento, cujo índice é 0!
arr[0] := valor;   // atribuição correcta.


Knowledge is free!

Share this post


Link to post
Share on other sites
Carlos Rocha

O -1 era apenas um teste que esqueci-me de apagar

function TForm1.primos(n: integer): MatrizInteira;
var
 conta_indice, contador, i, j: integer;
 array_primos: MatrizInteira;
begin
 conta_indice := 0;
 for i := 2 to n do
 begin
   contador := 0;
   for j := 2 to i-1 do //aqui, o j nunca será igual à i e sempre será menor que i
     begin
       if (i mod j = 0) then inc(contador);
     end;
   if contador = 0 then
      begin
        inc(conta_indice);
        ShowMessage(intTostr(i));
        setLength(array_primos, conta_indice);
        array_primos[conta_indice] := i;
      end;
 end;
 primos := array_primos;
end;                   

Sabe o que eu notei?

No primeiro for, que faz de i:=2 até n (n=12), esta fazendo 12 interações. Não deveriam ser 10?

Share this post


Link to post
Share on other sites
Carlos Rocha

Não, não. Retirei tudo aqui e deixei só o loop e deu certo.

Mas fiz outra observação aqui no segundo for:

Quando entro nesse if

      if contador = 0 then
       begin
        inc(conta_indice);
        ShowMessage(intTostr(conta_indice));
        //setLength(array_primos, conta_indice);
        //array_primos[conta_indice] := i;
       end;                               

Enquanto mando apenas imprimir o valor de conta_indice, ele vai certim, 1,2,3,4,5.

Quando descomento a linha do SetLength, ele depois dos 1,2,3,4,5, ele parece que entra em um loop por conta própria e continua,

1,2,3,4,5,0,1,2,3,4

E ai para.

Edited by carcleo

Share this post


Link to post
Share on other sites
thoga31

Claro, repara bem na primeira iteração: i começa em 2 mas no segundo ciclo vais de 2 até i-1: se i=2, então i-1=1, logo este ciclo nunca decorre. Só decorre na segunda iteração, em que i=3, pelo que i-1=2, e o segundo ciclo tem 1 iteração.

Tens de ver bem estas coisas, toma atenção.

Edited by thoga31

Knowledge is free!

Share this post


Link to post
Share on other sites
Carlos Rocha

Deu certo agora.

unit principal;

{$mode objfpc}{$H+}

interface

uses
 Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, StdCtrls;

type
 MatrizInteira = array of integer;
 { TForm1 }

 TForm1 = class(TForm)
   Button13: TButton;
   Button14: TButton;
   Edit1: TEdit;
   Edit2: TEdit;
   Edit3: TEdit;
   Edit4: TEdit;
   Edit5: TEdit;
   Edit6: TEdit;
   Edit7: TEdit;
   Edit8: TEdit;
   Edit9: TEdit;
   Edit10: TEdit;
   Edit11: TEdit;
   Edit12: TEdit;
   procedure Button14Click(Sender: TObject);
   procedure FormCreate(Sender: TObject);
   function primos(n: integer): MatrizInteira;
 private
   { private declarations }
 public
   { public declarations }
 end;

var
 Form1: TForm1;
 n: integer=12;

implementation

{$R *.lfm}

{ TForm1 }

function TForm1.primos(n: integer): MatrizInteira;
var
 conta_indice, contador, i, j: integer;
 array_primos: MatrizInteira;
begin
 conta_indice := 0;
 for i := 1 to n do
   begin
     contador := 0;
      for j := 1 to i do
       begin
         if (j <> i) and (j<>1) then //aqui, o j sempre será diferente de i e de 1
            if (i mod j = 0) then inc(contador);
       end;
      if contador = 0 then
       begin
        inc(conta_indice);
        setLength(array_primos, conta_indice);
        array_primos[conta_indice-1] := i;
       end;
   end;
 primos := array_primos;
end;

procedure TForm1.Button14Click(Sender: TObject);
var
 b: integer;
 array_primos: MatrizInteira;
begin
 array_primos:=primos(n);
 for b := low(array_primos) to high(array_primos) do
  begin
    (FindComponent('edit'+IntToStr(array_primos[b])) As TEdit).font.color :=clRed;
    (FindComponent('edit'+IntToStr(array_primos[b])) As TEdit).Text :=IntToStr(array_primos[b]);
  end;
end;

procedure TForm1.FormCreate(Sender: TObject);
var
 a: integer;
begin
 for a := 1 to n do
    (FindComponent('edit'+IntToStr(a)) As TEdit).Text :=IntToStr(a);
end;

end. 

Obrigado.

Edited by carcleo

Share this post


Link to post
Share on other sites
thoga31

Posso só dar uma opinião? O teu código é super ineficiente a calcular os nºs primos. :confused:

EDIT: O que é isto?? É assim que calculas números primos?? :O

if (j <> i) and (j<>1) then

Edited by thoga31

Knowledge is free!

Share this post


Link to post
Share on other sites
Carlos Rocha

Sim amigo, preciso me certificar de j nunca será nem 1 e nem igual a 1 (condição de numero primos).

Pois todos números são divisíveis por 1 e por eles mesmos. Mas, apenas os primos são diviseis só por 1 e por eles mesmos.

Entendeu? Alguma ideia de melhora para o código?

Share this post


Link to post
Share on other sites
Flinger

Fazes o primeiro ciclo a percorrer os inteiros todos, e o segundo a ir apenas de 2 (assim já não tens de testar para j =1) até i-1 (assim já não tens de testar o i==j).

Mas uma solução mais eficiente é testar a divisão apenas pelos números que já são primos :D

um exemplo:

Está a testar o número 23. Vale apena testar se é divisível por 4, sendo que não é divisível por 2???

Além disso, no caso do número 22, sendo que já descobriste que é divisível por 2, vale apena continuar a testar os restantes números? Basta ser divisível por 2 para já não ser primo...

Edited by Flinger

Share this post


Link to post
Share on other sites
thoga31

Sim amigo, preciso me certificar de j nunca será nem 1 e nem igual a 1 (condição de numero primos).

Começa o ciclo em j=2 e termina em n-1, nunca irás precisar dessa condição. Mas a ser bem feito, o ciclo deve terminar muito antes de n-1. Já vais ver.

Pois todos números são divisíveis por 1 e por eles mesmos. Mas, apenas os primos são diviseis só por 1 e por eles mesmos.

Isto não tem de estar directamente escrito no código.

@carcleo, tens de simplificar imenso o teu código. Ele é extremamente ineficiente. Tu tens ciclos com iterações desnecessárias que são controladas internamente por condições (aquele j<>i and j<>1, portanto). Tu fazes cálculos desnecessários pois não controlas o que já tens calculado.

Pensa nisto: para o número 8, tu vais testar a divisão por 1 (controlada numa condição - para que iteras isto, então?), 2, 3, 4, 5, 6, 7 e 8 (também controlada pela condição, outra vez algo desnecessário).

Vale a pena testar a divisão por 1 e por 8? Não! Qualquer número, primo ou não primo, é sempre divisível por 1 e por ele mesmo. n/n=1 e n/1=n sempre. Tira isto das tuas condições e ciclos. É mero desperdício de recursos.

Vale a pena testar a divisão por 5, 6, 7? Consideremos a fracção n/d. Se d > n/2, então 1 <= n/d < 2. Exemplo: divides 8 pela sua metade, que é 4: 8/4=2. A partir daqui, com denominadores maiores que a metade (d>4), a fracção vai sempre ter um resultado menor que 2 e não inteiro! Não vale a pena testar os denominadores acima da metade do número.

Vale a pena testar a divisão por 3 e adiante? Se 8/2=4 com resto 0, já sabemos que 8 não é primo - porquê continuar a fazer cálculos?

Exemplo clássico: mete esse teu código a calcular se 1.000.000.000 é um número primo. Daqui a umas horas falamos, porque o teu código vai demorar imenso. Com estas simplificações obtias a resposta em milésimos de segundo - 1.000.000.000 / 2 = 500.000.000 com resto 0, pelo que não é primo - está calculado.

Edited by thoga31

Knowledge is free!

Share this post


Link to post
Share on other sites
Carlos Rocha

Ok.,

O que acham desse código então:

function TForm1.primos(n: integer): MatrizInteira;
var
 conta_indice, contador, i, j: integer;
 array_primos: MatrizInteira;
begin
 conta_indice := 0;
 for i := 1 to n do
begin
  contador := 0;
   for j := 2 to i-1 do
	begin
	  if (i mod j = 0) then inc(contador);
	  if contador>0 then break;
	end;
   if contador = 0 then
	begin
	 inc(conta_indice);
	 setLength(array_primos, conta_indice);
	 array_primos[conta_indice-1] := i;
	end;
end;
 primos := array_primos;
end;

Melhorou?

Sim, a troca de cor evidencia os primos.

Lembrete: tenho apenas 12 Tedits no form. Por isso coloquei n=12. rsrs

Obrigado por frechares o outro tópico., Eu deveria ter pedido isto!

Edited by carcleo

Share this post


Link to post
Share on other sites
thoga31

Está melhor, sim. No entanto, a meu ver, complicaste com o uso de tanta variável.

      for j := 2 to i-1 do

O que é que eu falei cerca deste limite? Não pode ser menor do que i-1?

Sim, a troca de cor evidencia os primos.

Não, basta fazeres uma vez. Se o ciclo tiver 1000 iterações, estás sempre a mandar mudar a cor - são 1000 operações para o caixote do lixo. É só para aquecer a CPU?


Knowledge is free!

Share this post


Link to post
Share on other sites
Carlos Rocha

Observaste:

for j := 2 to i-1 do
    begin
        if (i mod j = 0) then inc(contador);
        if contador>0 then break;
    end;

na quarta linha, na primeira verificação de que não é primo já salto do laço.

E não, a mudança de cor não esta vinculada ai ciclo total. Mas sim, ao resultado da função que ´é um array of integer com apenas os números primos

Edited by carcleo

Share this post


Link to post
Share on other sites
thoga31

Que confusão... é o que dá criar múltiplos tópicos. -.-'

No outro código do outro lado, no outro tópico, tinhas um ciclo while que incrementava uma variável e fazia sempre a mesma operação: mudar a cor para azul. Aquilo que eu te estava a dizer é que isto é um desperdício.

Outra coisa é esse código dos nºs primos, que está mais eficiente.

Por último, reduz o limite superior do ciclo for para i div 2.

Edited by thoga31

Knowledge is free!

Share this post


Link to post
Share on other sites
Carlos Rocha

Por último, reduz o limite superior do ciclo for para i div 2.

Mas, quando verifico que o contador é superior a 0 uma vez não desobriga essa ação não?

Outra coisa: o outro tópico, era outro assunto. Embora o mesmo exercício, o outro era para transformar uma string em nome de edit

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×
×
  • Create New...

Important Information

By using this site you accept our Terms of Use and Privacy Policy. We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.