PDA

Ver la Versión Completa : Números impares desde 1 hasta 1000


Daniel2622
22-04-2017, 03:07:24
Hola, necesito ayuda con el siguiente ejercicio:
Escribe un programa que imprima en un objeto Memo los números impares desde 1 hasta 1000.

Este es el código que he construido:

procedure TForm1.BitBtn1Click(Sender: TObject);
var c,n1,n2:Integer;
a,b:Integer;
i:Integer;
begin
Memo1.Lines.Clear;
Memo1.Text:='Numeros impares';
if n1<n2 then
begin
a:=1;
b:=1000;
end
else
begin
a:=1000;
b:=1;
end;
For i:=a to b do
begin
c:=1;
while c<=1000 do
begin
Memo1.Lines.Add(
IntToStr(n1)+' '+IntToStr(n2)+FloatToStr(i mod c)!=0;
end;
Memo1.Lines.Add(' ');
end;
end;

Me dice que el operador de relación != es ilegal.

De antemano gracias por su ayuda.
Saludos.

Casimiro Notevi
22-04-2017, 03:35:52
Para empezar, copia y pega correctamente tu código, pues ese código seguro que no te compila.
Después revisa esa línea que te da error, mezclando texto con números, al mismo tiempo que haces una comparación/validación :rolleyes:

Y para futuras preguntas, recuerda poner títulos descriptivos al problema que tienes, y no pongas el enunciado de la tarea que te han encomendado.
¿Has leído ya nuestra guía de estilo (http://www.clubdelphi.com/foros/guiaestilo.php)?

Daniel2622
22-04-2017, 04:47:26
Es que así esta mi código y perdón por no hacer bien el título.

jhonny
22-04-2017, 16:17:40
Voy a dejarte el siguiente código con el animo de que le tomes amor a la herramienta y no sólo se quede en que pudiste resolver la tarea, lo estoy haciendo sin Delphi a la mano sin embargo lee bien los comentarios y compara con el código que enviaste para que veas lo que estabas haciendo errado y cómo puedes mejorar tu producción de código, un abrazo:

procedure TForm1.BitBtn1Click(Sender: TObject);
var
i :Integer;
begin
Memo1.Text:='Numeros impares'; //Al usar la propiedad Text reemplazas todo el texto que tenías, por eso
//no necesitas Clear acá
for i := 1 to 1000 do //Recorremos los números que necesitas evaluar, del 1 al 1000
begin
if ((i mod 2) <> 0) then //Los números divididos por 2 cuyo residuo es 0, son pares.
Memo1.Lines.Add(IntToStr(i));
end;
end;


Espero te sirva de ayuda y compartas con nosotros la calificación... de o de la profe ;).

Daniel2622
22-04-2017, 19:52:21
Muchas gracias jhonny me fue de gran ayuda tu código, y si ya vi cuales eran mis errores.

De nuevo muchas gracias y Saludos.

roman
23-04-2017, 00:18:22
procedure TForm1.BitBtn1Click(Sender: TObject);
var
i :Integer;
begin
Memo1.Text:='Numeros impares'; //Al usar la propiedad Text reemplazas todo el texto que tenías, por eso
//no necesitas Clear acá
for i := 1 to 1000 do //Recorremos los números que necesitas evaluar, del 1 al 1000
begin
if ((i mod 2) = 0) then //Los números divididos por 2 cuyo residuo es 0, son pares.
Memo1.Lines.Add(IntToStr(i));
end;
end;



Yo creo que podemos evitarnos el condicional:


procedure TForm1.BitBtn1Click(Sender: TObject);
var
i :Integer;
begin
Memo1.Text:='Numeros impares';

for i := 0 to 499 do
begin
Memo1.Lines.Add(IntToStr(2*i + 1));
end;
end;


LineComment Saludos

jhonny
23-04-2017, 06:11:42
Yo creo que podemos evitarnos el condicional:


procedure TForm1.BitBtn1Click(Sender: TObject);
var
i :Integer;
begin
Memo1.Text:='Numeros impares';

for i := 0 to 499 do
begin
Memo1.Lines.Add(IntToStr(2*i + 1));
end;
end;


LineComment Saludos


¡Estupendo!, al ver que has reducido el tiempo de ejecución a la mitad usando ingenio puro. Sólo resta mejorar aún más la velocidad de ejecución, aprovechando las nuevas herramientas, sin ser algo perfecto porque faltaría el tema del ordenamiento (aunque en el ejercicio no piden que sea ordenado)... así:

Agregando al uses las unidades System.Threading y System.SyncObjs...

procedure TForm1.Button2Click(Sender: TObject);
var
vCS: TCriticalSection;
vStringList: TStringList;
begin
vStringList := TStringList.Create;
try
vCS := TCriticalSection.Create;

TParallel.&For(0, 499,
procedure(pI: Integer)
begin
vCS.Acquire;
vStringList.Add((2 * pI + 1).ToString);
vCS.Release;
end);

Memo1.Text := 'Numeros impares';
Memo1.Lines.AddStrings(vStringList);
finally
vCS.DisposeOf;
vStringList.DisposeOf;
end;
end;

Ejecutando este mismo ejercicio hasta 10000 en lugar de 1000, en mi maquina con el método anterior se demoró 3150 milisegundos en promedio, sin embargo con el segundo método 295 milisegundos en promedio.

O cuando lo subí a 100000 el promedio con el ejercicio anterior fue de 33843 milisegundos, mientras que con el segundo ha sido de 2900 milisegundos.

Casimiro Notevi
23-04-2017, 15:50:55
¿Eso es de los últimos delphi? ¿qué hace, aprovechar el multiproceso?

jhonny
23-04-2017, 16:33:36
¿Eso es de los últimos delphi? ¿qué hace, aprovechar el multiproceso?

Así es ^\||/, tengo entendido que Lazarus también tiene TParallel pero recibe es un puntero a un procedimiento... aunque no lo he probado.

escafandra
23-04-2017, 19:36:53
El código puede escribirse de muchas formas pero lo que lo retrasa el la línea Memo1.Lines.Add(

// Similar al de roman con multiplicación binaria
var
i :Integer;
begin
Memo1.Text:='Numeros impares';

for i := 0 to 499 do
Memo1.Lines.Add(IntToStr((i shl 1) + 1));
end;



var
i: integer;
begin
i:= 1;
Memo1.Text:='Numeros impares';
repeat
Memo1.Lines.Add(IntToStr(i));
inc(i,2);
until i > 999;
end;


Si hacemos que el memo se escriba por mensajes, cualquiera de las formas se ejecuta en menos de 10 milisegundos:

procedure TForm1.Button1Click(Sender: TObject);
var
i: integer;
begin
i:= 1;
Memo1.Text:='Numeros impares';
repeat
//Memo1.Lines.Add(IntToStr(i));
PostMessage(Handle, WM_USER, i, 0);
inc(i,2);
until i > 999;
end;

procedure TForm1.OnMsg(var Msg: TMessage);
begin
Memo1.Lines.Add(IntToStr(Msg.WParam));
end;


Los bucles terminarán en escasos milisegundos aunque el Memo tardará en llenarse de forma ordenada lo que tarde Windows en tramitar la cola de mensajes.


Saludos.

Casimiro Notevi
23-04-2017, 21:36:30
El código puede escribirse de muchas formas pero lo que lo retrasa el la línea Memo1.Lines.Add(
Se puede almacenar en memoria y luego, al terminar, asignarlo al memo. Así será incluso más rápido.

ecfisa
23-04-2017, 21:44:34
Hola.

Si, el método Add es muy lento; yo he obtenido buenos resultados operando sobre una variable de tipo string y asignándola luego a la propiedad Text.

...
begin
s := '';
for i := 0 to 499 do
s := s + IntToStr(2 * i + 1) + CRLF; // (#13#10)
Memo1.Text := s;
end;

Resultados para 1000 números usando la propuesta de roman en todos los casos:

Concatenando : 203 µs.
Mensaje : 261 µs.
Método Add : 3320579 µs.

Como se vé, ya sea concatenando o usando la cola de mensajes como sugirió escafandra la diferencia es astronómica. Lo que no pude probar son las nuevas herramientas que menciona jhonny, pero supongo que mejorarán mas los tiempos.

Saludos :)

Edito: Mas o menos como lo que dijo Casimiro mientras componía este :)

Casimiro Notevi
24-04-2017, 01:03:48
Si, el método Add es muy lento; yo he obtenido buenos resultados operando sobre una variable de tipo string y asignándola luego a la propiedad Text.
Cierto, el manejo de cadenas de texto suele ser siempre lo más lento de todo.

roman
24-04-2017, 15:50:06
Bueno, pero antes de agregar líneas por PostMessage o concatenar y pegar, ¿han intentado un BeginUpdate - EndUpdate?

LineComment Saludos

jhonny
24-04-2017, 17:15:30
Bueno, pero antes de agregar líneas por PostMessage o concatenar y pegar, ¿han intentado un BeginUpdate - EndUpdate?

LineComment Saludos

Ahhh caramba, ¿VCL también tiene estos métodos?... porque los he usado es en FMX.

roman
24-04-2017, 17:18:59
Sí, claro. Ahí han estado por siempre ;)

