The Mathematical Hacker (2012)
evanmiller.org
evanmiller.org
If you want precise results for large sizes, then the integer-arithmetic recurrence is faster than computing the square root of 5 to very high precision and raising it to the nth power.
The author’s “proper solution” might better be named the “naive undergraduate introductory linear algebra student solution”. Or maybe “18th century analytic solution ignorant of computer number formats”.
> They run in constant [...] time
If we are going to artificially limit ourselves to numbers smaller than the precision of e.g. double precision floats, then the integer algorithms might just as well be considered “constant time”, where the constant is just whatever it takes to compute the largest value we are considering.
Or alternately we can just precompute and cache the entire set of them if we want. There are only 78.
Here’s a Javascript implementation
const fibcache = [
0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233,
377, 610, 987, 1597, 2584, 4181, 6765, 10946, 17711,
28657, 46368, 75025, 121393, 196418, 317811, 514229,
832040, 1346269, 2178309, 3524578, 5702887, 9227465,
14930352, 24157817, 39088169, 63245986, 102334155,
165580141, 267914296, 433494437, 701408733, 1134903170,
1836311903, 2971215073, 4807526976, 7778742049,
12586269025, 20365011074, 32951280099, 53316291173,
86267571272, 139583862445, 225851433717, 365435296162,
591286729879, 956722026041, 1548008755920,
2504730781961, 4052739537881, 6557470319842,
10610209857723, 17167680177565, 27777890035288,
44945570212853, 72723460248141, 117669030460994,
190392490709135, 308061521170129, 498454011879264,
806515533049393, 1304969544928657, 2111485077978050,
3416454622906707, 5527939700884757, 8944394323791464];
const fibonacci = (n) => fibcache[n];"crude but useful approximations" is such a strange view. The purpose of machine-controlled error arithmetics is to give the programmer a powerful language construct that enables evaluation of a function or algorithm for arbitrarily small margin of error, using automatically revised accuracy in the intermediate steps. Error analysis is a math subject that gives us better algorithms. One needs both for hitting that desired margin of error with certainty within as short time as possible.
AIUI, this is what "constructive (or 'exact') real numbers" give you, and it's quite computationally-intensive. Error arithmetic/interval arithmetic/etc. are only approximations to it. In many cases people don't even bother with the real deal because they're computing way more than one single result, so roundoff errors can be thought of as just one additional source of noise that is going to be averaged out over many 'steps'.
Personally I like the version better where you explicitly work in terms of integers like a + bφ or rational numbers like (a + bφ)/c. https://observablehq.com/@jrus/zome-arithmetic very handy when working with the symmetry system of the icosahedron. https://observablehq.com/d/cae3fea57d7cf2d4
Know what's even faster? Exponentiating a 2x2 matrix of integer coefficients. And again, that's what a bit of math tells you, so I think the point of the author holds.
Since the recurrence is always the same we can e.g. use the identities F_{m+n-1} = F_mF_n + F_{m-1}F_{n-1} and F_{m+n} = F_mF_n + F_mF_{n-1} + F_{m-1}F_n to precompute and cache all of the terms for 2^{n-1} and 2^{n}, and then multiply the appropriate ones together to find whatever value we need based on a binary expansion of the index we want to find a fibonacci number for.
This is the same idea as fast matrix exponentiation but saves low-level arithmetic and half of the space, because two of the terms in the matrix are basically redundant.
Programming will become true engineering soon. In a few decades, we will be building mainstream software with formal guarantees. And for that, there's tons of abstract algebra needed as a foundation, once you try to digest [1-6] and whatever replaces them in the future.
It's tragic most CS programs do not emphasize this a bit more.
[1] https://www.elsevier.com/books/lectures-on-the-curry-howard-...
[2] https://softwarefoundations.cis.upenn.edu/
[3] https://www.cis.upenn.edu/~bcpierce/tapl/
[4] https://www.springer.com/gb/book/9783540654100
I expect that formal methods will become another specialization that most engineers don't need to directly work with, alongside things like cryptography and low level arithmetic.
Nowadays it is mostly about tools/frameworks. You don't even need to master the programming language that you are using.
I work mainly with Java stack and see so many devs with 10+ years of experience who know nothing about Java internals, who know nothing about how Java's hashmap works...heck, many of them can't even explain what some _keywords_ do: volatile, transient.
Nevertheless, they 'successfully' close their JIRA tickets and deliver.
Leave those quotes away and I agree. If they know enough Java to finish those tickets in a way that the bug is actually fixed and the fix itself does not impose more technical debt than needed, then those developers know exactly enough java and close their tickets successfully indeed.
I wrote my own compiler for a subset of Java (including inheritance and overriding) to MIPS in uni, among the top in class, and am currently looking to compile Haskell to a very neat kind of calculus (check out "Formality-Core"). It was a really fun experience, but I feel that compilers are immensely overhyped. I guess that is because it is a hard topic and for many people, the investment is huge, so they exagerate the benefits they percieve to reap.
Being proud of understanding something or admitting that a topic is hard is all fine, but I am absolutely allergic to elitism where it isn't just. And no, in 2019, knowing all about parsing and register allocation won't unlock superpowers anymore.
Of course that perspective ignores two important things: physical constraints on performance, and the relationship of data to facts in the real world. From these constraints programming also takes on the characteristics of natural science and engineering. These considerations come into play as well, but so often the limiting resource is human time and intellectual bandwidth, requiring a simpler, easier solution.
In mathematics, the scale can be much grander than anything yet produced in computing. Calculus is just breathtaking -- the signature accomplishment of the greatest mathematicians of their time, refined over hundreds of years into a tool that can be wielded by ordinary teenagers. It might seem shallow to compare such a sweeping achievement to mundane programming tasks such as writing enterprise business logic. Yet, if you ignore scale and universality, the intellectual task is the same: to take something that requires careful thought and close familiarity with the product requirements today, and boil it down to something that can be understand at a glance six months from now by someone who was not involved in the initial development. To distill patterns of thought internalized via close familiarity with the motivating problems into easily defined and digested concepts.
E.g.
The question is how much time is it worth putting into writing a detailed spec. I think the answer is that it really depends on the size and complexity of your software.
I think the idea is that programmers have to be comfortable with writing something down, even if it's not perfect. A half-baked spec is better than no spec, basically. Otherwise you will likely end up with spaghetti code that needs to be rewritten or scrapped.
Seriously, though, that prose portion is where the vast majority of the bugs will be.
Now we've built ourselves a large number of layers of abstraction in an attempt to forget these things. And with those abstraction layers, programmers can be reasonably productive without having to think about their problems in terms of the math that the underlying math machine is doing. But in my mind, if someone wants to call themselves a computer scientist, they are necessarily concerned with the underlying math being done.
The fruit picker does not have to think about that math or understand it, just like most programmers do not have to understand what happens at the low level.
As such, all ordinary programmers are demonstrably writing mathematics - just with syntax, semantics, and applications that are unlike those of more traditional math.
Which mathematical expressions do memcpy, fork, and putchar map to?
Putchar is easily modeled in terms of something like a state monad. In earlier semantics, such as the original denotational semantics, it was just modeled directly as a store that was threaded through a program, i.e. passed to every function.
Fork is modeled by process calculi.
If you think programming languages somehow can't be modeled by mathematics, you're essentially taking the position that they're doing something supernatural.
Math is a way to add structure and understand the activity that programmers are engaged in. A lot of programming languages and computer architectures have been developed without the math in mind. There is still a lot of mathematical structure to be discovered in what an ordinary programmer does, I think. But yes, much of computer science, programming, etc feels very close to math, even compared to physics, linguistics, music theory, etc.
> A lot of programming languages and computer architectures have been developed without the math in mind.
They're still formal systems. In a broad definition, they're mathematics themselves. In a narrower definition, they map isomorphically to more rigorous mathematics.
Some mainstream programming languages do not even a formal grammar for their syntax. Valid syntax is whatever is accepted by the parsing program. Much more commonly there isn't a formal specification of the semantics. The semantics are similarly defined by the implementation.
I think you might be arguing that the implementation is itself part of a formal system, involving the processor. I don't think I feel the same way. The CPUs operation can be described precisely in terms of rules, in the same way that Newtonian physics can, with the difference that digital systems are more robust to modeling errors. This is what the Strawberry picker example above, I believe, was meant to touch on. Just because math is used to model something doesn't mean the thing is math.
Some aspects of computational sciences feel like they are math. Other aspects seem like they're just modeled by math.
It goes well beyond "basic arithmetic and simple Boolean logic". One of the comment semantic descriptions of imperative programming languages involves a stack of monads representing the various effects that programming language support.
While most programmers may not be aware of this, they're still writing working expressions in a formal language which is equivalent to this.
If software development is ripe for a mathematics-driven leap forward, just like logistics and baseball, why hasn’t it happened yet?
If software developers and baseball coaches were both sitting next to low-hanging fruit that merely required a bit of mathematical knowledge and spreadsheet-wrangling to exploit, I would have expected the people already using computers -- the software developers -- to find it first. What’s different, and why?
It could be that software is inherently less amenable to statistical and numeric approaches than baseball, and that’s just the way it is. When you look at baseball the right way, you have a great mass of data with fairly simple and consistent rules, ideal for a statistical approach. When you look at a typical software system, you have a mass of special-case business logic, with no real simplifying assumption to be found.
In cases where “big data” is available, successful companies are using statistics to exploit regularities in the system.
So maybe what the author is arguing for is already happening, albeit on an somewhat smaller and slower scale, and maybe this is inherent in the nature of software.
Applied mathematics, basically calculus, is great but it can’t do everything. The author seems to be ignoring discrete mathematics (which a lot of the “Lisp hacks” he dismisses would fall under). Then there are sociological issues, such as documentation, unit testing, debugging, modularity, maintenance of legacy systems.
Plenty of researchers have tried applying a data-driven approach to testing and debugging, and I think it’s safe to say no-one has completely cracked it yet. If another recent paper posted here is to be believed, we don’t even know if there’s a significant difference in bug rates between static and dynamic typing.
> Lisp-school essayists [...] spend their time worrying about how to make code more abstract. This kind of thinking may lead to compact, powerful code bases, but in the language of economics, there is an opportunity cost. If you’re thinking about your code, you’re not thinking about the world outside, and the equations that might best describe it.
Another way of saying the same thing in a positive light is to say that these things have already been thought about and can be applied to take advantage of opportunities where applicable.
> Although the early years in the twenty-first century seem to be favoring the Lisp-school philosophy, I predict the balance of the century will belong to the Fortran-school programmers
This part does seem to be happening. Just look at Apache Storm v2 port from Clojure to Java:
"While Storm’s Clojure implementation served it well for many years, it was often cited as a barrier for entry to new contributors. Storm’s codebase is now more accessible to developers who don’t want to learn Clojure in order to contribute."[0]
[0] https://www.dm4r.com/tech/apache-storm-v2-0-porting-from-clo...
Indeed, using fast exponentiations, you only need $O(log n) matrix multiplications. But the dominant cost is actually at the last step, where you multiply two numbers of size $O(n)$ bits for a total cost of $O(n log n)$.
Speaking of mathematics in programming: I find it remarkable that the recent convergence of async programming into the use of promises and async/await (in python, js, rust...) is a weak form of the continuation monad. And the continuation monad is a form of the 'not not' Lawrere topology!
For Fibonacci numbers, expressing terms of the sequence as a 2x2 matrix exponentiatied and then exponentiating by repeated squaring let's you get the n-th Fibonacci number in O(log n) multiplications. It's straightforward, if you get an input n and handle arbitrary precision integer arithmetic, you can calculate F_1000 quickly and without much hassle. Compare that to Binet's formula: How many digits of precision will you need to compute the golden ratio to ensure your result from Binet's formula is correct? And how will you compute it to that many digits? And that's not even worrying about the fact that floating point multiplication doesn't correspond exactly to multiplication of real numbers, so you may need to switch to arbitrary precision floating point arithmetic.
The time to compute sqrt(5) was 0.23 sec. The time compute the formula (I dropped the insignificant 1-sqrt(5) term) was 2.8 seconds. When I used the built in Mathematica function to compute the Fibonacci number, it took 0.12 seconds.
Dijkstra was trained as a Mathematical Engineer for example. Fact is, if astronomers had the mindset of most programmers, they'd call their field Telescope Science.
This is an interesting thought, but the two seem fundamentally different in nature (active vs passive).
"Mathematical engineering" looks like a modern invention. In any case, his undergraduate degree was in physics.
Before quibbling, note I said he was trained as a Mathematical Engineer, not that he had a degree in Mathematical Engineering.
[1] https://www.cs.utexas.edu/users/EWD/transcriptions/EWD11xx/E...
(Anyway, participating in the training of "mathematical engineers" and being trained as one are not necessarily the same thing.)
Except... mathematicians are very frequently going to be doing the same thing: asking if there is some generalization that covers two seemingly independent cases. Otherwise, the author should/would probably be talking about Stirling's approximation instead of the log gamma function in that piece.
More knowledge of mathematics relevant to a given domain an application is written for might often be a good thing -- I think that's an overarching point worth examining.
But if you're going really to look to mathematics as a guiding light, it's not going to be super useful in criticizing the drive for abstraction.
I think the article’s artificially limiting its point to mathematics - there are plenty of other disciplines where we can find well-tread useful metaphors, etc.
> I think the article’s artificially limiting its point to mathematics - there are plenty of other disciplines where we can find well-tread useful metaphors, etc.
I feel as though you have it backwards. Would you care to explain your rationale for fortran as art compared to lisp as craft?
> But I think that most programmers who are serious about what they do should know calculus (the real kind), linear algebra, and statistics. The reason has nothing to do with programming per se — compilers, data structures, and all that — but rather the role of programming in the economy...One way to read the history of business in the twentieth century is a series of transformations whereby industries that “didn’t need math” suddenly found themselves critically depending on it.
There are two ways to interpret the claim that more programmers should know advanced math. One of them, which the author preempts, is the idea that programmers will improve their own work through significant mathematical maturity. It seems the author and I already agree that isn't the case, so I won't address this interpretation. But I still consider this interpretation to be more defensible than the other one.
The other way to interpret it, which the author seems to be explicitly pushing, is that programmers with a strong mathematical background will be better situated to proactively find fertile areas of the economy which can be improved through a combination of computing and advanced math. I don't believe this is realistic. None of the author's specific examples - control theory, optimization, linear programming, econometrics, or Black-Scholes - were pioneered by generalist engineers with a strong grounding in mathematics. They were just applied mathematicians (and in a few of those cases, repurposed pure mathematicians).
Likewise the bar is constantly rising. Knowing calculus, linear algebra and statistics won't empower you to make significant, groundbreaking improvements to any major field. That knowledge is necessary but insufficient. What's considered low hanging fruit these days is something that could be conceivably noticed and tackled by a postdoc. Mathematics itself is ossifying as a field of specialists, to the point where prominent researchers within subfields like algebraic geometry can be unfamiliar with each other's principal work.
This is really why find the appeal to economic advancement less than convincing - every advancement the author mentions has an implicit foundation in specialization. Historically, the most notable advancements in technology have come about through division of labor and cross-pollination. You would be better served by having two collaborating teams of mathematicians and engineers than by a team of engineers who know undergraduate math. It's already common for applied mathematicians to be able to program competently; they dominate the authorship of fast, low level math libraries. If you need them, hire them.
Conversely, I would argue we should actually reduce the education for more developers, not add even more material to it. Most developers already don't need to learn a significant amount of the material in a standard computer science degree. Piling on math courses isn't going to be a fundamental improvement for the vast majority of engineers working on real world software.
With regards to control theory, whilst some key people like Bode [0] had mathematics degrees, others such as Kalman [1], Nyquist [2], and Black [3] had electrical engineering degrees. Claude Shannon had a bachelor's degree in both electrical engineering and mathematics.
[0]: https://en.wikipedia.org/wiki/Hendrik_Wade_Bode
[1]: https://en.wikipedia.org/wiki/Rudolf_E._K%C3%A1lm%C3%A1n
Although you may not need to know the math to build such software, given the tools that we have available. However you would need to know the math in order to understand the limitations, pros/cons of a particular ML algorithm for a given application (at the very least).
Sometimes computation is necessary, but that doesn't mean you'll save time by avoiding analysis entirely. When mathematical analysis succeeds, the time saved can be considerable. And you can combine both, e.g., recently I sped up a non-linear algebraic equation solver for my work by using an ansatz that I derived using the perturbation method. (Also probably helps the solver converge to the right solution.)
One possible explanation: https://en.wiktionary.org/wiki/if_all_you_have_is_a_hammer,_...
The author’s proposed solution only works up to n = 78. If you want to speed up the calculation for small values, just cache them all.
Personally I don’t ever want a compiler trying to replace straight-forward integer algorithms with floating point formulas. YMMV.
https://gist.github.com/justinmeiners/f7d08f99e7e9b8b84f0183...
[1] https://www.comap.com/undergraduate/contests/mcm/contests/20...
We did this as whilst studying difference equations in first year undergraduate mathematics. I imagine most books dealing with the subject will include the Fibonacci equation as an example.