1729
Explore the mathematical significance of 1729, the Hardy-Ramanujan number, and its connection to Srinivasa Ramanujan's legacy in this F# Advent post.
This post is part of F# Advent calendar. Thanks Sergey Tihon for arranging this and giving me the chance.
First, I'd like to clarify that this was not my original choice for FSAdvent calendar. I was going to write a long post about web, AKKA, APIs, and real-time systems—my current working area. But this time's calendar entries are reaching new levels and dimensions. And a recent post from Evelina Gabasova is kind of the reason behind the change. Do read the post and you may like to re-watch Star Wars series. Evelina, you are super awesome. And the post was too good.
So, why 1729?
I have a special love for Mathematics. Unfortunately, I never got the chance to take formal mathematics training. I'm an engineer, so I know things here and there to solve problems. Maybe more than others—for Physics and Mathematics especially—just because of my love. It's so much fun doing them.
In our part of the country, if you're scoring good marks in School, either you become a doctor, or if a little less, then an engineer. Normally there are no other choices. I hope you got the point why I'm an engineer (mine was a little too low). But if you ask for the name of a well-known gifted mathematician from India, the first name that comes up is Srinivasa Ramanujan. And we have an exam series on his name. It's way tough. I still remember that I prepared and never gathered enough guts to give one. Damn afraid of failing. But my senior and first love of mathematics was my role model in school, Dr. Vithal Rangarajan, who scored 2nd rank and joined Medicine. Yet, my another friend Dr. Parth Ganatra has the same story. You're mostly getting the gist of why we're having rare Ramanujans in India. Because most of them become Doctors.
Enough background noise. Let's have some fun with numbers. You can check out the wiki page for the story of the 1729 number. It is the smallest number expressible as the sum of two cubes in two different ways.
Ok, that's good. But I don't have enough brain power to cross check that. And if that's the smallest, which are the others?
So, I decided to find out by myself. And at midnight, I started working on this.
My first try was going to question the result. But for some reason, Math.Floor (1728.0 ** (1.0/3.0)) is resulting in 11.0. I have no idea why. It should come to 12.0. Then I thought about how he arrived at this number. It's the smallest, so he must have started from 1.
F# gurus, can you please tell me why
1728.0 ** (1.0/3.0) = 12.0 //false?
Here is a code snippet. I start with a sequence of numbers and cube it.
let posNum = seq {1..101}
let cube x = x * x * x
let cubeposNum =
posNum
|> Seq.map (fun x -> (x, cube x))
I have returned a tuple instead of a cube result so I can use it afterwards in visualizing data.
Now, it's time to find out the total combinations.
let totalCombination =
cubeposNum
|> Seq.map (fun (a,b) -> cubeposNum |> Seq.map (fun (x,y) -> (a,x,b+y)))
|> Seq.concat
It gives all the possible combinations of numbers. Again, in the tuple, I'm returning the result and the numbers that are being cubed and added. Same reason for display purposes.
Now, time for truth.
Here is the final result:
let finalResult =
totalCombination
|> Seq.countBy (fun (x,y,z) -> z)
|> Seq.filter (fun (x,y) -> y >= 4)
|> Seq.map (fun (x,y) -> x)
It will return a Sequence of numbers fulfilling the above condition. If you're wondering about y >= 4, then 4 is because (1,2,9) is the same as (2,1,9). As I need two distinct pairs, I have four pairs. I did try using greater than 4, but I couldn't find any pair bigger than 4 for the given set.
Time for doing some analysis of the numbers.
let dirtyHack (inputSeq)=
let a = inputSeq |> Seq.item 0
let b = inputSeq |> Seq.item 1
let c = inputSeq |> Seq.item 2
(a, b, c)
let pairValues =
finalResult
|> Seq.map (fun x -> totalCombination |> Seq.filter (fun (a,b,c) -> c = x ))
|> Seq.map (fun a -> a |> Seq.map (fun (x,y,z) -> seq[x;y;z] |> Seq.sort |> dirtyHack))
|> Seq.map (fun x -> x |> Seq.distinct)
It will give a Sequence of Sequences which contains tuples of numbers. Where the tuple is in the form of (a, b, a^3 + b^3). The dirty hack part I couldn't get right, so I'll be grateful if someone can provide a better solution for that part.
We have everything. It's time to put the data to view.
//form craziness - a copy paste from 2010 code :P
let form = new Form(Visible = true, Text = "A Simple F# Form",
TopMost = true, Size = Size(600,600))
let data = new DataGridView(Dock = DockStyle.Fill, Text = "Hardy–Ramanujan number",
Font = new Font("Lucida Console",12.0f),
ForeColor = Color.DarkBlue
)
form.Controls.Add(data)
data.DataSource <- (pairValues |> Seq.concat |> Seq.sortBy (fun (x,y,z) -> z) |> Seq.toArray)
data.Columns.[0].Width <- 200
data.Columns.[1].Width <- 200
data.Columns.[2].Width <- 200
data.Columns.[0].HeaderText <- "a"
data.Columns.[1].HeaderText <- "b"
data.Columns.[2].HeaderText <- "(a^3 + b^3)"
Clearing up the data showing up in the grid view. Below are screenshots of the complete data.
Looks good.
Put on a chart to see where the data is moving.
let taxicabnumbers = pairValues
|> Seq.concat
|> Seq.sortBy (fun (x,y,z) -> z)
|> Seq.map (fun (x,y,z) -> z)
|> Seq.distinct
Chart.Point (taxicabnumbers,"TaxiCabNumbers","TaxiCab Number")
The above chart is showing the distribution of the result of numbers which are fulfilling the above condition.
let taxicabnumbersX = pairValues
|> Seq.concat
|> Seq.sortBy (fun (x,y,z) -> z)
|> Seq.map (fun (x,y,z) -> (x,z))
Chart.Point (taxicabnumbersX,"TaxiCabNumbersX","TaxiCab Number in ref to X")
The above chart is showing the distribution in comparison to the number X.
let taxicabnumbersY = pairValues
|> Seq.concat
|> Seq.sortBy (fun (x,y,z) -> z)
|> Seq.map (fun (x,y,z) -> (y,z))
Chart.Point (taxicabnumbersY,"TaxiCabNumbersY","TaxiCab Number in ref to Y")
The above chart is showing the distribution in comparison to the number Y.
Chart.Combine[
Chart.Point(taxicabnumbersX, "TaxiCabNumbersX")
Chart.Point(taxicabnumbersY, "TaxiCabNumbersY")
]
The above chart is showing both X and Y. Here, X and Y are pretty much interchangeable.
The code is not that good. But F# makes it pretty easy to work with. I will not bash any other language or style of programming today. As it's my Birthday today (1712 was a pretty boring number, so I took 1729. :P). But you can make it out how easy it can be.
Ramanujan has many other interesting formulas that can be proved and analyzed the same way. One of them I personally like is Partition Number Theory, but maybe next time. In his time, we didn't have that much computing power or programming languages to solve these types of problems. Or even prove them. How did he do it? No one knows. (I doubt he must have been more functional. ;) ).
I will really appreciate any input for the code. I know it's pretty poor in quality. Gist is here. I love to have comments and PRs. I will update my code/blog accordingly.
Special request: It would be great if someone can compress the code that can be tweeted to fsibot and it will return 1729.
Thanks for the wonderful and supporting community. Merry Christmas and Happy New Year to all.
Update1
Special thanks to Stachu Korick. The complete logic comes in a single tweet. Here it is.
@fsibot seq{for el1 in [1..99] do for el2 in [1..99] do yield el1*el1*el1+el2*el2*el2}|>Seq.countBy(fun z->z)|>Seq.find(fun(_,y)->y>=4)|>fst
— Stachu Korick (@StachuDotNet) December 18, 2015
This is the reason I love the F# community so much.
Update2
Stachu Korick is not stopping at all. Here's a second version, a more legitimate one from him.
@fsibot
seq{for a in [1..99] do for b in [1..99] do yield (a*a*a)+(b*b*b)}
|>Seq.countBy(fun z->z)
|>Seq.find(fun(_,y)->y>=4)
|>fst
— Stachu Korick (@StachuDotNet) December 19, 2015
This is the fun part with F# and functional languages in general. You make things work with ugly and simple code. And then refactor it with beautiful and simple code.
Update3
Oh man, things are getting better (shorter). Have a look at Mathias Brandewinder's version.
@fsibot let f x=x*x*x in[for x in 1..99 do for y in 1..99->f x+f y]|>Seq.countBy id|>Seq.find((snd>>(=)4))|>fst // @StachuDotNet
— Mathias Brandewinder (@brandewinder) December 19, 2015
Sweeeeettttt :)
Update4
Yukitos made it even shorter. Hufff, this is getting more and more fun.
@fsibot [for x in 1..99 do for y in 1..99->pown x 3+pown y 3]|>Seq.countBy id|>Seq.find((snd>>(=)4))|>fst
// :) @brandewinder @StachuDotNet
— yukitos (@yukitos) December 19, 2015
Update5
Tomas Petricek made it even shorter. Beat this...
@fsibot Seq.countBy id [for x in 0..9800->pown(1+x/99)3+pown(1+x%99)3]|>Seq.find(snd>>(=)4)|>fst//@yukitos @brandewinder @StachuDotNet -9 ;)
— Tomas Petricek (@tomaspetricek) December 19, 2015
Update6
The above version is beaten already. The new record is by Scott Wlaschin.
@fsibot Seq.countBy(fun x->pown(x/99)3+pown(x%99)3)[0..9800]|>Seq.find(snd>>(=)4)|>fst//@tomaspetricek @yukitos @kunjee @StachuDotNet 79chs!
— Scott Wlaschin (@ScottWlaschin) December 19, 2015
PS: I can't exactly say what is going on here. But I am enjoying it a lot.
Frequently Asked Questions
1729 is known as the Hardy-Ramanujan number and is the smallest number that can be expressed as the sum of two cubes in two different ways: 1729 = 1³ + 12³ = 9³ + 10³. It's a famous number in mathematics with an interesting historical story involving the mathematician Srinivasa Ramanujan.
The author was inspired by Evelina Gabasova's post about Star Wars social networks and decided to shift focus to explore the fascinating mathematical properties of 1729. They were motivated by their personal passion for mathematics and wanted to investigate which numbers could be expressed as sums of cubes in multiple ways.
The Hardy-Ramanujan number (1729) is named after the famous Indian mathematician Srinivasa Ramanujan, who is considered one of the most gifted mathematicians India has produced. The number has historical significance from a conversation between Ramanujan and mathematician G.H. Hardy, making it an important part of mathematical folklore.
You can write a program that iterates through sequences of numbers, cubes them, and then searches for instances where different pairs of cubes sum to the same value. The author used F# to explore this computationally, starting from 1 and working upward to discover these special numbers systematically.