LineComment Saludos

jhonny
24-04-2017, 17:33:08
Sí, claro. Ahí han estado por siempre ;)

LineComment Saludos

Roman, definitivamente, sos el maestro :) (bueno, eso siempre se ha sabido), acabo de probarlo y funciona a las mil maravillas... no encontraba el método porque estaba buscando en el control, como si se tratara de FMX... sin embargo sí que ha funcionado a las mil maravillas al utilizarlo en el TStrings que es a donde ha pertenecido.

Gracias hombre.

roman
24-04-2017, 18:02:39
Oye jhonny, ¿podrías explicar un poco eso del TParallel? Bueno, es que ni la sintaxis entiendo :o ¿qué hace un & ahí? :eek:

LineComment Saludos

jhonny
24-04-2017, 19:46:34
Oye jhonny, ¿podrías explicar un poco eso del TParallel? Bueno, es que ni la sintaxis entiendo :o ¿qué hace un & ahí? :eek:

LineComment Saludos

Hombre, claro, espero ser de ayuda, ahí va:

TParallel es una clase que tiene varios métodos cuyo objetivo es ejecutar tareas (como su nombre lo indica) en paralelo, TParallel te hace más fácil el uso de los TTask (Que es como la nueva clase para manejo de hilos, si no que usa el TMonitor para la administración de la ejecución de hilos), es especial para aquellos casos de uso cotidiano, como lo es en este caso un mero for de toda la vida.

