Skip to content

Instantly share code, notes, and snippets.

@lkesteloot
Last active August 29, 2015 14:04
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save lkesteloot/4c3de075c9bea5c5bfea to your computer and use it in GitHub Desktop.
Save lkesteloot/4c3de075c9bea5c5bfea to your computer and use it in GitHub Desktop.
My first F# program! Generates simple expressions that evaluate to "near integers". These are numbers that are close to their nearest integer.
// Generates simple expressions that evaluate to "near integers". These are numbers
// that are close to their nearest integer. Inspired by:
//
// http://ssodelta.wordpress.com/2014/08/01/the-quest-for-near-integers/
// The numbers we calculate must be closer to their nearest integer than this
// value for them to be considered "near integers".
let eps = 0.0001
// An expressions is one of these subtypes.
type Expr =
| Constant of int
| Pi
| E
| Sum of Expr * Expr
| Difference of Expr * Expr
| Product of Expr * Expr
| Quotient of Expr * Expr
| Power of Expr * Expr
| Sine of Expr
| Cosine of Expr
// Evaluates the Expr to a float.
let rec evaluate expr =
match expr with
| Constant c -> float c
| Pi -> System.Math.PI
| E -> System.Math.E
| Sum (a,b) -> evaluate a + evaluate b
| Difference (a,b) -> evaluate a - evaluate b
| Product (a,b) -> evaluate a * evaluate b
| Quotient (a,b) -> evaluate a / evaluate b
| Power (a,b) -> evaluate a ** evaluate b
| Sine a -> sin (evaluate a)
| Cosine a -> cos (evaluate a)
// Random number generator used to create random trees of expressions.
let rnd = new System.Random()
// Return a random expression tree of depth "depth", where a depth of 0 means
// a constant, a depth of 1 means a function of a constant, etc.
let rec makeRandomExpression depth =
match depth with
| 0 -> match (rnd.Next(1, 12)) with
| 10 -> Pi
| 11 -> E
| c -> Constant c
| _ -> match (rnd.Next(7)) with
| 0 -> Sum (makeRandomExpression (depth - 1), makeRandomExpression (depth - 1))
| 1 -> Difference (makeRandomExpression (depth - 1), makeRandomExpression (depth - 1))
| 2 -> Product (makeRandomExpression (depth - 1), makeRandomExpression (depth - 1))
| 3 -> Quotient (makeRandomExpression (depth - 1), makeRandomExpression (depth - 1))
| 4 -> Power (makeRandomExpression (depth - 1), makeRandomExpression 0)
| 5 -> Sine (makeRandomExpression (depth - 1))
| 6 -> Cosine (makeRandomExpression (depth - 1))
| _ -> Constant 0 // Can't happen -- here to suppress compiler warning.
// Format the expression tree as a string in normal mathematical notation.
let rec prettyPrint expr =
match expr with
| Constant c -> sprintf "%d" c
| Pi -> "π"
| E -> "e"
| Sum (a,b) -> sprintf "(%s + %s)" (prettyPrint a) (prettyPrint b)
| Difference (a,b) -> sprintf "(%s - %s)" (prettyPrint a) (prettyPrint b)
| Product (a,b) -> sprintf "(%s * %s)" (prettyPrint a) (prettyPrint b)
| Quotient (a,b) -> sprintf "(%s / %s)" (prettyPrint a) (prettyPrint b)
| Power (a,b) -> sprintf "(%s ** %s)" (prettyPrint a) (prettyPrint b)
| Sine a -> "sin " + prettyPrint a
| Cosine a -> "cos " + prettyPrint a
// Whether the float x is a "near integer", meaning that it's close to its
// closest integer. "Close" is defined as "closer than eps".
let isNearInteger x =
let rounded = round x
rounded <> 0.0 && rounded <> x && abs (rounded - x) < eps
// Generate a list of random expression trees.
let expressions = List.init 10000 (fun _ -> makeRandomExpression (rnd.Next(2, 4)))
// Evaluate them and keep the ones that generate near integers.
expressions |>
List.map (fun expr -> (expr, evaluate expr)) |>
List.filter (fun (expr, value) -> isNearInteger value) |>
List.map (fun (expr, value) -> sprintf "%s -> %.12f" (prettyPrint expr) value) |>
String.concat "\n" |>
printfn "%s"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment