C# :: Aufgabe #168
3 Lösungen

Biorhytmus - zuerst Top oder Down?
Anfänger - C#
von hollst
- 13.03.2017 um 12:44 Uhr
Die Biorhytmen sind eine "Entdeckung" des Wiener Psychologen Swoboda und des Berliner Arzt Fließ zu Beginn des 20. Jahrhunderts.
Es soll beim Menschen drei Arten davon geben, den körperlichen Biorhythmus mit einer Periodendauer von 23 Tagen, den emotionalen
Biorhythmus (28 Tage) und den geistigen Biorhythmus (genannt Intellekt, Periodendauer 33 Tage). Die Existenz dieser Rhytmen ist
nicht bewiesen und eine praktische Nutzbarkeit kaum erkennbar. Allerdings sind sie sehr nützlich z. B. bei Ausreden. Wurde eine Prüfung
vermasselt, kann man das leicht auf einen schlechten Wert in der geistigen Periode schieben, war die Leistung auf dem Fußballplatz
miserabel, so lag es am Tiefpunkt beim körperlichen Schwingungswert.
Die Perioden beginnen mit dem Tag der Geburt - bereits ein Kritikpunkt, warum nicht mit dem Tag der Befruchtung oder noch früher?
Diesen gemeinsamen Nullpunkt aller drei Rhytmen wird man erst wieder in 21.252 Tagen erreichen, also nach dem 58. Geburtstag.
Ein gemeinsamer Nullpunkt ist allerdings von untergeordnetem Interesse, schlimmer wäre es, wenn alle drei Rhytmen gleichzeitig im Tiefpunkt
wären, also alle drei den Wert -1 hätten. Ein Glückstag wäre dagegen ein Tag, bei dem alle drei Werte den Topwert +1 hätten.
Die Frage lautet, was passiert im Leben zuerst, alle drei Werte DOWN (sehr nahe oder gleich -1) oder alle drei Werte TOP (sehr nahe oder
gleich +1). Nach wievielen Tagen (Jahren) tritt dies jeweils erstmalig ein?
Als Programmieraufgabe eignet sich die Biorhytmusproblematik gut zum Üben der Entwicklung einer GUI-Anwendung (siehe Abb.). Man kann
es sehr einfach in fast allen Programmiersprachen erlernen und realisieren. Wenn man dies so tut, bekommt man schnelle ein Gefühl dafür,
welche Programmiersprache einem von der Stilistik und Performanz hinsichtlich der grafischen Mensch-Maschine-Kommunikation am
symphatischsten ist.
Es soll beim Menschen drei Arten davon geben, den körperlichen Biorhythmus mit einer Periodendauer von 23 Tagen, den emotionalen
Biorhythmus (28 Tage) und den geistigen Biorhythmus (genannt Intellekt, Periodendauer 33 Tage). Die Existenz dieser Rhytmen ist
nicht bewiesen und eine praktische Nutzbarkeit kaum erkennbar. Allerdings sind sie sehr nützlich z. B. bei Ausreden. Wurde eine Prüfung
vermasselt, kann man das leicht auf einen schlechten Wert in der geistigen Periode schieben, war die Leistung auf dem Fußballplatz
miserabel, so lag es am Tiefpunkt beim körperlichen Schwingungswert.
Die Perioden beginnen mit dem Tag der Geburt - bereits ein Kritikpunkt, warum nicht mit dem Tag der Befruchtung oder noch früher?
Diesen gemeinsamen Nullpunkt aller drei Rhytmen wird man erst wieder in 21.252 Tagen erreichen, also nach dem 58. Geburtstag.
Ein gemeinsamer Nullpunkt ist allerdings von untergeordnetem Interesse, schlimmer wäre es, wenn alle drei Rhytmen gleichzeitig im Tiefpunkt
wären, also alle drei den Wert -1 hätten. Ein Glückstag wäre dagegen ein Tag, bei dem alle drei Werte den Topwert +1 hätten.
Die Frage lautet, was passiert im Leben zuerst, alle drei Werte DOWN (sehr nahe oder gleich -1) oder alle drei Werte TOP (sehr nahe oder
gleich +1). Nach wievielen Tagen (Jahren) tritt dies jeweils erstmalig ein?
Als Programmieraufgabe eignet sich die Biorhytmusproblematik gut zum Üben der Entwicklung einer GUI-Anwendung (siehe Abb.). Man kann
es sehr einfach in fast allen Programmiersprachen erlernen und realisieren. Wenn man dies so tut, bekommt man schnelle ein Gefühl dafür,
welche Programmiersprache einem von der Stilistik und Performanz hinsichtlich der grafischen Mensch-Maschine-Kommunikation am
symphatischsten ist.
Lösungen:
MainWindow.xaml.cs
C#-Code
In dem Layout habe ich noch die WPF-Bibliothek "Extended WPF Toolkit" (Version 2.3.0.0) unter dem namepace xmlns:xctk="http://schemas.xceed.com/wpf/xaml/toolkit" eingebunden.
MainWindow.xaml
XML-Code