TParallel tiene otro métodos a parte del for, el cual es Join... que también sirve para ejecutar varias tareas al tiempo... pero vamos a la explicación de la sintaxis y del asunto del &.

1) El &, este es un comodín que agregaron en Delphi (No recuerdo desde cual versión realmente), el cual sirve para que puedas ponerle como nombre a tus métodos aquellas palabras que siempre han sido reservadas... para el caso que vemos, la palabra For... entonces, podrías por ejemplo ponerle a un método de una clase tuya, el nombre While, anteponiendo simplemente &While... incluso podrías llamar tu método While usando TTuClase.While, sin el &... sin embargo soy de los que prefiere ponerle el & para evitar posibles "situaciones".

2) La sintaxis de TParallel:

procedure TForm1.Button2Click(Sender: TObject);
var
vCS: TCriticalSection;
vStringList: TStringList;
begin
vStringList := TStringList.Create;
try
vCS := TCriticalSection.Create;

TParallel.&For(0, 499,
procedure(pI: Integer)
begin
vCS.Acquire;
vStringList.Add((2 * pI + 1).ToString);
vCS.Release;
end);

Memo1.Text := 'Numeros impares';
Memo1.Lines.AddStrings(vStringList);
finally
vCS.DisposeOf;
vStringList.DisposeOf;
end;
end;



TParallel tiene "dos" métodos de clase principales (un método de clase nos permite hacer llamado al método sin necesidad de instanciar la clase, por eso está declarado como class function &For( ) dentro de la clase) y puedo llamarlo directamente con TParallel.&For.

