(ad + bc) = ((a + b) Γ (c + d)) β ac β bd.
First note this equation is more clearly be written as:
ad + bc = (a + b)(c + d) β ac β bd.
To see why this is so first expand (a + b)(c + d).
(a + b)(c + d) = ac + ad + bc + bd
now
(a + b)(c + d) β ac β bd = ac + ad + bc + bd β ac β bd
Hence
ad + bc = (a + b)(c + d) β ac β bd.
There are other weird formatting things in this article, which I blame on AI. I don't think the whole article was written by AI, but the copy-editing and formatting looks like an AI messed up things, such as those pointless round brackets or the inconsistency of multiplication (sometimes there is a Γ sign, sometimes there isn't), or this:
> have suspected that O(nΒ²) was an inherent speed limit for multiplication. The celebrated Soviet math professor Andrey Kolmogorov posed the O(n^2)
The AI can't decide on notation.
Take the first digit of the longer number. Multiply it by the shorter number and store the result. Take the second digit of the longer number. If it matches the first digit, do a lookup of the last result and use that, else multiply and store. Repeat.
There will be a maximum of 10 * (length of the shorter number) multiplies, because there are only 10 unique digits. After that every operation is a lookup.
You could even do a tiny optimization by skipping the multiplication for the zero digit.
Worst case, the two numbers are the same length, in which case it's O(n/2 * 10), which is a heck of a lot better than O(n log n).
What am I missing here?
EDIT to respond to the comments: in the article, they are only counting the number of multiplies in the O() value. They are not including the adds.
https://tech.yahoo.com/science/articles/mathematicians-still...
But obviously multiplying two n-bit binary numbers is not done in O(1) time, so "only counting the number of multiplies" is not a meaningful model, and not the model adopted by the researchers quoted in the article.
This simplification relies on the fact that after making a multiplication the cost of merging it with the result of another is always less than the cost of performing the multiplication, so it doesn't change the overall complexity.
This is not true in your proposed algorithm: a lookup is O(1), but merging is O(N), so you cannot do the same simplification and have to count the complexity of performing adds as well.
The lookup table would not work for that case
Addition is O(n), multiplication is more than O(n), by the nature of the big-O notation, when you have to do a series of operation, you only have to count the ones with the highest complexity. So in the Karatsuba example where the formula involves both additions and (recursive) multiplications, the additions don't count only because the multiplications dominate.
Or, as a formula, O(n log n) + O(n) = O(n log n), and btw O(n/2*10) = O(n), in big-O, constant factors don't count
I had a lot of fun hacking on this idea together with the maintainer of the NUMERIC data type, and after two months the patch finally was ready and got committed:
https://git.postgresql.org/gitweb/?p=postgresql.git;a=commit...
So for numbers we normally work with, no. Maybe with cryptographic operations though.
> Please don't complain about tangential annoyancesβe.g. article or website formats, name collisions, or back-button breakage. They're too common to be interesting.
Tons and tons of sites have annoying popups. As such, complaining about it is usually off topic; that is, the complaint has nothing to do with the substance of the specific article that was posted for discussion.
The best place for such a complaint is to send it back to the website publisher. Then at least they will know their audience doesnβt like it.
There is a nice picture of the "best" choice for different ranges of sizes of numbers to be multiplied at http://gmplib.org/devel/log.i7.1024.png
More context and explanation can be found at: http://gmplib.org/devel/
Yes, but it suffers from a large amount of space complexity, and probably would have high constant factors in practice.
If additions were truly free, an even easier optimal algorithm would just be repeated addition involving zero multiplications.
Some mathematical researchers are working in the million, billion, or even trillion-bit range.
Memoizing number-by-digit multiplication doesn't make multiplication O(1) because one must still do an N-digit addition (which is O(N)) for each digit.
Elementary school students might memorize their times tables for single-digit numbers, but memorization wonβt cut it when the teacher asks for three-digit multiplication. This requires an algorithm: students are taught to stack one number atop another and multiply each digit of the bottom number by each digit of the top one. For millennia, mathematicians believed this to be the fastest multiplication method, until a 23-year-old made a shocking discovery in 1960, which led to a mystery that remains unsolved to this day.
This mystery is critical to anyone who partakes in the digital world because multiplication is a foundational operation for computers. Encryption, robotics, artificial intelligence, audio processing and pretty much everything else we task silicon chips with involves multiplication, sometimes of huge numbers many times over. At this scale, even a simple operation becomes a bottleneck, and any extra efficiency has global economic consequences.
To understand the nature of that bottleneck, observe how the grade-school algorithm handles growth. When you multiply two two-digit numbers, you perform four single-digit multiplications. If you bump that up to a three-digit pair of numbers, you do nine single-digit multiplications. The workload scales with the square of the number of digits (_n_2, where n is the number of digits in the numbers being multiplied). When analyzing an algorithm like this, computer scientists donβt measure speed in seconds, because that depends on the hardware. Instead they count the computational steps. They also ignore minor bookkeeping details, such as the time it takes to carry a one when multiplying. When numbers get large enough, those lower-level operations cease to matter, entirely eclipsed by the more intensive operations involved. Computer scientists denote the number of steps using whatβs called Big O notation: the grade-school algorithm, for example, takes O(_n_2) steps, which is read as βorder n squared.β Broadly speaking, if the numbers are twice as long, the algorithm takes four times as much computational work to execute. If the numbers are a thousand times as long, it takes a million (1,000 squared) times as much work.
If you're enjoying this article, consider supporting our award-winning journalism by subscribing. By purchasing a subscription you are helping to ensure the future of impactful stories about the discoveries and ideas shaping our world today.
Since antiquity, mathematicians have suspected that O(_n_2) was an inherent speed limit for multiplication. The celebrated Soviet math professor Andrey Kolmogorov posed the O(n^2) speed limit as a formal conjecture and mentioned it during a 1960 seminar at Moscow State University. Whenever mathematicians propose a conjecture, they are planting a flag of sorts and waiting for others to either prove or disprove them. It took just a week for Anatoly Karatsuba, then a 23-year-old student in the audience, to return and prove Kolmogorov wrong. Kolmogorov was stunned. The result was published in the prestigious Proceedings of the USSR Academy of Sciences, but amusingly, Karatsuba didnβt write it. Kolmogorov wrote the formal proof himself and submitted it for publication with Karatsuba listed as the lead author. Karatsuba only found out about the paper when he received the reprints in the mail.
Karatsubaβs genius was realizing that you can trade expensive, time-consuming multiplications for cheap, fast additions. Adding two n-digit numbers takes only O(n) time because it entails a single sweep through the digits rather than a complete sweep through the top number for every digit of the bottom number, as in multiplication. To see how Karatsuba traded multiplication for addition, letβs look at a small example. The method would be overly complicated for such a simple problem, but it saves a meaningful amount of time when numbers get larger.
In this simple example, letβs calculate 12 Γ 34.
First, we split both numbers into their tens and ones digits. Assign a = 1 and b = 2 (for 12), and c = 3 and d = 4 (for 34). Algebraically, we can rewrite 12 Γ 34 as (10_a_ + b) Γ (10_c_ + d).
Expanding this gives 100(ac) + 10(ad + bc) + (bd).
To solve the equation in the traditional way, one must perform four distinct multiplications: ac = 3, ad = 4, bc = 6 and bd = 8, which is exactly what the grade-school stacking method entails. (Note that we donβt count the multiplications by 100 or by 10 because those only involve plopping zeroes at the ends of numbers.) Karatsuba noticed a brilliant algebraic trick. Once you compute the first and last terms, ac and bd, you can figure out that pesky middle term (ad + bc) with one more multiplication step rather than two. You donβt need to calculate ad and bc individually:
(ad + bc) = ((a + b) Γ (c + d)) β ac β bd,
Or with our concrete numbers:
((1 Γ 4) + (2 Γ 3)) = ((1 + 2) Γ (3 + 4)) β 3 β 8 = 10.
Pause to notice the weirdness in the equation above. It suggests that to multiply 12 Γ 34 quickly, you should add the 1 and the 2 in 12 and the 3 and the 4 in 34. This is hardly a natural thing to do. No wonder it took so long for someone to figure it out. It ends up reducing the workload, however: because weβve already computed ac and bd, the right-hand side only contains one more multiplication, plus some additions and subtractions.
Returning to 100(ac) + 10(ad + bc) + (bd), we only need three multiplications rather than four. We compute ac and bd in the straightforward way and then use Karatsubaβs trick to compute (ad + bc) with a single multiplication. Plugging in ac = 3, bd = 8 and (ad + bc) = 10 gives our answer of 408.
We shaved one multiplication off the procedure. If that seems measly, Karatsuba has another insight in store. Say weβre multiplying bigger numbers: 1,234 Γ 5,678. We split them in half like we did before: a = 12, b = 34, c = 56 and d = 78, and we write the problem as (100_a_ + b) Γ (100_c_ + d) = 10,000(ac) + 100(ad + bc) + (bd).
We can solve this with three multiplications. Those multiplications, however, now involve two-digit numbers. Luckily, we know a way to multiply two-digit numbers with only three single-digit multiplications each! In total, a problem that would take 16 single-digit multiplications the traditional way now needs only nine. By recursively applying Karatsubaβs trick on large numbers, the savings compound. It splits the input numbers in half, then splits those halves in half, and so on, applying this four-for-three trade all the way down. The algorithm works out to have a running time of roughly O(_n_1.585), which is drastically faster than O(_n_2). For reference, multiplying a pair of thousand-digit numbers involves a million single-digit multiplications using the grade-school method but fewer than 57,000 using Karatsubaβs algorithm.
The 23-year-oldβs efficiency is baked into software that runs every day. Because of its extra overhead (the additions, managing the repeated splitting and recombining of numbers, and so on) its advantages over the grade-school algorithm donβt kick in until numbers grow relatively big. Python, for example, is a popular programming language that is famously smooth at handling integers of any size. If you peek into Python's underlying source code (search βKaratsubaβ here), you will see it relies on a hybrid approach. For modestly sized inputs, it uses the grade-school math, but once numbers reach around 630 decimal digits, it flips a switch and employs Karatsubaβs algorithm. That many digits might seem gargantuan by terrestrial standards, but computers deal with much bigger. (Technical aside: On most modern machines Python stores large numbers in base 230, so the linked Karatsuba cutoff of 70 digits in base 230 translates to roughly 630 decimal digits).
Karatsubaβs algorithm ignited a decades-long race to find the ultimate speed limit of multiplication. That pursuit culminated in 2019, when mathematicians David Harvey and Joris van der Hoeven described an extremely sophisticated algorithm that beat Karatsubaβs by more than any of the previous breakthroughs. The new algorithm runs in O(n Γ log n) time. Here log denotes the logarithm of n, which is a function that grows very slowly. Itβs a staggering result. The function n Γ log n is just a little bit bigger than n itself. This means that calculating the product of two massive numbers requires only a little more time than adding them, or even reading them, would in the first place (it takes n computational steps to read all n digits of a number).
The triumph comes with a crucial caveat, though. Just as Karatsubaβs algorithm only outperforms the grade-school approach when numbers get reasonably large, the Harvey-van der Hoeven algorithm doesnβt pull ahead until the numbers become truly galactic. In computer science, a βgalactic algorithmβ is a formal term for a method that is impressively efficient on sufficiently large numbers but will never be useful in practice because the numbers are so large.
Even with that asterisk, it was a watershed achievement. It secured the record for the fastest known multiplication method in principle and could pave the way for algorithms that run in O(n Γ log n) steps, not just in principle, but in practice. Today, theoretical computer scientists widely suspect that O(n Γ log n) is the fastest possible speed for multiplication, and formally proving it has become the holy grail for this niche field of mathematics. But as history reminds us, widespread consensus is not a mathematical proof. Conjectures about the speed limit of multiplication have been overturned before.
There's also NTT / Fourier multiplication as an option, for big integers or polynomials or modular arithmetic.
>All O(1) with lookup table!
k bits digits
10 319 96
20 639 192
30 959 288
40 1279 385
50 1599 481
75 2399 722
100 3199 963
150 4799 1444
250 7999 2408
500 15999 4816
1000 31999 9632
Here it is if we use the k largest primes that fit in 16-bit unsigned integers: k bits digits
10 159 48
20 319 96
30 479 144
40 639 192
50 799 240
75 1199 361
100 1598 481
150 2397 721
250 3991 1201
500 7967 2398
1000 15868 4776
If we use primes that fit in 8-bit unsigned integers, here's what we can handle with the largest k such primes. This table only goes to 54 because after that we run out of primes. k bits digits
10 78 23
20 152 45
30 220 66
40 281 84
50 327 98
54 334 100Btw, your recursion doesn't necessarily need a terminating case.
See eg this definition of the list of Fibonacci numbers in Haskell:
fibs :: [Integer]
fibs = 1 : 1 : zipWith (+) fibs (tail fibs)A: "X people don't know how to do Y"
B: "Why not do Z?"
A: "Z is too easy and boring so they actually added more restrictions to how you are allowed to do Y so that solution doesn't count"
Moreover it only works with bounded inputs. If your input is unbounded (as is this case with multiplication over arbitrarily large numbers) then an infinitely big lookup table is just not possible because it's part of the algorithm and hence needs to be finite.
If infinitely-big lookup tables were allowed you could for example write an algorithm that solves the halting problem, just index into the lookup table for its solution. And actually you could do this for any problem! So any problem, even so called "non computable" ones, admit a solution that runs in time linear to their input. I hope you see that this is nonsensical and it's why lookup tables are considered part of the algorithm and hence need to be finite.
> You might not "want" something in a proof, but if it works then it works.
And at the same time you don't get to change the definition of algorithm to allow your "proof" to be valid, otherwise you're just talking about nonsense.
It is other people who are trying to attack my statement who are trying to apply words like "algorithm" to it. You have such a fixed view of how things operate that you are failing to be able to take a step back and accept the existence of being able to solve any problem in a single step. You can claim that it is obvious, and trivial, and uninteresting, but that doesn't invalidate it. Sometimes thinking outside of the box is required and such strict adherence to what has come before can cloud your view of an "obvious" solution that was there the whole time.
Then sure, go ahead. Define your own model of computation, one not based on turing machines and which somehow allows for infinitely big lookup tables, and then see what great insight it provides you.
I wonder discoveries you will be able to make in a system where you can say that the solution of a problem is just its solution.