# Project Euler – Problem 89 Solution

Check out my new course Learn you some Lambda best practice for great good! and learn the best practices for performance, cost, security, resilience, observability and scalability.

#### Problem

The rules for writing Roman numerals allow for many ways of writing each number (see FAQ:Roman Numerals). However, there is always a “best” way of writing a particular number.

For example, the following represent all of the legitimate ways of writing the number sixteen:

IIIIIIIIIIIIIIII

VIIIIIIIIIII

VVIIIIII

XIIIIII

VVVI

XVI

The last example being considered the most efficient, as it uses the least number of numerals.

The 11K text file, roman.txt (right click and ‘Save Link/Target As…’), contains one thousand numbers written in valid, but not necessarily minimal, Roman numerals; that is, they are arranged in descending units and obey the subtractive pair rule (see FAQ for the definitive rules for this problem).

Find the number of characters saved by writing each of these in their minimal form.

Note: You can assume that all the Roman numerals in the file contain no more than four consecutive identical units.

#### Solution

```open System.IO

// define the set of available roman numerals
let romanNumerals = [(['I'],1I);(['I';'V'],4I);(['V'],5I);(['I';'X'],9I);
(['X'],10I);(['X';'L'],40I);(['L'],50I);(['X';'C'],90I);
(['C'],100I);(['C';'D'],400I);(['D'],500I);(['C';'M'],900I);
(['M'],1000I)]

// define function to get the numeric value of a roman numeral
let getNumericValue (numeral:char) =
snd (romanNumerals |> Seq.filter (fun (l, v) -> l.Length = 1 && l. = numeral) |> Seq.head)

// define function to convert a roman numerals to its corresponding numeric value
let toNumeric (numerals:string) =
let rec toNumericRec ((head::tail):char list) (num:bigint) =
| _, [] -> num + getNumericValue head
| _, hd::tl when getNumericValue head >= getNumericValue hd ->
toNumericRec tail (num + getNumericValue head)
| _, hd::[] -> // subtractive pair as last number i.e. XIX - 19
num + getNumericValue hd - getNumericValue head
| _, hd::tl -> // subtractive pair not as last number XCV - 95
toNumericRec tl (num + getNumericValue hd - getNumericValue head)

toNumericRec (numerals.ToCharArray() |> Array.toList) 0I

// converts an integer value to corresponding minimal roman numerals form
let toMinimalRomanNumerals (value:bigint) =
let rec toMinimalRomanNumeralsRec (value:bigint) (list:char list) =
if value = 0I then list
else
let (list', value') =
romanNumerals
|> List.sortBy (fun (l, v) -> -v)
|> Seq.filter (fun (l, v) -> v <= value)
toMinimalRomanNumeralsRec (value-value') (list@list')

toMinimalRomanNumeralsRec value []

let minimal = original |> Array.map toNumeric |> Array.map toMinimalRomanNumerals

let originalLen = original |> Array.sumBy (fun str -> str.Length)
let minimalLen = minimal |> Array.sumBy (fun str -> str.Length)
originalLen - minimalLen
``` Hi, I’m Yan. I’m an AWS Serverless Hero and the author of Production-Ready Serverless.

I specialise in rapidly transitioning teams to serverless and building production-ready services on AWS.

Are you struggling with serverless or need guidance on best practices? Do you want someone to review your architecture and help you avoid costly mistakes down the line? Whatever the case, I’m here to help. Check out my new course, Learn you some Lambda best practice for great good! In this course, you will learn best practices for working with AWS Lambda in terms of performance, cost, security, scalability, resilience and observability. Enrol now and enjoy a special preorder price of £9.99 (~\$13). Are you working with Serverless and looking for expert training to level-up your skills? Or are you looking for a solid foundation to start from? Look no further, register for my Production-Ready Serverless workshop to learn how to build production-grade Serverless applications!