TParallel tiene 2 métodos de clase los cuales son For y Join... ambos tienen bastantes sobre cargas, en el For una de sus sobrecargas es aquella que usé para este caso, en ella pasas el primer valor del recorrido, el último y un método anónimo... en el caso del método anónimo (Que es el que está entre el procedure...begin...end)... éste devuelve el número que está "recorriendo" (lo pongo entre comillas porque este "recorrido" es en paralelo), este número lo capturas en una variable (en el caso del ejemplo me inventé el nombre pI que es el que contendrá el valor de dicho número... esta variable puede tener el nombre que quieras mientras respetes la convención del nombre de una variable) y este método anónimo es el que ejecutará todo aquello que quieres que se ejecute en paralelo.

El TCriticalSection es en sí la "señal de semaforización" para que se ejecute lo que está entre el Acquire y el Release, cuando el proceso anterior haya terminado... es el TCriticalSection que siempre se ha usado con TThread.


Es muy curioso que preguntes por esto, porque tengo hace rato en el tintero un artículo donde quiero explicar este tema, aunque nunca encuentro es ¿Por dónde comenzar?, puesto que hay que explicar primero el uso de métodos anónimos, los class methods o bueno... tal vez al revés... me ayudaría mucho tu retroalimentación acerca de si se entendió más o menos lo que trato de explicar y lo que ha quedado en el completo aire para complementar y explicarlo mejor.

ecfisa
24-04-2017, 20:04:56
Hola roman.
Bueno, pero antes de agregar líneas por PostMessage o concatenar y pegar, ¿han intentado un BeginUpdate - EndUpdate?

LineComment Saludos
Olvidé incluirlos en las pruebas :o, tal vez por que recordaba que aunque mejoraba el tiempo, no lo hacía un rival de la concatenación.

Concatenando : 203 µs.
Mensaje : 261 µs.
Begin/Update : 858024 µs.
Método Add : 3320579 µs.


Saludos :)

roman
24-04-2017, 20:04:56
Muchas gracias jhonny. Sí que queda claro, aunque


nunca encuentro es ¿Por dónde comenzar?


para palurdos como yo comenzaría por una breve explicación de qué es eso de ejecutar tareas en paralelo y cuáles son las diferencias con el uso de threads.

Lo de las funciones anónimas y los métodos de clase lo dejaría sólo como referencia a otros artículos tuyos o de terceros.

LineComment Saludos

jhonny
24-04-2017, 20:58:13
Muchas gracias jhonny. Sí que queda claro, aunque



para palurdos como yo comenzaría por una breve explicación de qué es eso de ejecutar tareas en paralelo y cuáles son las diferencias con el uso de threads.

Lo de las funciones anónimas y los métodos de clase lo dejaría sólo como referencia a otros artículos tuyos o de terceros.

LineComment Saludos

Bueno Roman, si tú eres un palurdo, no sé qué soy yo entonces jejeje

Me alegra que se haya entendido y gracias por la retroalimentación, es un norte que necesitaba.

jhonny
24-04-2017, 21:01:19
Hola roman.

Olvidé incluirlos en las pruebas :o, tal vez por que recordaba que aunque mejoraba el tiempo, no lo hacía un rival de la concatenación.

Concatenando : 203 µs.
Mensaje : 261 µs.
Begin/Update : 858024 µs.
Método Add : 3320579 µs.


Saludos :)

Bueno, a mí el BeginUpdate/EndUpdate me ha arrojado los mismo números que me arroja el uso de TParallel. Que curioso todo. :eek:

Casimiro Notevi
24-04-2017, 21:22:48
Bueno, a mí el BeginUpdate/EndUpdate me ha arrojado los mismo números que me arroja el uso de TParallel. Que curioso todo. :eek:
¿Tenéis un ordenador compartido? :eek:

jhonny
24-04-2017, 21:44:36
¿Tenéis un ordenador compartido? :eek:

jejeje bueno, me refiero a que las pruebas que hice con el BeginUpdate/EndUpdate, me arrojaron el mismo resultado que me habían arrojado las pruebas que había hecho con TParallel en el ejercicio anterior... o sea, 295 milisegundo en promedio para 10000 como valor máximo.

Lo que me asombra es que a ecfisa se le haya demorado tanto usando BeginUpdate/EndUpdate.

roman
24-04-2017, 21:55:18
Lo que me asombra es que a ecfisa se le haya demorado tanto usando BeginUpdate/EndUpdate.

