Would you believe me if I told you that this fibonacci function actually produces the right result?
def fibonacci(n):
def is_power_of_two(counter):
bin = "{0:b}".format(counter)
return bin[0] == "1" and all([x == "0" for x in bin[1:]])
# http://lomont.org/posts/2017/fractran/
fractions = [
(17, 65),
(133, 34),
(17, 19),
(23, 17),
(2233, 69),
(23, 29),
(31, 23),
(74, 341),
(31, 37),
(41, 31),
(129, 287),
(41, 43),
(13, 41),
(1, 13),
(1, 3),
]
counter = 78 * pow(5,n)
while True:
if is_power_of_two(counter):
return int(math.log2(counter))
for (numerator, denominator) in fractions:
if counter % denominator == 0:
counter //= denominator
counter *= numerator
breakYou probably wouldn't, but if you run it you'll see that it actually does work. This is fractran, an esoteric programming language developed by John Conway. If you remove the is_power_of_two part of the while loop, this becomes a turing complete programming language where you can compute anything given the correct list of fractions.
The magic of fractran fades a little bit when you realize the trick. Simply put, fractran is a language with registers and one instruction
- The "registers" are stored in the
countervariable. If you prime factorize the counter, the powers of the prime factors are the values of the registers. So24is the same asregs[2]=3,regs[3]=1. - Each fraction is then a conditional register addition/subtraction instruction. The denominator indicates the condition that the registers much reach as well as the subtraction (
24in the denominator means this instruction will only be run ifregs[2]>=3andregs[3]>=1). The numerator represents additions to the register values
With this knowledge the fractions seem much less obtuse and you can kinda understand how this would be Turing complete. Even still, writing a program out of a list of conditional register instructions is still non-trivial (you need to encode a program counter somehow!)
I wanted to mess around with a few fractran interpreters to see how much it could be optimized. Obviously storing the register values directly instead of having an arbitrarly large counter value increases the performance, but how much farther can we go? Can we possibly lift the program counter out of the list of fractions and slowly lift this up to a more imperative instruction set?