|
Antwort |
Registriert seit: 1. Mai 2016
Eine kleine Spielerei für mathematisch Interessierte. Sicher gehörend zur Kategorie: "Dinge, die die Welt nur selten braucht".
Es handelt sich um eine iterative Berechnung(Annäherung) zur Bestimmung von Wurzeln mit verschiedener Wurzelexponenten. Das Heron-Verfahren für die Berechnung der 2-ten Wurzel wurde vermutlich in der Schule gelehrt, jedoch läßt sich diese Formel auch auf Wurzeln mit größerem Wuzelexponent erweitern. Ich stelle das kleine Programm der Kritik der User des Forums und bin für Anregungen und Verbesserungsvorschläge dankbar. Entstanden ist das Ganze aus einer Aufgabenstellung zum Erlernen von Delphi (Uni Leipzig) zunächst mit der Umsetzung der 2-ten Wurzel und der Maßgabe, wer möchte könne als Zusatzaufgabe auch ein Programm entwickeln für die n-te Wurzel. Dazu habe ich einige Zeit gebraucht und die Hilfe des Internet in Anspruch genommen, um die entsprechende Formel zu finden. Danach entstand zunächt ein prozedurales Programm zur Berechnung von Wurzeln nach Heron, dass nunmehr objektorientiert weiterentwickelt wurde. Ich hoffe es macht ein wenig Spaß damit zu experimentieren und warte auf Reaktionen. |
nahpets
|
#21
Na klar kann ich 'nen Tipp geben.
Sonst hätt' ich ja nicht 'ne so blödsinnige Forderung gestellt. Am Beginn des Timerereignisses diesen ausschalten. Arbeit machen. Zeit bis zur nächsten vollen Sekunde berechnen. Timerintervall entsprechend setzen. Timer einschalten. Achso: Für die Zeitangaben reicht aber eigentlich ein Timer aus. Und die Zeit sollte man im Laufe der Ereignisabarbeitung nicht mehrfach auslesen, es könnten dann (marginale) Abweichungen auftreten |
Zitat |
Delphi 10.2 Tokyo Professional |
#22
ich stehe im winterlichen Nebel(also es ist kalt und ich sehe keinen Horizont) und nirgends ist ein blinkender Fernsehturm erkennbar. Also die Hinweise zum Timer (nur einen) habe ich berücksichtigt.
Doch was ich ggf. berechnen sollte ist mir überhaupt nicht klar. Ich habe nicht einmal einen Ansatzpunkt, worüber ich nachdenken könnte. Am Beginn des Timergerg. Timer aus; wo genau? Arbeit machen? welche denn? Zeit bis zur neuen Sec berechnen? wie das den bitte? Dementsprechend Timerintervall setzen; wäre schön, wenn ich da angekommen wäre! Timer anstellen, das würde mir sicher gelingen mit Timer.enabled:=true das ist wohl das einzige was ich an dieser Stelle könnte, doch den Timer ausschalten könnte ich wohl auch. Es ist mir auch wirklich nicht klar, was konkret unabhängig von der Programmierung, die Aufgabe an dieser STelle ist. Das Ding läuft doch so vor sich hin und zeigt die Zeit sogar in roemischen Zahlen!
Norbert
|
Zitat |
nahpets
|
#23
Also, damit ein Timer arbeitet, muss irgendwo Enabled auf True gesetzt worden sein. Das kann im Objektinspektor sein oder im Quelltext.
Im Timerereignis nun zu beginn den Timer ausschalten, also Enabled auf false. Jetzt wird ja allerlei im Ereignis gemacht. Zeit ermitteln, in römische Zahlen umwandeln, zur Anzeige bringen ... Dabei vergeht ja etwas Zeit. Bis zur nächsten vollen Sekunde (für die nächste Anzeige ...) besteht nun noch eine Zeitdifferenz von x Millisekunden (> 0 und < 999). Das Timerintervall ist im Objektinspektor (vermutlich) auf 1000 gesetzt. Die verbleibende Zeit bis zur nächsten vollen Sekunde ist aber nicht immer 1000 Ms. Daher muss hier die Zeitspanne zwischen Jetzt und der nächsten vollen Sekunden berechnet werden. Dieser Wert muss dann dem Timerintervall zugewiesen werden und anschließend Enabled wieder auf True gesetzt werden. Warum reite ich auf dem ganzen (hier im konkreten Fall) Quatsch rum? Stell' Dir vor, Du musst in regelmäßigen Abständen eine bestimmte Aufgabe erledigen. Z. B. genau zu jeder vollen Minute. Aber es kommt erschwerend hinzu: Unter bestimmten, ungünstigen, Bedingungen dauert die Erledigung der Aufgabe länger als eine Minute. Ein permanent laufender Timer, der stoisch alle 60000 MS "zuschlägt", würde also die Timerroutine bereits aufrufen, während die vorherige noch abgearbeitet wir. Das sollte verhindert werden. Da nicht bekannt ist, wie lange nun die Abarbeitung der Aufgabe in jedem Einzellfall dauert, kann man hier also nicht mit einem statischen Timerintervall arbeiten. Am Ende der Aufgabe muss also die Anzahl der Millisekunden bis zur nächsten vollen Minute berechnet werden, das Timerinterval entsprechend gesetzt werden und dann der Timer aktiviert werden. Oder mal einfach so als Spasserweiterung für Deine Uhr. Der Timer setzt jede Sekunde "Alles". Das Datum ändert sich aber nur einmal täglich. Dafür könnte man auch einen Timer nehmen. Beim Programmstart wird die Timerroutine einmal aufgerufen und dort wird dann die verbleibende Zeit in Millisekunden bis 0 Uhr berechnet, das Timerintervall gesetzt und der Timer eingeschaltet. Damit wird das Datum nur noch einmal täglich um 0 Uhr gesetzt. Nimm einfach mal an, dass Du da nicht einfach nur Text auf ein Label schreibst, sondern über eine Schnittstelle eine große mechanische Anzeigetafel ansteuern musst, dann ist es schon eine Überlegung wert, ob man das wirklich jede Sekunde macht oder nur wenn sich das Datum ändert. Als Spielerei könnte man jetzt ja auch noch einen Timer nehmen, der nur für die Anzeige der vollen Stunde zuständig ist. Und einen, der nur für die Anzeige der vollen Minuten zuständig ist. Und als letztes den für die Sekunden. Klar ist das für 'ne einfach Uhr völlig überdimensioniert, aber mal mit Zeiten rechnen und Zeitdifferenzen ermitteln, um damit ein bestimmtes, in der Zukunft liegendes, zu einem konkreten Zeitpunkt auszulösendes, Ereignis in Gang zubringen, ist schon 'ne Lernaufgabe |
Zitat |
Delphi 10.2 Tokyo Professional |
#24
ich habe die Zeitdifferenz gesondert gemessen. Sie beträgt 0 ms.
Delphi-Quellcode:
Ich habe sie jedoch nur ohne Umwandlung in roem. Zahlen gemessen, da so wie oben geschrieben kein Wert ausgegeben wird.
procedure TForm1.Button1Click(Sender: TObject);
var Present: TDateTime; Year, Month, Day, Hour, Min, Sec, MSec: Word; Zeit : String; startTime: Cardinal; begin timer2.Enabled:=false; startTime := GetTickCount; //Befehlesfolge deren Zeitdauer bestimmt werden soll Present:= Now; SysUtils.DecodeDate(Present, Year, Month, Day); Label1.Caption := 'Der heutige Tag ist der ' + Dezinroem(IntToStr(Day)) + ' des '+Dezinroem(inttoStr(Month)) +'. Monates ' + ' des Jahres ' + DezinRoem(IntToStr(Year)); SysUtils.DecodeTime(Present, Hour, Min, Sec, MSec); Label2.Caption := DezInRoem(inttostr(Hour))+ ' Stunden, '+DezInRoem(inttostr(Min))+' Minuten und ' +DezinRoem(inttostr(Sec))+' Sekunden und '+DezinRoem(inttostr(MSec)) +' Milisekunden'; Label4.Caption:=DezInRoem(inttostr(Hour)); Label5.caption:=DezInRoem(inttostr(Min)); Label6.caption:=DezInRoem(inttostr(Sec)); Label7.caption:=DezInRoem(inttostr(mSec)); ShowMessage('Die Routine benötigte etwa ' + DezInRoem(IntToStr(GetTickCount - startTime)) + 'ms'); timer2.Enabled:=true; end; Also ist alles so wie bislang geschrieben? Habe ich nun den richtigen Ansatz für die Berechnung der Zeitdifferenz? Wie gibt man eigentlich eine Null in roem. Zahlen aus? Das Problem übersteigt wohl doch meine Möglichkeiten.
Norbert
|
Zitat |
nahpets
|
#25
Bei den römischen Zahlen gibt es keine 0, kann von daher auch nicht ausgegeben werden
Was ich mit der Intervallberechnung bezwecken möchte, mal ein Beispiel: Das Programm wird um 21:36:45,567 gestartet und soll zu jeder vollen Sekunde ein Timerereignis ausführen. Wir benötigen hier also die Anzahl der Millisekunden zwischen 21:37:00,000 und 21:36:45,567 Das wäre das Timerintervall, das gesetzt werden müsste. Klar, bei so einer Aufgabe, wie der Ausgabe der Uhrzeit, ist die Laufzeit so kurz, dass eine Neuberechnung absurd erscheint. Wenn Du Deine Uhr aber mal über einen längeren Zeitraum laufen lässt, wirst Du bemerken, dass die Zahl der Millisekunden langsam, aber kontinuierlich, ansteigt. Bei meinem Rechner zwischen 2 und 3 Millisekunden pro Minute, das sind am Tag irgendwas zwischen 2880 und 4320 Millisekunden. Auf's Jahr gerechnet werden daraus zwischen 1051200 und 1576800 Millisekunden, gibt also sowas um die 20 Sekunden. Hier wäre es schön, wenn die Uhr diesen Fehler selbst korrigieren könnte. Dazu muss halt ab und an das Timerintervall etwas kürzer als 1000 Millisekunden sein. So, wie es auf meinem Rechner aussieht, zwei bis dreimal pro Minute. Hier mal ein paar Fragmente aus 'ner von TTimer abgeleiteten Komponente von mir:
Delphi-Quellcode:
Der Aufruf könnte dann z. B. so erfolgen:
const
iOneSecond : Integer = 1000; // eine Sekunde in Millisekunden iFiveSeconds : Integer = 5000; // fünf Sekunden in Millisekunden iFifteenSeconds : Integer = 15000; // fünfzehn Sekunden in Millisekunden iOneMinute : Integer = 60000; // eine Minute in Millisekunden iFiveMinutes : Integer = 300000; // Fünf Minuten in Millisekunden iFifteenMinutes : Integer = 900000; // Fünfzehn Minuten in Millisekunden iOneHour : Integer = 3600000; // Eine Stunde in Millisekunden iOneDay : Integer = 86400000; // Ein Tag in Millisekunden function TSpecialTimer.CalcTimerInterval(iTimerInterval : Integer) : Integer; Var dNow : Double; begin // Interval setzen // Tagesdatum und Uhrzeit holen dNow := Now; // Den Tagesanteil holen (= Nachkommastellen). dNow := dNow - Trunc(dNow); // Rest bis Mitternacht holen. dNow := 1 - dNow; // Nachkommastellen mal Millisekunden pro Tag Result := Trunc(dNow * iOneDay); // wir benötigen den Rest bis zum angegeben Interval, damit der Timer // zur nächsten Minute, Stunde, 0 Uhr ... aktive wird. Result := (Result mod Max(iTimerInterval,1)); end;
Delphi-Quellcode:
// Damit der Timer zur nächsten vollen Sekunde ausgelöst wird:
Timer1.Interval := CalcTimerInterval(iOneSecond); // Zur nächsten vollen Viertelstunde, um den Glockenschlag für die Viertelstunde auszulösen: Timer2.Interval := CalcTimerInterval(iFifteenMinutes); // Oder um Mitternacht, z. B. für den Datumswechsel: Timer3.Interval := CalcTimerInterval(iOneDay); |
Zitat |
|
#26
Ich suche nach Programmideen, nachdem ich hier im Forum bereits eine Warenberechnung (Programm Waren) und den Verkauf von Kinokarten (Programm Kinokarten) vorgestellt habe. Bei der Realisierung dieser Programme konnte ich eine Reihe von Anregungen umsetzen und habe dabei viel dazugelernt.
Ich will keineswegs auf der Stelle treten. [...] Ich werde also weitergrübeln und die Foren durchstreifen. Wenn jemand eine Idee haben solle, wäre ich dafür dankbar. Ich kenne jetzt nicht so genau deine Aufgabenpräferenz, aber vielleicht reizt dich das ja? Besonders das Erstellen und Lösen solcher Sudokus dem Computer beizubringen bringt dich vielleicht weiter? Brighty |
Zitat |
Delphi 10.2 Tokyo Professional |
#27
nun habe ich auch über einen längeren Zeitraum die Zeitdauer der Ausführung des Programmteils gemessen und stets den Wert 0 ms erhalten.
Nehmen wir mal Dein Beispiel mit einer Zeitdauer von angenommen 15 ms für die Programmausführung. Dann müsste also das Intervall auf 985 ms gesetzt werden, damit dann genau zur neuen sec. der neue Ablauf der Bestimmung der roem. Zahlen auf dem Turm neu beginnen kann? Sehe ich das so richtig? Ich habe mit 2 Methoden die Messung vorgenommen und immer nur 0 ms erhalten. Somit kann ich also kein Intervall setzen oder? Anbei das Beispiel für die Messung mit einer der beiden Methoden:
Delphi-Quellcode:
procedure TForm1.Timer2Timer(Sender: TObject); //Messung Intervall
var freq: Int64; startTime: Int64; endTime: Int64; Present: TDateTime; Year, Month, Day, Hour, Min, Sec, MSec: Word; Zeit : String; begin timer2.Enabled:=false; QueryPerformanceFrequency(freq); QueryPerformanceCounter(startTime); Present:= Now; SysUtils.DecodeDate(Present, Year, Month, Day); Label1.Caption := 'Der heutige Tag ist der ' + Dezinroem(IntToStr(Day)) + ' des '+Dezinroem(inttoStr(Month)) +'. Monates ' + ' des Jahres ' + DezinRoem(IntToStr(Year)); SysUtils.DecodeTime(Present, Hour, Min, Sec, MSec); Label2.Caption := DezInRoem(inttostr(Hour))+ ' Stunden, '+DezInRoem(inttostr(Min))+' Minuten und ' +DezinRoem(inttostr(Sec))+' Sekunden und '+DezinRoem(inttostr(MSec)) +' Milisekunden'; Label4.Caption:=DezInRoem(inttostr(Hour)); Label5.caption:=DezInRoem(inttostr(Min)); Label6.caption:=DezInRoem(inttostr(Sec)); Label7.caption:=DezInRoem(inttostr(mSec)); Zeit:=timeToStr(Time); Panel1.Caption:=Zeit; // Panel1.Caption:=TimeToStr(Time); StatusBar1.Panels[0].Text:=TimeToStr(Time); StatusBar1.Panels[1].Text:=FormatDateTime('"Heute ist "dddd," der "d.mmmm yyyy"',Date); DecodeTime(Time, Hour, Min, Sec, mSec); Trackbar1.Position:=Sec; QueryPerformanceCounter(endTime); timer2.Enabled:=true; ShowMessage('Die Routine benötigte etwa ' + IntToStr((endTime - startTime) * 1000 div freq) + ' ms'); end;
Norbert
|
Zitat |
nahpets
|
#28
Du hast da wohl einen schnelleren Rechner als ich.
Zu Deinem : Ja, Du gehst recht in der Annahme. Mess' bitte noch einmal, aber mit anderen Bedingungen. Setze die Priorität der Uhr (im Taskmanager) mal auf den niedrigstmöglichen Wert. Starte einen anderen Prozess (oder mehrere), von denen Du weißt, das richtig Systemlast erzeugt wird, setze deren Priorität eventuell auf Hoch, bei Echtzeit kann es sein, dass Du Windows nicht mehr so recht bedienen kannst. Prüfe dann, ob die Uhr weiterhin genau geht. Deine obige Berechnung ist genau das, was ich meine. Bei meinem Rechner müsste zwei- bis dreimal pro Minute das Intervall auf 999 gesetzt werden. Unabhängig von den Messerergebnissen: Wenn Du das Intervall immer neu berechnest, kommt bei der Berechnung halt (fast) immer 1000 heraus, dann wird eben das Intervall (fast) immer auf 1000 gesetzt, macht nix. Sollte aber mal (irgendwann im Laufe des Jahres) aus welchem Grund auch immer, die Uhr etwas nachgehen, dann korrigiert sie sich eben halt dann bei der Berechnung des Intervalls zur nächsten Sekunde. Die Schaltsekunde von vor Kurzem hätte sie so auch automatisch korrigieren können. Auch eine beliebige Umstellung der Systemuhr (um Sekundenbruchteile) würde sie so automatisch korrigieren. Sagen wir mal so: Wenn Du hier in Deinen Quelltext noch meine Routine von oben einbaust
Delphi-Quellcode:
bin ich schon zufrieden.
QueryPerformanceCounter(endTime);
timer2.Interval := CalcTimerInterval(iOneSecond); timer2.Enabled:=true; ShowMessage('Die Routine benötigte etwa ' + IntToStr((endTime - startTime) * 1000 div freq) + ' ms'); Um zu prüfen, ob dies auch wirklich geht, setze die Aktuallisierungszeit mal auf die volle Minute und schau dann, ob die Zeitangabe immer HH:MM:00,000 (+/- 1 MS) ist. Als Chromleiste: Baue, wenn Du mit dem bisherigen klargekommen bist und das zu Deiner Zufriedenheit funktioniert, mal einen Nachfahren von TTimer, der, beim Setzen von Enabled := True, automatisch das Intervall auf die nötige Anzahl von Millisekunden bis zur nächsten vollen Sekunde setzt. Ein von mir gebauter TTimer-Nachfahre hat u. a. folgende Attribute:
Delphi-Quellcode:
Sie dienen dazu, wenn einer der boolschen Werte gesetzt ist, wird das Timerereignis zu exakt diesem Zeitpunkt ausgelöst. Es kann (neben Enabled) immer nur einer der Werte auf True stehen. Die Änderung eines der Werte setzt die anderen automatisch auf False. Das Setzen von Enabled auf True berechnet automatisch für das Intervall die benötigte Anzahl von Millisekunden bis zum Eintreten dieses Zeitpunktes, ist keiner der boolschen Werte gesetzt, so wird das Intervall unverändert belassen, es bleibt also die Funktionälität des Vorfahrens TTimer erhalten.
// Zu jeder vollen Sekunde (HH:MM:00,000, HH:MM:01,000, HH:MM:02,000, ...)
property OneSecond : Boolean read GetOneSecond Write SetOneSecond default False; // Alle fünf Sekunden (HH:MM:00,000, HH:MM:05,000, HH:MM:10,000, ...) property FiveSeconds : Boolean read GetFiveSeconds Write SetFiveSeconds default False; // Alle fünfzehn Sekunden (HH:MM:00,000, HH:MM:15,000, HH:MM:30,000, ...) property FifteenSeconds : Boolean read GetFifteenSeconds Write SetFifteenSeconds default False; // Jede volle Minute (HH:01:00,000, HH:02:00,00, HH:03:00,000 ...) property OneMinute : Boolean read GetOneMinute Write SetOneMinute default False; // Jede vollen fünf Minuten (HH:00:00,000, HH:05:00,000, HH:10:00,000, ...) property FiveMinutes : Boolean read GetFiveMinutes Write SetFiveMinutes default False; // Jede vollen fünfzehn Minuten (HH:00:00,000, HH:15:00,000, HH:30:00,000, ...) property FifteenMinutes : Boolean read GetFifteenMinutes Write SetFifteenMinutes default False; // Jede volle Stunde (01:00:00,000, 02:00:00,000, 03:00:00,000 ...) property OneHour : Boolean read GetOneHour Write SetOneHour default False; // Um Mitternacht (00:00:00,000) property Midnight : Boolean read GetMidnight Write SetMidnight default False; property Enabled : Boolean read GetEnabled Write SetEnabled default False; property Interval : Cardinal read GetInterval Write SetInterval default 1000; property OnTimer : TNotifyEvent read fOnTimer Write SetOnTimer; |
Zitat |
Delphi 10.2 Tokyo Professional |
#29
ich hab Deiner Empfehlung folgend beiliegendes geschrieben. Bin aber selbst davon nicht so richtig überzeugt, zumal sich der constructor create nicht aufrufen läßt und auch sonst ist das wohl so la la...
Ist es zuminstest etwa in die richtige Richtung? Was sollte ich also mit der abgeleiteten class tun? (hoffentlich keine zu blöde Frage)
Norbert
|
Zitat |
Delphi 11 Alexandria |
#30
Ok dann gebe ich auch noch meinen Senf dazu.......
Ich schreibe jetzt hier nur mal was mir beim überfliegen so aufgefallen ist
Delphi-Quellcode:
So kannst Du jederzeit die Form mit UpdateVisual(now) updaten;
procedure TForm1.Timer2Timer(Sender: TObject); // Messung Intervall
begin Timer2.Enabled := false; UpdateVisualData(now); Timer2.Interval := CalcTimerInterval(iOneSecond); Timer2.Enabled := true; end; procedure TForm1.UpdateVisualData(const Present: TDateTime); // Update Form var Present: TDateTime; Year, Month, Day, Hour, Min, Sec, MSec: Word; Zeit: string; begin SysUtils.DecodeDate(Present, Year, Month, Day); Label1.Caption := 'Der heutige Tag ist der ' + DezInRoem(IntToStr(Day)) + ' des ' + DezInRoem(IntToStr(Month)) + '. Monates ' + ' des Jahres ' + DezInRoem(IntToStr(Year)); SysUtils.DecodeTime(Present, Hour, Min, Sec, MSec); Label2.Caption := DezInRoem(IntToStr(Hour)) + ' Stunden, ' + DezInRoem(IntToStr(Min)) + ' Minuten und ' + DezInRoem(IntToStr(Sec)) + ' Sekunden und ' + DezInRoem(IntToStr(MSec)) + ' Milisekunden'; Label4.Caption := DezInRoem(IntToStr(Hour)); Label5.Caption := DezInRoem(IntToStr(Min)); Label6.Caption := DezInRoem(IntToStr(Sec)); Label7.Caption := DezInRoem(IntToStr(MSec)); Zeit := timeToStr(Time); Panel1.Caption := Zeit; StatusBar1.Panels[0].Text := timeToStr(Time); StatusBar1.Panels[1].Text := FormatDateTime('"Heute ist "dddd," der "d.mmmm yyyy"', Date); DecodeTime(Time, Hour, Min, Sec, MSec); TrackBar1.Position := Sec; end; Wie z.B in deinem Button2Click
Delphi-Quellcode:
Ach ja schau Dir auch noch die System.DateUtils; an. Da sind einige interessante Routinen rund um Time/Date drin
procedure TForm1.Button2Click(Sender: TObject);
var freq: Int64; startTime: Int64; endTime: Int64; begin Timer2.Enabled := false; if QueryPerformanceFrequency(freq) and QueryPerformanceCounter(startTime) then begin // Befehlesfolge deren Zeitdauer bestimmt werden soll UpdateVisualData(now); QueryPerformanceCounter(endTime); ShowMessage('Die Routine benötigte etwa ' + IntToStr((endTime - startTime) * 1000 div freq) + ' ms'); end else begin // Hier sollte eigentlich untersucht werden was nicht gegangen ist getLastError oder so ShowMessage('OOPS'); end; Timer2.Enabled := true; end;
Fritz Westermann
|
Zitat |
Ansicht |
Linear-Darstellung |
Zur Hybrid-Darstellung wechseln |
Zur Baum-Darstellung wechseln |
ForumregelnEs ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.
BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus. Trackbacks are an
Pingbacks are an
Refbacks are aus
|
|
Nützliche Links |
Heutige Beiträge |
Sitemap |
Suchen |
Code-Library |
Wer ist online |
Alle Foren als gelesen markieren |
Gehe zu... |
LinkBack |
LinkBack URL |
About LinkBacks |