// Get a registry value. public static object GetRegistryValue(RegistryKey hive, string subkey_name, string value_name, object default_value) { RegistryKey subkey = hive.OpenSubKey(subkey_name, false); object result = subkey.GetValue(value_name, default_value); subkey.Close(); return result; }The hive parameter indicates the part of the Registry to search. This should be something like Registry.ClassesRoot, Registry.CurrentConfig, Registry.CurrentUser, Registry.DynData, Registry.LocalMachine, or Registry.PerformanceData. The method creates a subkey to work with a subkey inside the hive. The second parameter to OpenSubKey indicates that the method does not need write access to the subkey. The method then uses the subkey's GetValue method to get the desired value and returns it. Notice that the return value is an object. Different registry settings hold different data types so the calling code is responsible for converting the object into the appropriate type. The example program uses the following code to display the desktop icon size.
// Get the desktop icon size. private void btnGetSize_Click(object sender, EventArgs e) { object size_string = RegistryTools.GetRegistryValue( Registry.CurrentUser, @"Control Panel\Desktop\WindowMetrics", "Shell Icon Size", -1); txtSize.Text = size_string.ToString(); }This code uses the GetRegistryValue method to get the value stored in HKEY_CURRENT_USER\Control Panel\Desktop\WindowMetrics\Shell Icon Size. It uses ToString to convert the non-specific object into a string and displays it.
// Thrown if a file size contains an unknown exception. public class UnknownExtensionException : FormatException { public string Extension = ""; public UnknownExtensionException() : base() { } public UnknownExtensionException(string extension) : base() { Extension = extension; } public UnknownExtensionException(string extension, string message) : base(message) { Extension = extension; } public UnknownExtensionException(string extension, string message, Exception inner_exception) : base(message, inner_exception) { Extension = extension; } public UnknownExtensionException(string extension, SerializationInfo info, StreamingContext context) : base(info, context) { Extension = extension; } }The class starts by defining an Extension field that will hold the unknown extension. Next the class defines four constructors. Each takes the unknown extension as a first parameter and then other parameters that let the constructor invoke various constructors provided by the base class. You can decide which constructors you want to provide. Usually custom exception classes should at least include one constructor that takes no parameters and another that takes a message as a parameter. A third that takes a message and inner exception as parameters is also common. A constructor that takes a SerializationInfo info and a StreamingContext makes it possible to deserialize the class. This example complicates the constructors somewhat by including the extension parameter. The main program uses the following code to demonstrate the UnknownExtensionException class.
// Convert a file size into bytes. private void btnParse_Click(object sender, EventArgs e) { txtBytes.Clear(); txtCheck.Clear(); try { double bytes = ParseFileSize(txtSize.Text, 1024); txtBytes.Text = bytes.ToString("N0"); txtCheck.Text = ToFileSize(bytes); } catch (UnknownExtensionException ex) { // Tell the user it's a bad file name extension. MessageBox.Show("Unrecognized extension " + ex.Extension + ". " + "You must end the file size in one of: " + "bytes, KB, MB, GB, TB, PB, EB, ZB, or YB."); } catch (Exception ex) { // Just tell the user about this. MessageBox.Show(ex.Message); } }The code uses a try-catch block to look for errors. It uses a catch block to look specifically for UnknownExtensionExceptions. When it finds that kind of exception, it displays a message customized for it. The program uses a second catch block to look for other exceptions and, if it finds one, displays the exception's message. In this example UnknownExtensionException is derived from FormatException so a catch block that looks for FormatException would also catch UnknownExtensionException. Some additional tips:
throw new NotImplementedException("The SelfDestruct method is not yet implemented.");
throw new FormatException("Cost must be a monetary amount.");Replace FormatException with the exception class that you want to throw. The constructors provided by different exception classes take different parameters. Mots of the let you pass a message that is included in the exception object and that the program can show to the user. You should make this message as specific as possible so the user has a change to fix the problem. For example, say "Cost must be a monetary amount" not "Format error." Some exception classes may let you pass other values to their constructors such as the name of the parameter causing the problem and the parameter's value. In this example, when you enter a cost and tip percentage and click Calculate, the following code executes.
// Calculate the tip. private void btnCalculate_Click(object sender, EventArgs e) { try { PerformCalculation(); } catch (Exception ex) { txtTipAmount.Clear(); MessageBox.Show(ex.Message); } } private void PerformCalculation() { // Parse the cost. decimal cost; if (!decimal.TryParse(txtCost.Text, NumberStyles.Currency, null, out cost)) throw new FormatException("Cost must be a monetary amount."); // Validate the cost. if ((cost < 0.01m) || (cost > 500m)) throw new ArgumentOutOfRangeException( "Cost must be between $0.01 and $500.00."); // Parse the tip percentage. string percent_string = txtPercentTip.Text; if (percent_string.StartsWith("%")) percent_string = percent_string.Substring(1); else if (percent_string.EndsWith("%")) percent_string = percent_string.Substring(0, percent_string.Length - 1); decimal tip_percent; if (!decimal.TryParse(percent_string, out tip_percent)) throw new FormatException("% Tip must be a numeric value."); // If the original value contained a % symbol, divide by 100. if (txtPercentTip.Text.Contains("%")) tip_percent /= 100m; // Validate the percentage. if ((tip_percent < 0) || (tip_percent > 100)) throw new ArgumentOutOfRangeException( "% Tip must be between 0% and 100%."); // Everything's valid. Perform the calculation. decimal tip_amount = cost * tip_percent; txtTipAmount.Text = tip_amount.ToString("C"); }The button's Click event handler uses a Try Catch block to protect itself when it calls the PerformCalculation method, which does all of the interesting work. The PerformCalculation method attempts to parse the cost value that you entered and, if it fails, throws a FormatException to tell you that the cost is invalid. If the cost parses, then the code validates its value and throws an ArgumentOutOfRangeException if the value is too big or too small. The code then performs similar validations for the tip percent value. Whenever the code encounters a problem that it can't fix, it throws an exception to tell the calling code what happened. In this simple example, you could put code in the PerformCalculation method to display error messages, although that would make the code more complicated and a bit messier. This technique is much more important when your code is called by someone else's code. In that case you may not know what action the calling code should take in response to various errors so the exception classes tell the calling code what went wrong. For some of the calculations, you could just try to let the program fail if necessary. For example, you could try to use decimal.Parse to parse the numeric values and let decimal.Parse throw an exception if it fails. That tells the calling code in general what went wrong but doesn't give any context that the code can use to help the user. This example throws its own exceptions so it can provide better error messages so the user knows what went wrong. For example, it tells the user "Cost must be a monetary amount" rather than just saying "Input string was not in a correct format."
// Generate the rolls. private void btnRoll_Click(object sender, EventArgs e) { picGraph.Image = null; Cursor = Cursors.WaitCursor; Refresh(); // Make an array to hold value counts. // The value counts[i] represents rolls of i. long[] counts = new long[13]; // Roll. Random rand = new Random(); long numTrials = long.Parse(txtNumTrials.Text); for (int i = 0; i < numTrials; i++) { int result = rand.Next(1, 7) + rand.Next(1, 7); counts[result]++; } // The expected percentages. float[] expected = { 0, 0, 1 / 36f, 2 / 36f, 3 / 36f, 4 / 36f, 5 / 36f, 6 / 36f, 5 / 36f, 4 / 36f, 3 / 36f, 2 / 36f, 1 / 36f }; // Display the results. Bitmap bm = new Bitmap( picGraph.ClientSize.Width, picGraph.ClientSize.Height); using (Graphics gr = Graphics.FromImage(bm)) { gr.SmoothingMode = SmoothingMode.AntiAlias; gr.TextRenderingHint = TextRenderingHint.AntiAliasGridFit; using (StringFormat sf = new StringFormat()) { sf.Alignment = StringAlignment.Center; sf.LineAlignment = StringAlignment.Far; float ymax = picGraph.ClientSize.Height; float scale_x = picGraph.ClientSize.Width / 11; float scale_y = ymax / (counts.Max() * 1.05f); for (int i = 2; i <= 12; i++) { gr.FillRectangle(Brushes.LightBlue, (i - 2) * scale_x, ymax - counts[i] * scale_y, scale_x, counts[i] * scale_y); gr.DrawRectangle(Pens.Blue, (i - 2) * scale_x, ymax - counts[i] * scale_y, scale_x, counts[i] * scale_y); float percent = 100 * counts[i] / (float)numTrials; float expected_percent = 100 * expected[i]; float error = 100 * (expected_percent - percent) / expected_percent; string txt = percent.ToString("0.00") + Environment.NewLine + expected_percent.ToString("0.00") + Environment.NewLine + error.ToString("0.00") + "%"; gr.DrawString(txt, this.Font, Brushes.Black, (i - 2) * scale_x, ymax - counts[i] * scale_y); } // Scale the expected percentages for the number of rolls. for (int i = 0; i < expected.Length; i++) { expected[i] *= numTrials; } // Draw the expected results. ListThe code makes a counts array to record the number of rolls that give each of the possible totals 2 through 12. It actually allocates an array larger with bounds 0 through 12 so the code can record the number of times the total is K in counts[K]. Next the code performs its trial rolls. It simply loops over the number of trials and uses a Random object to generate two "rolls" between 1 and 6. (Note that you need to do this to get the proper distribution to simulate the dice. You can't just pick a random number between 2 and 12.) The code then adds 1 to the count for that roll. The code then defines the expected fractions for each total roll. For example, there are 3 ways you can roll a total of 4 (1+3, 2+2, and 3+1) and 36 possible rolls so expected[4] = 3/36. Like the counts array, the expected array allocates extra entries so the expected probability for rolling a total of K can be stored in expected[K]. Next the code starts drawing the results. It creates a Bitmap and an associated Graphics object. It then loops over the roll result values 2 through 12. For each value, the code draws a rectangle showing how many times the roll occurred. It draws text below the top of the rectangle showing the percentage of times that value was rolled, the expected percentage, and the percent error between the two. The code then loops through the expected values and draws them in red so you can easily compare the expected and actual results. If you run the example, you may be surprised at how many trials you need to run to consistently get results that match the expected values well. For example, 100 trials gets you a fairly bad match, often giving results more than 20% off the expected values. Even with 1,000 trials you'll often get results more than 15% from their expected values. It isn't until you perform on the order of 10,000 trials that you start to get consistently good matches.expected_points = new List (); expected_points.Add(new PointF(0, ymax)); for (int i = 2; i <= 12; i++) { float y = ymax - expected[i] * scale_y; expected_points.Add(new PointF((i - 2) * scale_x, y)); expected_points.Add(new PointF((i - 1) * scale_x, y)); } expected_points.Add(new PointF(11 * scale_x, ymax)); using (Pen red_pen = new Pen(Color.Red, 3)) { gr.DrawLines(red_pen, expected_points.ToArray()); } } } picGraph.Image = bm; Cursor = Cursors.Default; }
// Declare events. public event EventHandler HoursScrolled; public event EventHandler HoursChanged;These events tell the main program when the symbiote's hours change. The following code shows the class's StartHour and StopHour properties.
// The selected hours. private int _StartHour = 0; private int _StopHour = 0; public int StartHour { get { return _StartHour; } set { _StartHour = value; // Raise the HoursChanged event. if (HoursChanged != null) HoursChanged(this, null); } } public int StopHour { get { return _StopHour; } set { _StopHour = value; // Raise the HoursChanged event. if (HoursChanged != null) HoursChanged(this, null); } }These are fairly straightforward properties. You could implement them as public fields (if you're willing to ignore the purists who say you should never expose fields publicly), but I wanted the code to raise the HoursChanged event when these values changed. The following code shows the class's only constructor.
// The PictureBox we manage. private PictureBox Pic; // Constructor. public SelectHoursSymbiote(PictureBox pic) { Pic = pic; // Add event handlers. Pic.Paint += pic_Paint; Pic.MouseDown += pic_MouseDown; Pic.MouseMove += pic_MouseMove; Pic.MouseUp += pic_MouseUp; }The Pic variable holds a reference to the PictureBox to which the symbiote is attached. The constructor saves a reference to the PictureBox. It then gives the PictureBox event handlers to catch the Paint event and the mouse events it needs to let the user select hours. These event handlers are almost exactly the same as those used by the previous example except they work with the Pic control instead of the specific picHours control. See the previous example for information about how the mouse events and the Paint event work. The only real differences are where the MouseMove and MouseUp event handlers raise events. After it calculates the newly selected hours, the MouseMove event handler uses the following code to raise the symbiote's HoursScrolled event.
// Raise the HoursScrolled event. if (HoursScrolled != null) HoursScrolled(this, null);The MouseUp event handler uses the following code to raise the HoursChanged event.
// Raise the HoursChanged event. if (HoursChanged != null) HoursChanged(this, null);The main program uses the following code to attach symbiotes to its PictureBoxes.
// The symbiotes. private SelectHoursSymbiote Symbiote1, Symbiote2; private void Form1_Load(object sender, EventArgs e) { this.ResizeRedraw = true; // Create the symbiotes. Symbiote1 = new SelectHoursSymbiote(picHours1); Symbiote1.HoursChanged += pic_HoursChanged; Symbiote1.HoursScrolled += pic_HoursChanged; Symbiote1.StartHour = 6; Symbiote1.StopHour = 14; Symbiote2 = new SelectHoursSymbiote(picHours2); Symbiote2.HoursChanged += pic_HoursChanged; Symbiote2.HoursScrolled += pic_HoursChanged; Symbiote2.StartHour = 9; Symbiote2.StopHour = 17; }The code creates the new symbiotes, passing their constructors the PictureBoxes to which they should be attached. It registers event handlers for the symbiotes' HoursScrolled and HoursChanged events and gives them initial StartHour and StopHour values. The following code shows how the program responds when it receives an HoursScrolled or HoursChanged event.
// Show the times in the TextBoxes. private void pic_HoursChanged(object sender, EventArgs e) { SelectHoursSymbiote symbiote = sender as SelectHoursSymbiote; DateTime start_time = new DateTime(2000, 1, 1, symbiote.StartHour, 0, 0); DateTime stop_time = new DateTime(2000, 1, 1, symbiote.StopHour, 0, 0); if (symbiote == Symbiote1) { txtStartTime1.Text = start_time.ToShortTimeString(); txtStopTime1.Text = stop_time.ToShortTimeString(); } else { txtStartTime2.Text = start_time.ToShortTimeString(); txtStopTime2.Text = stop_time.ToShortTimeString(); } }This code converts the symbiote's hours into DateTime objects and then displays them as short times in the appropriate TextBoxes. That's all there is to it. Creating and using symbiote classes is remarkably easy. Usually it only takes a few minutes to convert code inside a form into a symbiote. (Writing it all up in a post takes longer.)
// The selected hours. private int StartHour = 6; private int StopHour = 18; // Draw the hour indicator on this PictureBox. private void DrawHours(PictureBox pic, Graphics gr, int start_hour, int stop_hour) { gr.Clear(Color.LightGreen); // Scale to fit a 24-hour period. const int margin = 3; float scale_x = XScale(pic); float hgt = pic.ClientSize.Height; float y1 = margin; float y2 = hgt - margin; // Draw the selected time range. RectangleF hours_rect = new RectangleF( start_hour * scale_x, y1, (stop_hour - start_hour) * scale_x, y2 - y1); gr.FillRectangle(Brushes.Blue, hours_rect); // Draw tick marks. float x = 0; for (int i = 0; i <= 24; i++) { gr.DrawLine(thin_pen, x, 0, x, hgt); x += scale_x; } // Draw times. gr.RotateTransform(-90); int xmid = -pic.ClientSize.Height / 2; using (Font font = new Font(FontFamily.GenericSansSerif, 12)) { using (StringFormat sf = new StringFormat()) { sf.Alignment = StringAlignment.Center; sf.LineAlignment = StringAlignment.Center; x = 0; for (int i = 0; i <= 24; i++) { gr.DrawString(HourToString(i), font, Brushes.Black, xmid, x, sf); x += scale_x; } } } }This method clears the PictureBox with light green. It then calculates a horizontal scale factor scale_x to use when drawing and uses it to fill a rectangle representing the selected hours in blue. Next the code loops over 25 hours (0 through 24) adding tick marks to show where the hours are in the drawing. The method then adds a -90 degree rotation (90 degrees counter-clockwise) to the Graphics object to rotate future drawing. It finishes by looping through the hours again to draw each hour's name. It uses the following HourToString method to convert an hour number into its name.
// Return the hour formatted as we want to display it. private string HourToString(int hour) { if (hour == 0) return "midnight"; if (hour == 12) return "noon"; if (hour == 24) return "midnight"; if (hour <= 12) return hour.ToString() + "am"; return (hour - 12).ToString() + "pm"; }The HourToString method simply returns a string appropriate for an hour number. The following code shows the XScale method that the program uses to calculate a horizontal scale factor.
// Get the horizontal scale factor. private float XScale(PictureBox pic) { return pic.ClientSize.Width / 24; }The scale factor is just the width of the PictureBox's client area divided by the number of hours the program draws. The remaining interesting code handles the mouse events that let the user select a time range. The following MouseDown event handler starts the process.
// Handle mouse events. private bool Drawing = false; private int DrawingStartHour, DrawingStopHour; private void picHours_MouseDown(object sender, MouseEventArgs e) { Drawing = true; DrawingStartHour = (int)Math.Round(e.X / XScale(picHours)); DrawingStopHour = DrawingStartHour; StartHour = DrawingStartHour; StopHour = DrawingStartHour; picHours.Refresh(); }The event handler sets Drawing = true to indicate that the user has pressed the mouse. It divides the mouse's X position by the horizontal scale factor to see what hour is being selected, rounding to the nearest hour. The event handler saves the hour in the variables DrawingStartHour, DrawingStopHour, StartHour, and StopHour, and refreshes the PictureBox to make it redraw. The following code shows the MouseMove event handler.
private void picHours_MouseMove(object sender, MouseEventArgs e) { if (!Drawing) return; // Calculate the value and display a tooltip. int hour = (int)Math.Round(e.X / XScale(picHours)); string new_tip = HourToString(hour); if (tipHour.GetToolTip(picHours) != new_tip) tipHour.SetToolTip(picHours, new_tip); // Save the new value. DrawingStopHour = hour; // Redraw. if (DrawingStartHour < DrawingStopHour) { StopHour = DrawingStopHour; StartHour = DrawingStartHour; } else { StartHour = DrawingStopHour; StopHour = DrawingStartHour; } picHours.Refresh(); }This code gets the hour under the mouse the same way the MouseDown event handler does. It calls HourToString to get the hour's name and compares it to the PictureBox's current tooltip. If the name is different from the current tooltip, the code sets the PictureBox's tooltip to the new hour name. (You could remove this if it annoys you. I'm on the fence on this one.) Next the code saves the hour in DrawingStopHour. It then sets StartHour and StopHour so StartHour <= StopHour. (If you don't do this, the DrawHours method earlier doesn't correctly draw the selected hours because it tries to draw a RectangleF with a negative width and the Graphics object won't do that.) Finally the method refreshes the PictureBox to make it redraw. The last interesting piece of code is the following MouseUp event handler.
private void picHours_MouseUp(object sender, MouseEventArgs e) { if (!Drawing) return; tipHour.SetToolTip(picHours, ""); Drawing = false; DisplayTimes(); }This event handler simply clears the PictureBox's tooltip and calls the following DisplayTimes method to display the selected time range in TextBoxes.
// Show the times in the TextBoxes. private void DisplayTimes() { DateTime start_time = new DateTime(2000, 1, 1, StartHour, 0, 0); DateTime stop_time = new DateTime(2000, 1, 1, StopHour, 0, 0); txtStartTime.Text = start_time.ToShortTimeString(); txtStopTime.Text = stop_time.ToShortTimeString(); }This method converts the selected times into DateTime values and then uses their ToShortTimeString method to produce a nicely formatted output. There are lots of interesting modifications you could make to this code. For example, you could allow the user to select times in half hour or quarter hour increments. Or you could draw different selected hours in different colors to show overtime or normal business hours. (I may add some of those in a later example.)
private void Form1_Load(object sender, EventArgs e) { txtDirectory.Text = Application.StartupPath; txtDirectory.Select(txtDirectory.Text.Length, 0); txtFile.Text = Application.ExecutablePath; txtFile.Select(txtFile.Text.Length, 0); }This code displays the startup directory in the Directory TextBox and the executable program's name in the File TextBox. When you click the buttons, the following event handlers execute.
private void btnDirectoryExists_Click(object sender, EventArgs e) { if (System.IO.Directory.Exists(txtDirectory.Text)) txtDirectoryResult.Text = "Yes"; else txtDirectoryResult.Text = "No"; } private void btnFileExists_Click(object sender, EventArgs e) { if (System.IO.File.Exists(txtFile.Text)) txtFileResult.Text = "Yes"; else txtFileResult.Text = "No"; }These event handlers just use System.IO.Directory.Exists and System.IO.File.Exists to see if the directory and file exist.
Fibonacci(0) = 0 Fibonacci(1) = 1 Fibonacci(n) = Fibonacci(n - 1) + Fibonacci(n - 2)This recursive definition leads naturally to this recursive algorithm for calculating Fibonacci numbers.
// Recursive. private double Fibonacci1(int n) { if (n <= 1) return n; return Fibonacci1(n - 1) + Fibonacci1(n - 2); }This method is simple. Unfortunately it's also very slow. For example, to calculate Fibonacci(40) the program must calculate Fibonacci(39) and Fibonacci(38). But to calculate Fibonacci(39) the program must calculate Fibonacci(38) and Fibonacci(37). Here Fibonacci(38) is being calculated twice. As the recursion continues, the same values must be calculated again and again a huge number of times making the program take a very long time. The following method solves the problem of recalculating values by storing the intermediate values it creates in a look-up table for later use.
// With look-up values. private double Fibonacci2(int n) { if (n <= 1) return n; // Create the look-up table. double[] fibo = new double[n + 1]; return Fibonacci2(fibo, n); } private double Fibonacci2(double[] fibo, int n) { if (n <= 1) return n; // If we have already calculated this value, return it. if (fibo[n] > 0) return fibo[n]; // Calculate the result. double result = Fibonacci2(fibo, n - 1) + Fibonacci2(fibo, n - 2); // Save the value in the table. fibo[n] = result; // Return the result. return result; }The first version of the method makes a look-up table big enough to hold the Fibonacci numbers 0 through n. It then calls the second overloaded version to do all the work, passing it the look-up table and n. The second version of Fibonacci2 looks in the look-up table to see if we have already calculated the n-th Fibonacci number and returns it if we have. This prevents the method from calculating the same values more than once and saves a huge amount of time. If we have not yet calculated the n-th Fibonacci number, the method calls itself recursively as before, saves the value it calculates in the look-up table, and returns the result it calculated. This technique of caching values that you will need later can be extremely powerful. It can let you avoid a lot of work even if you don't really know when you'll need the values later. (I worked on a project once where some performance analysis showed that a lot of time was spent calculating the same values repeatedly. I added a look-up table and improved performance greatly.) This method is a great improvement on the previous version but there's still room for improvement. Both of the previous methods generate Fibonacci numbers in a top-down way, but the recursive definition of the Fibonacci numbers is really bottom-up. The definition starts with Fibonacci(0) and Fibonacci(1) and works up from there. The following method uses a bottom-up approach to build Fibonacci numbers without recursion.
// Iterate holding the two previous values. private double Fibonacci3(int n) { if (n <= 1) return n; double minus2 = 0; double minus1 = 1; double fibo = minus1 + minus2; for (int i = 3; i <= n; i++) { minus2 = minus1; minus1 = fibo; fibo = minus1 + minus2; } return fibo; }The method creates variables minus2, minus1, and fibo. When fibo holds Fibonacci(n), minus2 holds Fibonacci(n - 2) and minus1 holds Fibonacci(n - 1). Initially minus2 = Fibonacci(0), minus1 = Fibonacci(1), and fibo = Fibonacci(2). The method then enters a for loop. Each trip through the loop, the code moves minus2 up to the current value of minus1, minus1 up to the current value of fibo, and calculates the next value for fibo. When the loop finishes, fibo holds Fibonacci(n). This method doesn't use recursion and doesn't need a look-up table so it's even faster than the previous method (and uses less memory), but even it can be improved. Abraham de Moivre discovered that:
Fibonacci(n) = (φ1n - φ2n) / Sqrt(5)Where φ1 and φ2 are the two roots to the equation for the golden ratio. (See the earlier post.)
φ1 = (1 + Math.Sqrt(5)) / 2 φ2 = (1 - Math.Sqrt(5)) / 2The following method uses de Moivre's formula to calculate Fibonacci numbers directly.
// Use Abraham de Moivre's formula. private double Fibonacci4(int n) { double phi1 = (1 + Math.Sqrt(5)) / 2.0; double phi2 = (1 - Math.Sqrt(5)) / 2.0; return (Math.Pow(phi1, n) - Math.Pow(phi2, n)) / Math.Sqrt(5); }Even this can be simplified (but I promise this is the last one). Because the absolute value of φ2 is less than 1 (it's roughly -0.62), φ2n is small for all n so you can drop that term and round the result off to the nearest integer.
// Ignore phi2. private double Fibonacci5(int n) { double phi1 = (1 + Math.Sqrt(5)) / 2.0; return Math.Truncate(Math.Pow(phi1, n) / Math.Sqrt(5)); }The lessons to learn are:
// The random number provider. private RNGCryptoServiceProvider Rand = new RNGCryptoServiceProvider(); // Return a random integer between a min and max value. private int RandomInteger(int min, int max) { uint scale = uint.MaxValue; while (scale == uint.MaxValue) { // Get four random bytes. byte[] four_bytes = new byte[4]; Rand.GetBytes(four_bytes); // Convert that into an uint. scale = BitConverter.ToUInt32(four_bytes, 0); } // Add min to the scaled difference between max and min. return (int)(min + (max - min) * (scale / (double)uint.MaxValue)); }Outside of the method, the program creates the RNGCryptoServiceProvider. The RandomInteger method uses that object to generate random numbers. All the RNGCryptoServiceProvider does is generate bytes. It's up to you to convert those bytes into whatever values you need. The RandomInteger method starts by setting the value scale to uint.MaxValue. I don't want the method to use that value for scale (you'll see why in a moment) so the method enters a while loop that executes as long as scale is uint.MaxValue. Inside the loop, the method uses the RNGCryptoServiceProvider to generate four bytes. It then uses BitConverter.ToUInt32 to convert those four bytes into a four-byte unsigned integer. If the bytes span all of the possible byte values (and they should), then the unsigned integer spans all possible unsigned integer values. If scale is still uint.MaxValue, then the loop repeats until it gets a new value. (It is extremely unlikely that scale will be uint.MaxValue and much less likely that this will happen twice in a row so the loop won't last long.) The code then divides scale by the uint.MaxValue. This produces a value between 0.0 and 1.0, not including 1. (This is why I didn't want scale to be uint.MaxValue. So the result of this division would be less than 1.0.) It multiplies this value by the difference between the maximum and minimum desired values and adds the result to the minimum value. The result is a value between min and max, not including max. The code then uses the (int) cast operator to truncate the result to get an integer. This matches the behavior provided by the Random class's Next method. It returns an integer between a lower bound (inclusive) and an upper bound (exclusive). When you fill in the values and click Generate, the program generates random numbers and displays histograms showing their distribution. The more numbers you generate, the closer the histograms' bars should be to the same height. First the program uses the Random class to generate random numbers and displays their histogram in the top PictureBox. Then it uses the RandomInteger method to generate a new set of numbers and displays their histogram in the bottom PictureBox. Looking at the histograms, both seem pretty "random." Using a cryptographic random number generators has advantages and disadvantages.