using System; using System.Collections.Generic; using System.Linq; using System.Windows; using System.Windows.Controls; using System.Windows.Media; using System.Windows.Shapes; namespace WpfBiorhythmus { /// <summary> /// Interaktionslogik für MainWindow.xaml /// </summary> public partial class MainWindow : Window { const int physical = 23; const int emotional = 28; const int intellectual = 33; const double phiPhysical = 2 * Math.PI / physical; const double phiEmotional = 2 * Math.PI / emotional; const double phiIntellectual = 2 * Math.PI / intellectual; private double SKX, SKY; private Point zero; public MainWindow() { InitializeComponent(); } static double CalcPhysical(DateTime birth, DateTime date) { double days = (date - birth).Ticks / 864e9; double v = Math.Sin(days * phiPhysical); return v; } static double CalcEmotional(DateTime birth, DateTime date) { double days = (date - birth).Ticks / 864e9; double v = Math.Sin(days * phiEmotional); return v; } static double CalcIntellectual(DateTime birth, DateTime date) { double days = (date - birth).Ticks / 864e9; double v = Math.Sin(days * phiIntellectual); return v; } static IEnumerable<BioRhythm> GetInterception(DateTime birth, double accuracy, int years = 100) { DateTime from = birth;// DateTime.Today; DateTime to = from.AddYears(years); int days = (to - from).Days; List<BioRhythm> rhythms = new List<BioRhythm>(); for (int i = 0; i < days; i++) { DateTime dt = from.AddDays(i); BioRhythm b = new BioRhythm() { Date = dt, Physical = CalcPhysical(from, dt), Emotional = CalcEmotional(from, dt), Intellectual = CalcIntellectual(from, dt), }; rhythms.Add(b); } return rhythms.Where(b => (b.Physical >= accuracy && b.Intellectual >= accuracy && b.Emotional >= accuracy) || (b.Physical <= -accuracy && b.Intellectual <= -accuracy && b.Emotional <= -accuracy)); } private void DrawAxes() { canvasChart.UpdateLayout(); double height = canvasChart.ActualHeight; double width = canvasChart.ActualWidth; int month = numericUpDownMonth.Value.Value; int year = numericUpDownYear.Value.Value; int days = DateTime.DaysInMonth(year, month); Line lineX = new Line() { X1 = 0, X2 = width, Y1 = height, Y2 = height, Stroke = Brushes.Black, StrokeThickness = 1 }; Line lineY = new Line() { X1 = 0, X2 = 0, Y1 = 0, Y2 = height, Stroke = Brushes.Black, StrokeThickness = 1 }; canvasChart.Children.Add(lineX); canvasChart.Children.Add(lineY); double stepY = height / 8; double d = 1; for (int i = 0; i < 9; i++) { Line y = new Line() { X1 = 0,//-5, X2 = width,// 5, Y1 = stepY * i, Y2 = stepY * i, Stroke = Brushes.Black, StrokeThickness = 1 }; TextBlock textY = new TextBlock() { Text = d.ToString(), }; Canvas.SetTop(textY, -8 + stepY * i); Canvas.SetLeft(textY, -35); d -= 0.25; canvasChart.Children.Add(y); canvasChart.Children.Add(textY); } double stepX = width / (days - 1); DateTime dt = new DateTime(year, month, 1); for (int i = 0; i < days; i++) { DateTime cur = dt.AddDays(i); Line x = new Line() { X1 = stepX * i, X2 = stepX * i, Y1 = 0,//height - 5, Y2 = height,//height + 5, Stroke = Brushes.Black, StrokeThickness = 1 }; TextBlock textX = new TextBlock() { Text = cur.ToString("dd.MM") }; textX.RenderTransform = new RotateTransform(90); Canvas.SetLeft(textX, stepX * i + 9); Canvas.SetTop(textX, height + 12); canvasChart.Children.Add(x); canvasChart.Children.Add(textX); } } private void Draw() { canvasChart.Children.Clear(); DrawAxes(); SetZeroPoint(); int month = numericUpDownMonth.Value.Value; int year = numericUpDownYear.Value.Value; double days = DateTime.DaysInMonth(year, month); DateTime dt = new DateTime(year, month, 1); DateTime birth = datePickerBirth.SelectedDate.Value; for (double d = 0; d < days - 1; d += 0.01) { DateTime cur = dt.AddDays(d); double y; Point p; Ellipse ell; if (checkBoxPhysical.IsChecked.Value) { y = CalcPhysical(birth, cur); p = GetPoint(d, y); ell = new Ellipse() { Stroke = Brushes.Blue, Fill = Brushes.Blue, Width = 2, Height = 2, }; Canvas.SetLeft(ell, p.X); Canvas.SetTop(ell, p.Y); canvasChart.Children.Add(ell); } if (checkBoxEmotional.IsChecked.Value) { y = CalcEmotional(birth, cur); p = GetPoint(d, y); ell = new Ellipse() { Stroke = Brushes.Red, Fill = Brushes.Red, Width = 2, Height = 2, }; Canvas.SetLeft(ell, p.X); Canvas.SetTop(ell, p.Y); canvasChart.Children.Add(ell); } if (checkBoxIntellectual.IsChecked.Value) { y = CalcIntellectual(birth, cur); p = GetPoint(d, y); ell = new Ellipse() { Stroke = Brushes.Green, Fill = Brushes.Green, Width = 2, Height = 2, }; Canvas.SetLeft(ell, p.X); Canvas.SetTop(ell, p.Y); canvasChart.Children.Add(ell); } } } private void SetZeroPoint() { int month = numericUpDownMonth.Value.Value; int year = numericUpDownYear.Value.Value; double width = DateTime.DaysInMonth(year, month) - 1; double height = 2; canvasChart.UpdateLayout(); SKX = canvasChart.ActualWidth / width; SKY = canvasChart.ActualHeight / height; zero = new Point(0, canvasChart.ActualHeight / 2); } private Point GetPoint(double x, double y) { Point P = new Point(); P.X = zero.X + x * SKX; P.Y = zero.Y - y * SKY; return P; } private void window_Loaded(object sender, RoutedEventArgs e) { //DrawAxes(); Draw(); } private void numericUpDownMonth_ValueChanged(object sender, RoutedPropertyChangedEventArgs<object> e) { if (numericUpDownMonth.Value == 0) { numericUpDownMonth.Value = 12; numericUpDownYear.Value--; } else if (numericUpDownMonth.Value == 13) { numericUpDownMonth.Value = 1; numericUpDownYear.Value++; } } private void buttonCalcInterception_Click(object sender, RoutedEventArgs e) { var result = GetInterception(datePickerBirth.SelectedDate.Value, numericUpDownAccuracy.Value.Value, numericUpDownInterceptionYears.Value.Value); listBoxResult.Items.Clear(); foreach (var b in result) { listBoxResult.Items.Add(b.ToTopDownString()); } } private void buttonPlot_Click(object sender, RoutedEventArgs e) { Draw(); } } class BioRhythm { public DateTime Date { get; set; } public double Physical { get; set; } public double Emotional { get; set; } public double Intellectual { get; set; } public override string ToString() { string s = string.Format("{0} - {1}: Physisch = {2}, Emotional = {3}, Intellektuell = {4}", CheckTopDown(), Date.ToString("dd.MM.yyyy"), Physical, Emotional, Intellectual); return s; } public string ToTopDownString() { return string.Format("{0} - {1}", CheckTopDown(), Date.ToString("dd.MM.yyyy")); } string CheckTopDown() { if (Physical < 0 && Emotional < 0 && Intellectual < 0) { return "DOWN"; } else if (Physical > 0 && Emotional > 0 && Intellectual > 0) { return "TOP"; } else { return "NULL"; } } } }
In dem Layout habe ich noch die WPF-Bibliothek "Extended WPF Toolkit" (Version 2.3.0.0) unter dem namepace xmlns:xctk="http://schemas.xceed.com/wpf/xaml/toolkit" eingebunden.
MainWindow.xaml

<Window xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:local="clr-namespace:WpfBiorhythmus" xmlns:xctk="http://schemas.xceed.com/wpf/xaml/toolkit" x:Name="window" x:Class="WpfBiorhythmus.MainWindow" mc:Ignorable="d" Title="Biorhythmus" Height="600" Width="900" Loaded="window_Loaded" ResizeMode="CanMinimize"> <Grid> <TabControl> <TabItem Header="Grafik"> <Grid x:Name="grid"> <Grid.ColumnDefinitions> <ColumnDefinition Width="40"/> <ColumnDefinition Width="1*"/> <ColumnDefinition Width="20"/> </Grid.ColumnDefinitions> <Grid.RowDefinitions> <RowDefinition Height="10"/> <RowDefinition Height="1*"/> <RowDefinition Height="45"/> <RowDefinition Height="68"/> <RowDefinition Height="10"/> </Grid.RowDefinitions> <Canvas x:Name="canvasChart" Grid.Column="1" Grid.Row="1" Margin="0"/> <Grid Grid.Column="1" Grid.Row="2"/> <Grid x:Name="gridProperty" Grid.Row="3" Grid.Column="1"> <!--</Grid> </Expander>--> <!--<Expander x:Name="expander" Header="Ausklappen" HorizontalAlignment="Left" Margin="0,5,0,0" VerticalAlignment="Top" Expanded="expander_Expanded" Collapsed="expander_Collapsed"> <Grid x:Name="gridAdditional" Background="LightGreen" Width="{Binding ActualWidth, ElementName=gridProperty}" Height="97">--> <CheckBox x:Name="checkBoxPhysical" HorizontalAlignment="Left" Margin="247,12,0,0" VerticalAlignment="Top" IsChecked="True" Width="100"> <Grid Height="16"> <Ellipse Width="16" Height="16" Stroke="Blue" Fill="Blue" Margin="0,0,29,0"/> <TextBlock Text="Physisch" Margin="20,0,-20,0"/> </Grid> </CheckBox> <CheckBox x:Name="checkBoxEmotional" HorizontalAlignment="Left" Margin="347,12,0,0" VerticalAlignment="Top" IsChecked="True" Width="100"> <Grid Height="16"> <Ellipse Width="16" Height="16" Stroke="Red" Fill="Red" Margin="0,0,37,0"/> <TextBlock Text="Emotional" Margin="20,0,-20,0"/> </Grid> </CheckBox> <CheckBox x:Name="checkBoxIntellectual" HorizontalAlignment="Left" Margin="452,12,0,0" VerticalAlignment="Top" IsChecked="True"> <Grid Height="16"> <Ellipse Width="16" Height="16" Stroke="Green" Fill="Green" Margin="0,0,45,0"/> <TextBlock Text="Intellektuell" Margin="20,0,-20,0"/> </Grid> </CheckBox> <TextBlock HorizontalAlignment="Left" Margin="10,14,0,0" TextWrapping="Wrap" Text="Geburtstag" VerticalAlignment="Top"/> <DatePicker x:Name="datePickerBirth" HorizontalAlignment="Left" Margin="93,12,0,0" VerticalAlignment="Top" SelectedDate="2000-01-01"/> <Button x:Name="buttonPlot" Content="Plotten" HorizontalAlignment="Left" VerticalAlignment="Top" Width="75" Margin="739,10,0,0" Click="buttonPlot_Click"/> <xctk:IntegerUpDown x:Name="numericUpDownMonth" HorizontalAlignment="Left" Margin="92,37,0,0" VerticalAlignment="Top" Value="1" Maximum="13" Minimum="0" ValueChanged="numericUpDownMonth_ValueChanged"/> <xctk:IntegerUpDown x:Name="numericUpDownYear" HorizontalAlignment="Left" Margin="133,37,0,0" VerticalAlignment="Top" Maximum="9999" Minimum="1" Value="{Binding SelectedDate.Year, ElementName=datePickerBirth, Mode=OneWay}"/> <TextBlock HorizontalAlignment="Left" Margin="10,39,0,0" TextWrapping="Wrap" Text="Anzeigemonat" VerticalAlignment="Top"/> </Grid> </Grid> </TabItem> <TabItem Header="Hochpunkt / Tiefpunkt"> <Grid> <TextBlock HorizontalAlignment="Left" Margin="10,14,0,0" TextWrapping="Wrap" Text="Geburtstag" VerticalAlignment="Top"/> <DatePicker HorizontalAlignment="Left" Margin="87,11,0,0" VerticalAlignment="Top" SelectedDate="{Binding SelectedDate, ElementName=datePickerBirth, Mode=TwoWay}"/> <Button x:Name="buttonCalcInterception" Content="Hochpunkte / Tiefpunkte berechnen" Width="200" Height="25" Margin="247,10,0,0" HorizontalAlignment="Left" VerticalAlignment="Top" Click="buttonCalcInterception_Click"/> <xctk:DoubleUpDown x:Name="numericUpDownAccuracy" HorizontalAlignment="Left" Margin="87,43,0,0" VerticalAlignment="Top" Minimum="0" Maximum="1" Increment="0.01" Value="1" Width="70"/> <TextBlock HorizontalAlignment="Left" Margin="10,42,0,0" TextWrapping="Wrap" Text="Genauigkeit" VerticalAlignment="Top"/> <ListBox x:Name="listBoxResult" Margin="10,68,0,0" HorizontalAlignment="Left" VerticalAlignment="Top" Width="337" Height="457"/> <xctk:IntegerUpDown x:Name="numericUpDownInterceptionYears" Width="100" Margin="247,43,0,0" HorizontalAlignment="Left" VerticalAlignment="Top" Value="100" Minimum="1"/> <TextBlock x:Name="textBlock" HorizontalAlignment="Left" Margin="162,45,0,0" TextWrapping="Wrap" Text="Jahre beachten" VerticalAlignment="Top"/> </Grid> </TabItem> </TabControl> </Grid> </Window>
Um die Frage zu beantworten, "was passiert im Leben zuerst ... ", muss man zunächst definieren, was man unter "sehr nahe oder gleich +/- 1" versteht, das Ergebnis hängt nämlich davon ab. Nebenbei, der nächste gemeinsamen Nullpunkt aller drei Rhytmen wird bereits in 10.626 Tagen erreicht (kleiner Denkfehler).
C#-Code

using System; using static System.Console; namespace biorythmus_console { public static class Program { static void Main() { double nahe = 1.0E-2; for (var i = 0; i < 5; i++) { int first = run(nahe); (nahe.ToString("E2") + " erg: " + first.ToString("n0").ToMyString(8) + " " + (Math.Sin(2 * Math.PI * first / periode[0])).ToString().ToMyString(25) + " " + (Math.Sin(2 * Math.PI * first / periode[1])).ToString().ToMyString(25) + " " + (Math.Sin(2 * Math.PI * first / periode[2])).ToString().ToMyString(25) + " " ).MessageLine(); grenze /= 2; } "ready".EndMessage(); } static int[] periode = new int[] { 23, 28, 33 }; static int run(double grenze) { int result = 0; double old_diff = 0.0; bool bo_break = false; do { result++; double f = 2 * Math.PI * result; double[] sins = new double[] { Math.Sin(f / periode[0]), Math.Sin(f / periode[1]), Math.Sin(f / periode[2]) }; double diff = Math.Sqrt( Math.Abs((sins[0] - sins[1]) * (sins[0] - sins[1])) + Math.Abs((sins[0] - sins[2]) * (sins[0] - sins[2])) + Math.Abs((sins[1] - sins[2]) * (sins[1] - sins[2]))); bo_break = (diff < old_diff) && (diff < grenze); old_diff = diff; } while (!bo_break); return result; } static void MessageLine(this string s) => WriteLine(s); static ConsoleKeyInfo EndMessage(this string s) { s.MessageLine(); return ReadKey(true); } static string ToMyString(this string s, int digits) { string result = s; while (result.Length < digits) result = " ".ToString() + result; return result; } } }

using System; namespace Biorythmus { class Biorythmus { static void Main(string[] args) { int k = 23; int e = 28; int g = 33; int kz = 0; int ez = 0; int gz = 0; int z = berechnen(k, e, g, kz, ez, gz); Console.WriteLine("Gemeinsamer Nullpunkt in " + z * k / 365 + " Jahren"); kz = Convert.ToInt32(Math.Round(k / 4.0, 0, MidpointRounding.AwayFromZero)); ez = Convert.ToInt32(Math.Round(e / 4.0, 0, MidpointRounding.AwayFromZero)); gz = Convert.ToInt32(Math.Round(g / 4.0, 0, MidpointRounding.AwayFromZero)); z = berechnen(k, e, g, kz, ez, gz); Console.WriteLine("Fast Gemeinsamer Höhepunkt in " + z * k / 365 + " Jahren"); kz = Convert.ToInt32(Math.Round(k / 4.0 * 3, 0, MidpointRounding.AwayFromZero)); ez = Convert.ToInt32(Math.Round(e / 4.0 * 3, 0, MidpointRounding.AwayFromZero)); gz = Convert.ToInt32(Math.Round(g / 4.0 * 3, 0, MidpointRounding.AwayFromZero)); z = berechnen(k, e, g, kz, ez, gz); Console.WriteLine("Fast Gemeinsamer Tiefpunkt in " + z * k / 365 + " Jahren"); } static int berechnen(int k, int e,int g, int kz, int ez, int gz) { int z = 0; bool f = true; while (f == true) { z += 1; kz += k; if (kz > ez) ez += e; if (kz > gz) gz += g; if (kz == ez & kz == gz & ez == gz) f = false; } return z; } } }