A mi me asombran sus números. No sé cómo los obtuvo. Para 1000 números yo obtengo:


Concatenando: Alrededor de 3,000 microsegundos
BeginUpdate/EndUpdate: Alrededor de 72,000 microsegundos


Uso esto:


type
ICounter = Interface
function Ellapsed(): Single;
function EllapsedAsString: String;
end;

TCounter = class(TInterfacedObject, ICounter)
private
Frequency, StartTicks: Int64;

public
constructor Create();
function Ellapsed(): Single;
function EllapsedAsString: String;
end;

{ TCounter }

constructor TCounter.Create;
begin
QueryPerformanceFrequency(Frequency);
QueryPerformanceCounter(StartTicks);
end;

function TCounter.Ellapsed: Single;
var
CurrentTicks: Int64;

begin
QueryPerformanceCounter(CurrentTicks);
Result := 1000000*(CurrentTicks - StartTicks);
Result := Result/Frequency;
end;

function TCounter.EllapsedAsString: String;
begin
Result := FormatFloat('0,000.0 µs', Ellapsed);
end;


Ejemplo:


var
Counter: ICounter;

begin
Counter := TCounter.Create();

// Proceso a medir

ShowMessage(Counter.EllapsedAsString);
end;


LineComment Saludos

jhonny
24-04-2017, 22:10:57
A mi me asombran sus números. No sé cómo los obtuvo. Para 1000 números yo obtengo:

Uso esto:

.
.
.



Yo uso este record: http://docwiki.embarcadero.com/Libraries/Seattle/en/System.Diagnostics.TStopwatch

Así:


var
sw: TStopWatch;
begin
sw := TStopWatch.Create;
sw.Start;
//Proceso a medir
sw.Stop;

Label1.Caption := sw.ElapsedMilliseconds.ToString; //Lugar donde muestro el resultado de la medición...
end;

roman
24-04-2017, 22:17:01
Tus resultados son en milisegundos y los míos en microsegundos. En realidad, lo hice en microsegundos porque ecfisa así lo hizo, o al menos es lo que indica la unidad µs que puso. Quizá sean milisegunods pero aún así, es demasiado tiempo para el beginupdate/endupdate. Aunque, ciertamente, es más lento este último método.

LineComment Saludos

jhonny
24-04-2017, 22:26:21
Tus resultados son en milisegundos y los míos en microsegundos. En realidad, lo hice en microsegundos porque ecfisa así lo hizo, o al menos es lo que indica la unidad µs que puso. Quizá sean milisegunods pero aún así, es demasiado tiempo para el beginupdate/endupdate. Aunque, ciertamente, es más lento este último método.

LineComment Saludos

Ciertamente, así es. El error mío no ha sido el tema de la conversión entre milisegundos y microsegundos, pues soy consciente que los resultados de ecfisa y los tuyos son en microsegundos... el error mío ha sido que he comparado los ejercicios incorrectos... debo comparar es el de String vs Begin/Update. Que ciertamente concatenar el String ha resultado mucho más rápido.

De todas maneras (Por si sirve de algo), para hacer el calculo con dicho record con todo el valor, se puede hacer así:


var
sw: TStopWatch;
begin
sw := TStopWatch.Create;
sw.Start;
//Proceso a medir
sw.Stop;

Label1.Caption := sw.Elapsed.ToString; //Que para el BeginUpdate/EndUpdate me devuelve 00:00:00.2985187 y concatenando 00:00:00.0021155
end;

ecfisa
25-04-2017, 03:55:58
Hola.

Pido disculpas, por que el tiempo transcurrido para la concatenación era de 2038 µs y no como erróneamente escribí 203 µs. :o
Luego al agregar el resultado de la prueba con BeginUpdate/EndUpdate, copié/pegué del mensaje #12 y reproduje el error en el mensaje #20.

Es decir que el único método que me arrojó valores por debajo de los 400 µs. fué el que se vale del uso de mensajes. Pero hay que aclarar que no es el tiempo total en completar la ejecución, ya que hay que sumarle lo que toma el S.O. para procesar la cola de mensajes para finalizar el mostrado ( se recibe el resultado cuando aún no terminó la impresión ).

Saludos :)