Ir para o conteúdo
carcleo

Erro em array dinâmico

Mensagens Recomendadas

carcleo

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?

Partilhar esta mensagem


Ligação para a mensagem
Partilhar noutros 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.

Partilhar esta mensagem


Ligação para a mensagem
Partilhar noutros 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.

Partilhar esta mensagem


Ligação para a mensagem
Partilhar noutros sites
carcleo

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.

Editado por carcleo

Partilhar esta mensagem


Ligação para a mensagem
Partilhar noutros 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! | Occasional Fortnite player

Partilhar esta mensagem


Ligação para a mensagem
Partilhar noutros sites
carcleo

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?

Partilhar esta mensagem


Ligação para a mensagem
Partilhar noutros sites
carcleo

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.

Editado por carcleo

Partilhar esta mensagem


Ligação para a mensagem
Partilhar noutros 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.

Editado por thoga31

Knowledge is free! | Occasional Fortnite player

Partilhar esta mensagem


Ligação para a mensagem
Partilhar noutros sites
carcleo

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.

Editado por carcleo

Partilhar esta mensagem


Ligação para a mensagem
Partilhar noutros 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

Editado por thoga31

Knowledge is free! | Occasional Fortnite player

Partilhar esta mensagem


Ligação para a mensagem
Partilhar noutros sites
carcleo

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?

Partilhar esta mensagem


Ligação para a mensagem
Partilhar noutros 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...

Editado por Flinger

Partilhar esta mensagem


Ligação para a mensagem
Partilhar noutros 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.

Editado por thoga31

Knowledge is free! | Occasional Fortnite player

Partilhar esta mensagem


Ligação para a mensagem
Partilhar noutros sites
carcleo

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!

Editado por carcleo

Partilhar esta mensagem


Ligação para a mensagem
Partilhar noutros 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! | Occasional Fortnite player

Partilhar esta mensagem


Ligação para a mensagem
Partilhar noutros sites
carcleo

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

Editado por carcleo

Partilhar esta mensagem


Ligação para a mensagem
Partilhar noutros 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.

Editado por thoga31

Knowledge is free! | Occasional Fortnite player

Partilhar esta mensagem


Ligação para a mensagem
Partilhar noutros sites
carcleo

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

Partilhar esta mensagem


Ligação para a mensagem
Partilhar noutros sites
thoga31

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

Não. Se o número n for primo, estás a correr o ciclo n-2 vezes, metade das quais é desnecessário.

A optimização deve ser a maior possível. Se chegas a i div 2 e não houve nenhum resto 0, significa que é primo - para que é que vais continuar?

A única explicação que encontro é que adoras mesmo aquecer a CPU just for fun...

Editado por thoga31

Knowledge is free! | Occasional Fortnite player

Partilhar esta mensagem


Ligação para a mensagem
Partilhar noutros sites
carcleo

em que posição?

Aqui?


   for j := 2 to i-1 do
	begin

	 [i] if (i mod 2 = 0) then break;[/i]


	  if (i mod j = 0) then inc(contador);

	  if contador>0 then break;
	end;							

Editado por carcleo

Partilhar esta mensagem


Ligação para a mensagem
Partilhar noutros sites
thoga31

Imagina a situação de um número primo - chega à condição i div 2 = 0 (e não mod!) e interrompe - este é o limite do ciclo, ele nunca, mas nunca, vai chegar a i-1.

Portanto, porque não metes assim?

for j := 2 to i div 2

Oh gente, pensai um pouco.


Knowledge is free! | Occasional Fortnite player

Partilhar esta mensagem


Ligação para a mensagem
Partilhar noutros sites

Crie uma conta ou ligue-se para comentar

Só membros podem comentar

Criar nova conta

Registe para ter uma conta na nossa comunidade. É fácil!

Registar nova conta

Entra

Já tem conta? Inicie sessão aqui.

Entrar Agora

×

Aviso Sobre Cookies

Ao usar este site você aceita os nossos Termos de Uso e Política de Privacidade. Este site usa cookies para disponibilizar funcionalidades personalizadas. Para mais informações visite esta página.