niloy / blog

blog raw files
3 stars 1 forks source link

Generating all possible arithmetic combinations #29

Open niloy opened 7 years ago

niloy commented 7 years ago

Recently, I read this very interesting blog post which talked about combining the 4 basic arithmetic operators (+, -, , /) with bunch of given numbers to generate an answer. For example, how can the numbers {1, 2, 3, 4} be combined with the 4 basic arithemetic operators to give the answer 24. One way is `(1 + 2 + 3) 4 = 24`. There are other ways as well. We can write a program that will generate all possible combinations. I decided to write my own version using F#. The program works relatively well.

type Node =
    | Number of float
    | Addition of (Node * Node)
    | Subtraction of (Node * Node)
    | Division of (Node * Node)
    | ProductList of Node list

type Nodelist = Node list

let combineProduct (n1: Node) (n2: Node) : Node =
    match (n1, n2) with
    | (Number 0.0, _) -> Number 0.0
    | (_, Number 0.0) -> Number 0.0
    | (ProductList p1, ProductList p2) -> ProductList (p1 @ p2)
    | (ProductList p1, p2) -> ProductList (p2 :: p1)
    | (p1, ProductList p2) -> ProductList (p1 :: p2)
    | (_, _) -> ProductList [n1; n2]

let combineDivision (top: Node) (bottom: Node) : Node =
    match (top, bottom) with
    | (_, Number 1.0) -> top
    | (_, _) -> Division (top, bottom)

let combineTwoNodes (n1: Node) (n2: Node) : Nodelist =
    [
        Addition (n1, n2)
        Subtraction (n1, n2)
        Subtraction (n2, n1)
        combineProduct n1 n2
        combineDivision n1 n2
        combineDivision n2 n1
    ]

let rec pickAtIndex (xs: 'T list) (index: int) : 'T * 'T list =
    match index with
    | 0 -> (List.head xs, List.tail xs)
    | _ ->
        let head = List.head xs
        let tail = List.tail xs
        let e, rest = pickAtIndex tail (index - 1)
        (e, head :: rest)

let rec permuteNodes (nodes: Nodelist) : Nodelist =
    match nodes with
    | [] -> []
    | [n] -> [n]
    | [a; b] -> combineTwoNodes a b
    | nodes ->
        let n = [1 .. (nodes.Length - 1)]
        let a = n |> List.map (pickAtIndex nodes)
        a |> List.collect (fun (x, y) -> (permuteNodes y) |> List.collect (combineTwoNodes x))

let encloseInBrackets str = "(" + str + ")"

let rec str (node: Node) : (string * float) =
    match node with
    | Number n -> (string n, n)
    | Addition (n1, n2) -> 
        let str1, value1 = str n1
        let str2, value2 = str n2
        (str1 + "+" + str2 |> encloseInBrackets, value1 + value2)
    | Subtraction (n1, n2) -> 
        let str1, value1 = str n1
        let str2, value2 = str n2
        (str1 + "-" + str2 |> encloseInBrackets, value1 - value2)
    | Division (n1, n2) ->
        let str1, value1 = str n1
        let str2, value2 = str n2
        (str1 + "/" + str2 |> encloseInBrackets, value1 / value2)
    | ProductList p ->
        let a = p |> List.map str
        let str = a |> List.map fst |> String.concat "*" |> encloseInBrackets
        let value = a |> List.map snd |> List.reduce (*)
        (str, value)

[1.0; 2.0; 3.0; 4.0]
    |> List.map Number
    |> permuteNodes
    |> List.map str 
    |> List.filter (fun (s, v) -> v = 24.0)
    |> List.map (fun (s, v) -> s + "=" + (string v))
    |> String.concat "\n"
    |> printfn "%s"