#!/usr/bin/python # -*- coding: utf-8 -*- """Generate CSV file of Briggsian logarithms to import into Anki. The idea is to come up with a minimal set of logarithms to memorize in order to be able to calculate a product to within a given precision; 10% worst-case precision spaces the mantissas about 10% apart, and that gives you only about 30 logarithms to memorize. And if you memorize them to three places, you should be able to linearly interpolate to get better than 1% precision. This is turning out to be a harder problem than I had expected, because I discovered a hidden requirement in my subconscious that the numbers not skip over round numbers. So, for example, increasing by 10% from 1.0, we get to 1.1, 1.2, 1.3, 1.4, 1.5, 1.7, 1.9, and then 2.1, skipping over 2! (Also note that 1.7 is more than 10% high from 1.5.) So my strategy is to recursively subdivide the interval at a given precision. First subdivide the interval from 1 to 10 into 1, 2, 3, etc., which differ by more than 10%; then divide the interval from 1 to 2 as evenly as possible into 1, 1.1, 1.2, 1.3, 1.4, 1.5, 1.7, and 1.8; and then if we’re doing more digits of precision, subdivide each of those intervals to hit our desired precision. So for example in between 1.5 and 1.7 we would get 1.6, really 1.59687 or so. This is probably the best way to rule a slide rule as well. But this code doesn’t work yet; I need to rethink it. """ import csv import math import sys def subdivide(start, stop, spacing): """Yield numbers in [start, stop) equally exponentially spaced by spacing. spacing --- the worst-case relative error, like 0.1 for 10%. start --- the first number to yield. stop --- the first number that would follow the returned sequence. In general the numbers yielded will be spaced somewhat closer than spacing in order to evenly divide the interval between start and stop. """ interval_size_log = math.log(stop) - math.log(start) n_divisions = int(math.ceil(interval_size_log / math.log(1 + spacing))) return ((start * math.exp(i * interval_size_log / n_divisions) for i in range(n_divisions))) def recursively_subdivide(start, stop, precision, spacing): print start, stop, precision, round(start, precision), round(stop, precision) if round(start, precision) == round(stop, precision): return subdivide(start, stop, spacing) rv = [] recursion = recursively_subdivide(start, stop, precision-1, spacing) for substart, substop in pairs(recursion + [stop]): rv.extend(recursively_subdivide(substart, substop, precision, spacing)) return rv def pairs(seq): seq = iter(seq) last = seq.next() for item in seq: yield last, item last = item # This still doesn’t work either. It doesn’t jump far enough. def iteratively_subdivide(start, stop, precision, spacing): i = start while i < stop: yield i for size in range(precision+1): next_i = round(i + 10**-size, size) if size == precision or next_i <= i * (1 + spacing): break i = next_i def writelogs(output, spacing, mprecision, lprecision): mantissa = 1.0 while mantissa < 10.0: output.writerow(('%.*f' % (mprecision, mantissa), '%0.*f' % (lprecision, math.log10(mantissa)))) new_mantissa = round(max(mantissa * (1 + spacing), mantissa + 10**-mprecision), mprecision) # If we’ve jumped past a value that we should include, include it candidate = round(new_mantissa, mprecision-1) if mantissa < candidate < new_mantissa: new_mantissa = candidate mantissa = new_mantissa if __name__ == '__main__': writelogs(csv.writer(sys.stdout), spacing=0.1, mprecision=3, lprecision=3)