Differences
This shows you the differences between two versions of the page.
| en:algo:babbageproblem [2019-01-07 19:44] – created rainer | en:algo:babbageproblem [2025-02-02 19:48] (current) – added TAV program rainer | ||
|---|---|---|---|
| Line 1: | Line 1: | ||
| - | |||
| - | |||
| Charles Babbage wrote 1837: | Charles Babbage wrote 1837: | ||
| "What is the smallest positive integer | "What is the smallest positive integer | ||
| Line 100: | Line 98: | ||
| 971041 (largest result found so far with 6 digits) | 971041 (largest result found so far with 6 digits) | ||
| 3716 (for a 16 bit computer) | 3716 (for a 16 bit computer) | ||
| + | |||
| + | Some more information are in an example programme (written in TAV, see [[https:// | ||
| + | < | ||
| + | \( | ||
| + | Charles Babbage wrote 1837: | ||
| + | " | ||
| + | whose square ends in the digits 269, | ||
| + | |||
| + | A solution within the features of the Analytical Engine (AE) | ||
| + | would be appropriate, | ||
| + | and no floating-point numbers, no built-in square-root, | ||
| + | and the remainder of a division used to determine the trailing digits. | ||
| + | The (integral or fixed-point) numbers were rather long, 30 decimal digits. | ||
| + | We assume the execution time for an addition to be 1 second, | ||
| + | and for a division as well as a multiplication 1 second per digit | ||
| + | (excluding leading zeroes). Just to show the order of time, | ||
| + | 10 seconds will be assumed for each multiplication and division. | ||
| + | (Maybe the division by a power of ten was much quicker.) | ||
| + | |||
| + | The simple but reliable method to probe all intergers (manually | ||
| + | entered start point, e.g. 500 for the above example), | ||
| + | about 20000 multiplications and the same number of divisions were needed, | ||
| + | running about 400,000 seconds, or 111 hours or nearly 5 days. | ||
| + | Babbage surely would have never considered to use his precious machine | ||
| + | this way. | ||
| + | |||
| + | Instead of enumerating the squares by multiplication, | ||
| + | formula x² = (x-1)² + 2x - 1 could be used to enumerate the squares. | ||
| + | Now we have 3 seconds to provide the next square, 10 seconds for | ||
| + | the remainder, and 1 second to test the result, | ||
| + | i.e. 14*20,000 seconds or 78 hours or 3 days, still too long. | ||
| + | (In modern computers, where the multiplication is not slower than an addition, | ||
| + | this version will even be longer, as more instructions are performed). | ||
| + | |||
| + | It is well known that any square number can only end in the | ||
| + | digits 0, 1, 4, 5, 6 and 9; so the problem is solvable, as it ends in 6. | ||
| + | To produce the end digit 6, only numbers with the end digits 4 and 6 | ||
| + | need to be considered. | ||
| + | Thus, when enumerating squares, we can step by alternatingly | ||
| + | by 2 (_4 to _6) and 8 (_6 to _4), which reduces the time to | ||
| + | 4000 rounds with 14 seconds each, i.e. 56,000 seconds or 16 hours. | ||
| + | The same for the last two digits _96 gives _14, _64, _36 and _96 | ||
| + | nees 800 rounds, 11,200 seconds or 3 hours. | ||
| + | |||
| + | Still Babbage as a mathematicion would not use such a raw attempt; | ||
| + | but instead try to calculate the set of permissible set of endings: | ||
| + | The inital set for 10 as a modulus and 6 as ending digit is {4, 6}. | ||
| + | Now the sequences 04, 14, 24, ... and 06, 16, 26, ... squared | ||
| + | and probed for the ending ' | ||
| + | The next step adds multiplies of 100 to the numbers of the set, | ||
| + | and checks the squares for the ending ' | ||
| + | {264 236 736 764}. Two more sets, | ||
| + | {264 2236 2764 4736 5264 7236 7764 9736} and | ||
| + | {264 24736 25264 49736 50264 74736 75264 99736} are determined, | ||
| + | where the last one contains two solutions, 25264 and 99736. | ||
| + | As the sets have together 26 elements, for each 10 probes have to | ||
| + | be done, i.e. 260 divisions. But as various other operations | ||
| + | are required, we should estimate 20 seconds for each test, | ||
| + | thus 5200 seconds or nearly 2 hours. | ||
| + | |||
| + | An experienced human computer at that time can compare the trailing | ||
| + | digits very quickly, so the " | ||
| + | Moreover, the squares of 4, 14, 24, ... are not obtained by | ||
| + | multiplication, | ||
| + | (x + 10)² = x² + 2*10*x + 100 | ||
| + | As only the result modulo 100 is required, only twice the last digit | ||
| + | has to be added to the first digit, modulo 100. | ||
| + | The addend is 8 for a trailing 4, and 2 for a trailing 6. | ||
| + | For the number above, the pattern is in the first digits is recurring, | ||
| + | i.e. 1, 9, 7, 5, 3 for the first digit, if 4 is the last digit, | ||
| + | and 1, 3, 5, 7, 9 if 6 is the last digit. | ||
| + | So it is only left to count the distance, to obtain two new set elements, | ||
| + | and if the first square has the wrong ending (i.e. 36² = 296) and | ||
| + | an even first digit, the whole series can be skipped. | ||
| + | So one test would require not more than 5 seconds, giving 1300 seconds | ||
| + | or about 20 minutes or less than half an hour, which I can confirm. | ||
| + | (The setup not counted, as that has to be done to program a | ||
| + | coumputer anyhow. And the situation for other numbers may not be | ||
| + | equally advantageous.) | ||
| + | |||
| + | Note that for the number ' | ||
| + | the same time, but as the result is 126929 and most numbers | ||
| + | have more digits, the machine would require 20*100,000 seconds | ||
| + | or nearly 600 hours or 23 days. | ||
| + | |||
| + | The major advantage of the set method is that is terminates | ||
| + | if there is no result, while the others will run for ever. | ||
| + | |||
| + | There is an infinite number of solutions. | ||
| + | If ' | ||
| + | x² mod m = e | ||
| + | Then: | ||
| + | (x + 5m)² = x² + 10m x + 25m² = x² mod m | ||
| + | and for all z: | ||
| + | (x + z * 10m)² = x² + 20zm + 100m² = x² mod m | ||
| + | There may be more solutions, e.g. for the above example, 150264 | ||
| + | is another solution not generated as shown. | ||
| + | |||
| + | |||
| + | Other sample input numbers (all require more than 32 bits for the square): | ||
| + | 123456 (nice number) | ||
| + | 971041 (largest result found so far with 6 digits) | ||
| + | 3716 (for a 16 bit computer) | ||
| + | |||
| + | See also: | ||
| + | http:// | ||
| + | http:// | ||
| + | |||
| + | Options: | ||
| + | -d Debug | ||
| + | -v verbose, show sets | ||
| + | -t show times | ||
| + | |||
| + | \) | ||
| + | |||
| + | main(args): | ||
| + | opts =: row args parse options values () | ||
| + | |||
| + | ? opts{' | ||
| + | debug enable | ||
| + | system info enable | ||
| + | ? opts{' | ||
| + | debug enable \ use better solution | ||
| + | |||
| + | \ supply default argument(s) if none given | ||
| + | ? args.count = 1 \ no arguments | ||
| + | args[1] =: " | ||
| + | args[2] =: " | ||
| + | args[3] =: " | ||
| + | args[4] =: " | ||
| + | |||
| + | \ no scan, args[0] is program name | ||
| + | row args compact | ||
| + | ?# i =: from 1 upto args.count-1 | ||
| + | determine square ending in args[i] showtimes opts{' | ||
| + | print '' | ||
| + | |||
| + | |||
| + | | ||
| + | \( Main processing function for a single ending | ||
| + | Ending can be a string with leading zeros | ||
| + | \) | ||
| + | determine square ending in (ed) showtimes (showtimes): | ||
| + | \ determine modulus as m =: 10^(ed.count) | ||
| + | m =: 1 | ||
| + | ?# i =: from 1 upto ed.count | ||
| + | m =* 10 | ||
| + | e =: string ed as integer base 10 \ leading zeroes are not octal | ||
| + | |||
| + | \ check last digit | ||
| + | ld =: e %% 10 | ||
| + | ? ld = 2 | ld = 3 | ld = 7 | ld = 8 | ||
| + | error print " | ||
| + | :> | ||
| + | |||
| + | \ calculate sets of endings increasingly | ||
| + | t =: system cpu seconds | ||
| + | xx =: use sets of endings e mod m | ||
| + | ? showtimes | ||
| + | tt =: ((system cpu seconds) - t) * 1000.0 | ||
| + | tt =: format ' | ||
| + | ? xx = () | ||
| + | print "No solution" | ||
| + | :> | ||
| + | |||
| + | tail =: tt _ " (set method)" | ||
| + | ?# x =: map xx give keys ascending | ||
| + | x2 =: x * x | ||
| + | print x __ (split x2 at ed.count) __ tail | ||
| + | tail =: '' | ||
| + | ?> \ print only the first one | ||
| + | |||
| + | |||
| + | \ Simple, yet efficient only on modern machines: count and square | ||
| + | t =: system cpu seconds | ||
| + | x =: count and square find e mod m | ||
| + | ? showtimes | ||
| + | t =: ((system cpu seconds) - t) * 1000.0 | ||
| + | tt =: format ' | ||
| + | x2 =: x * x | ||
| + | \ steps =: x - math int sqrt e | ||
| + | print x __ (split x2 at ed.count) __ tt _ " (count and square)" | ||
| + | |||
| + | \ Enumerate squares | ||
| + | t =: system cpu seconds | ||
| + | x =: enumerate squares upto e mod m | ||
| + | ? showtimes | ||
| + | tt =: ((system cpu seconds) - t) * 1000.0 | ||
| + | tt =: format ' | ||
| + | x2 =: x * x | ||
| + | print x __ (split x2 at ed.count) __ tt _ " (enum squares linearily)" | ||
| + | |||
| + | \ Enumerate only squares ending in 4 or 6 | ||
| + | t =: system cpu seconds | ||
| + | x =: enumerate squares sparse upto e mod m | ||
| + | ? showtimes | ||
| + | tt =: ((system cpu seconds) - t) * 1000.0 | ||
| + | tt =: format ' | ||
| + | x2 =: x * x | ||
| + | print x __ (split x2 at ed.count) | ||
| + | |||
| + | |||
| + | split (x) at (l): | ||
| + | r =: '' | ||
| + | h =: string r upto r.count - l | ||
| + | t =: string r from -l | ||
| + | |||
| + | :> h _ ' | ||
| + | |||
| + | |||
| + | \ Simple method: Count and square | ||
| + | \ ================================================ | ||
| + | count and square find (ed) mod (m): | ||
| + | x =: integer ed sqrt | ||
| + | ?* (x*x) %% m ~= ed | ||
| + | x =+ 1 | ||
| + | :> x | ||
| + | |||
| + | |||
| + | \ Still simple: Enumerate squares | ||
| + | \ ================================================ | ||
| + | |||
| + | \( Enumerate squares starting below the square root | ||
| + | of a limit, until the limit is reached, | ||
| + | using the binomial formula | ||
| + | x² = (x-1)² + 2x - 1 | ||
| + | \) | ||
| + | |||
| + | enumerate squares upto (ed) mod (m): | ||
| + | x =: integer ed sqrt | ||
| + | y =: x * x | ||
| + | ?* y %% m ~= ed | ||
| + | x =+ 1 | ||
| + | y =+ 2*x - 1 | ||
| + | :> x | ||
| + | |||
| + | |||
| + | \ Enumerate sparse | ||
| + | \ ======================================= | ||
| + | |||
| + | \( Enumerate squares starting below the square root | ||
| + | of a limit, until the limit is reached. | ||
| + | Depending on the end digit, the end digits are | ||
| + | 0 0 | ||
| + | 1 1,9 | ||
| + | 4 2,8 | ||
| + | 5 5 | ||
| + | 6 4,6 | ||
| + | 9 3,7 | ||
| + | |||
| + | Stepping is in increments of 10 using the binomial formula | ||
| + | x² = (x-10)² + 20x - 100 | ||
| + | \) | ||
| + | |||
| + | enumerate squares sparse upto (ed) mod (m): | ||
| + | x =: integer ed sqrt | ||
| + | x =: 10 * (x // 10) \ must be multiple of 10 | ||
| + | |||
| + | ld =: ed %% 10 \ last digit | ||
| + | xa =: 0 | ||
| + | xb =: 0 | ||
| + | ? ld = 0 | ||
| + | xa =: x | ||
| + | ? ld = 1 | ||
| + | xa =: x + 1 | ||
| + | xb =: x + 9 | ||
| + | ? ld = 4 | ||
| + | xa =: x + 2 | ||
| + | xb =: x + 8 | ||
| + | ? ld = 5 | ||
| + | xa =: x + 5 | ||
| + | ? ld = 6 | ||
| + | xa =: x + 4 | ||
| + | xb =: x + 6 | ||
| + | ? ld = 9 | ||
| + | xa =: x + 3 | ||
| + | xb =: x + 7 | ||
| + | ! xa ~= 0 | ||
| + | |||
| + | ya =: xa * xa | ||
| + | yb =: xb * xb | ||
| + | ?* | ||
| + | ? ya %% m = ed | ||
| + | :> xa | ||
| + | xa =+ 10 | ||
| + | ya =+ 20*xa - 100 | ||
| + | ? xb = 0 | ||
| + | ?^ | ||
| + | ? yb %% m = ed | ||
| + | :> xb | ||
| + | xb =+ 10 | ||
| + | yb =+ 20*xb - 100 | ||
| + | |||
| + | |||
| + | |||
| + | |||
| + | \ Advanced: determine sets of endings | ||
| + | \ ================================== | ||
| + | |||
| + | \ from the old set, the new set is calculated | ||
| + | \ by squaring each number in the old set, plus a multiple of inc | ||
| + | \ | ||
| + | next set from (old) step (inc) find (ed) mod (m): | ||
| + | m =: 10*inc \ truncate | ||
| + | e =: ed %% m \ this ending | ||
| + | new =: {} | ||
| + | ?# x =: give keys old | ||
| + | \ multiplication used; linear advance is too complex here | ||
| + | ?# y =: from x upto (x+m-1) step inc | ||
| + | z =: y * y | ||
| + | z =: z %% m | ||
| + | \- print ?x __ ?i __ ?y __ ?z __ ?e __ ?m | ||
| + | ? z = e | ||
| + | new{y} =: y | ||
| + | :> new | ||
| + | |||
| + | |||
| + | show set (x): | ||
| + | \ print x.count _ ':' | ||
| + | ?# i =: map x give keys ascending | ||
| + | print i _ ' ' nonl | ||
| + | print '' | ||
| + | |||
| + | \ returns set of result | ||
| + | use sets of endings (e) mod (m): | ||
| + | res =: {} | ||
| + | set =: {} | ||
| + | set{0} =: 0 | ||
| + | |||
| + | \ | ||
| + | i =: 1 | ||
| + | ?* i < m | ||
| + | set =: next set from set step i find e mod m | ||
| + | ? debug is enabled | ||
| + | show set set | ||
| + | ? set.count = 0 | ||
| + | :> () \ no solution | ||
| + | i =* 10 | ||
| + | |||
| + | \ check if result is in final set | ||
| + | ?# y =: give keys set | ||
| + | z =: y * y | ||
| + | z =: z %% m | ||
| + | ? e = z | ||
| + | res{y} =: y | ||
| + | :> res | ||
| + | |||
| + | |||
| + | \ enumerate powers of 10 | ||
| + | powers of (base) upto (lim): | ||
| + | ? $ = () | ||
| + | $ =: 1 | ||
| + | | | ||
| + | $ =* base | ||
| + | ? $ > lim | ||
| + | $ =: () | ||
| + | :> $ | ||
| + | |||
| + | \ Helper Functions | ||
| + | \ ================ | ||
| + | |||
| + | \ Determine smallest power of ten larger than given number | ||
| + | \ As to round up the decimal logarithm is limited in precision, | ||
| + | \ use simple loop, which may even be quicker for small numbers. | ||
| + | xpower of ten above (x): | ||
| + | p =: 1 | ||
| + | ?* p <= x | ||
| + | p =* 10 | ||
| + | :> p | ||
| + | |||
| + | |||
| + | \+ stdlib | ||
| + | </ | ||
