Iterated digits squaring: Difference between revisions
m (→{{header|REXX}}: because REXX version 1 is faster now, used 10 million as a default.) |
m (→version 2: changed the default (and output) for the REXX version 2 example.) |
||
Line 1,596: | Line 1,596: | ||
<lang rexx>/*REXX program to perform iterated digits squaring ('til sum=1 │ sum=89)*/ |
<lang rexx>/*REXX program to perform iterated digits squaring ('til sum=1 │ sum=89)*/ |
||
parse arg n . /*get optional N from the C.L. */ |
parse arg n . /*get optional N from the C.L. */ |
||
if n=='' then n= |
if n=='' then n=100 * 1000000 /*Was N given? No, use default.*/ |
||
!.=0; do m=1 to 9; !.m=m**2; end /*build a short-cut for squares. */ |
!.=0; do m=1 to 9; !.m=m**2; end /*build a short-cut for squares. */ |
||
$.=.; $.0=0; $.00=0; $.000=0; $.0000=0; @.=. /*short-cuts for some sums*/ |
$.=.; $.0=0; $.00=0; $.000=0; $.0000=0; @.=. /*short-cuts for some sums*/ |
||
Line 1,633: | Line 1,633: | ||
'''output''' when using the default input: |
'''output''' when using the default input: |
||
<pre> |
<pre> |
||
count of "1" chains for all natural numbers up to |
count of "1" chains for all natural numbers up to 100000000 is 14255667 |
||
count of "89" chains for all natural numbers up to |
count of "89" chains for all natural numbers up to 100000000 is 85744333 |
||
</pre> |
</pre> |
||
Revision as of 01:13, 25 May 2015
You are encouraged to solve this task according to the task description, using any language you may know.
If you add the square of the digits of a Natural number (an integer bigger than zero), you always end with either 1 or 89:
15 -> 26 -> 40 -> 16 -> 37 -> 58 -> 89 7 -> 49 -> 97 -> 130 -> 10 -> 1
An example in Python:
<lang python>>>> step = lambda x: sum(int(d) ** 2 for d in str(x)) >>> iterate = lambda x: x if x in [1, 89] else iterate(step(x)) >>> [iterate(x) for x in xrange(1, 20)] [1, 89, 89, 89, 89, 89, 1, 89, 89, 1, 89, 89, 1, 89, 89, 89, 89, 89, 1]</lang>
- Task
- Count how many number chains for integers 1 <= n < 100_000_000 end with a value 89.
Or, for much less credit - (showing that your algorithm and/or language is slow):
- Count how many number chains for integers 1 <= n < 1_000_000 end with a value 89.
This problem derives from the Project Euler problem 92.
For a quick algorithm for this task see the talk page
- Cf
BBC BASIC
Three versions timed on a 2.50GHz Intel Desktop. <lang bbcbasic> REM Version 1: Brute force
REM --------------------------------------------------------- T%=TIME N%=0 FOR I%=1 TO 100000000 J%=I% REPEAT K%=0:REPEAT K%+=(J%MOD10)^2:J%=J%DIV10:UNTIL J%=0 J%=K% UNTIL J%=89 OR J%=1 IF J%>1 N%+=1 NEXT PRINT "Version 1: ";N% " in ";(TIME-T%)/100 " seconds."
REM Version 2: Brute force + building lookup table REM --------------------------------------------------------- T%=TIME DIM B% 9*9*8,H%(9) N%=0 FOR I%=1 TO 100000000 J%=I% H%=0 REPEAT K%=0:REPEAT K%+=(J%MOD10)^2:J%=J%DIV10:UNTIL J%=0 H%(H%)=K%:H%+=1 J%=K% IF B%?J%=1 EXIT REPEAT UNTIL J%=89 OR J%=1 IF J%>1 N%+=1:WHILE H%>0:H%-=1:B%?H%(H%)=1:ENDWHILE NEXT PRINT "Version 2: ";N% " in ";(TIME-T%)/100 " seconds."
REM Version 3: Calc possible combinations (translation of C) REM --------------------------------------------------------- T%=TIME DIM B%(9*9*8):B%(0)=1 FOR N%=1 TO 8 FOR I%=9*9*N% TO 1 STEP -1 FOR J%=1 TO 9 S%=J%*J% IF S%>I% EXIT FOR B%(I%)+=B%(I%-S%) NEXT NEXT NEXT
N%=0 FOR I%=1 TO 9*9*8 J%=I% REPEAT K%=0:REPEAT K%+=(J%MOD10)^2:J%=J%DIV10:UNTIL J%=0 J%=K% UNTIL J%=89 OR J%=1 IF J%>1 N%+=B%(I%) NEXT PRINT "Version 3: ";N% " in ";(TIME-T%)/100 " seconds."
END</lang>
- Output:
Version 1: 85744333 in 1447.08 seconds. Version 2: 85744333 in 718.04 seconds. Version 3: 85744333 in 0.02 seconds.
C
C99, tested with "gcc -std=c99". Record how many digit square sum combinations there are. This reduces numbers to , and the complexity is about . The 64 bit integer counter is good for up to , which takes practically no time to run. <lang c>#include <stdio.h>
typedef unsigned long long ull;
int is89(int x) { while (1) { int s = 0; do s += (x%10)*(x%10); while ((x /= 10));
if (s == 89) return 1; if (s == 1) return 0; x = s; } }
int main(void)
{
// array bounds is sort of random here, it's big enough for 64bit unsigned.
ull sums[32*81 + 1] = {1, 0};
for (int n = 1; ; n++) { for (int i = n*81; i; i--) { for (int j = 1; j < 10; j++) { int s = j*j; if (s > i) break; sums[i] += sums[i-s]; } }
ull count89 = 0; for (int i = 1; i < n*81 + 1; i++) { if (!is89(i)) continue;
if (sums[i] > ~0ULL - count89) { printf("counter overflow for 10^%d\n", n); return 0; } count89 += sums[i]; }
printf("1->10^%d: %llu\n", n, count89); }
return 0; }</lang>
- Output:
1->10^1: 7 1->10^2: 80 1->10^3: 857 1->10^4: 8558 1->10^5: 85623 1->10^6: 856929 1->10^7: 8581146 1->10^8: 85744333 1->10^9: 854325192 1->10^10: 8507390852 1->10^11: 84908800643 1->10^12: 850878696414 1->10^13: 8556721999130 1->10^14: 86229146720315 1->10^15: 869339034137667 1->10^16: 8754780882739336 1->10^17: 87975303595231975 1->10^18: 881773944919974509 1->10^19: 8816770037940618762 counter overflow for 10^20
Fast C implementation (<1 second my machine), which performs iterated digits squaring only once for each unique 8 digit combination. The cases 0 and 100,000,000 are ignored since they don't sum to 89: <lang c>
- include <stdio.h>
const int digits[] = { 0,1,2,3,4,5,6,7,8,9 };
// calculates factorial of a number int factorial(int n) {
return n == 0 ? 1 : n * factorial(n - 1);
}
// returns sum of squares of digits of n unsigned int sum_square_digits(unsigned int n) {
int i,num=n,sum=0; // process digits one at a time until there are none left while (num > 0) { // peal off the last digit from the number int digit=num % 10; num=(num - digit)/10; // add it's square to the sum sum=sum+digit*digit; } return sum;
}
// builds all combinations digits 0-9 of length len // for each of these it will perform iterated digit squaring // and for those which result in 89 add to a counter which is // passed by pointer. long choose_sum_and_count_89(int * got, int n_chosen, int len, int at, int max_types, int *count89) {
int i; long count = 0; int digitcounts[10]; for (i=0; i < 10; i++) { digitcounts[i]=0; } if (n_chosen == len) { if (!got) return 1;
int sum=0; for (i = 0; i < len; i++) { int digit=digits[got[i]]; digitcounts[digit]++; sum=sum + digit * digit; } if (sum == 0) { return 1; } if ((sum != 1) && (sum != 89)) { while ((sum != 1) && (sum != 89)) { sum=sum_square_digits(sum); } } if (sum == 89) { int count_this_comb=factorial(len); for (i=0; i<10; i++) { count_this_comb/=factorial(digitcounts[i]); } (*count89)+=count_this_comb; }
return 1; }
for (i = at; i < max_types; i++) { if (got) got[n_chosen] = i; count += choose_sum_and_count_89(got, n_chosen + 1, len, i, max_types, count89); } return count;
}
int main(void) {
int chosen[10]; int count=0; // build all unique 8 digit combinations which represent // numbers 0-99,999,999 and count those // whose iterated digit squaring sum to 89 // case 0, 100,000,000 are ignored since they don't sum to 89 choose_sum_and_count_89(chosen, 0, 8, 0, 10, &count); printf("%d\n",count); return 0;
} </lang>
- Output:
85744333
C++
Slow (~10 seconds on my machine) brute force C++ implementation: <lang cpp>
- include <iostream>
// returns sum of squares of digits of n unsigned int sum_square_digits(unsigned int n) {
int i,num=n,sum=0; // process digits one at a time until there are none left while (num > 0) { // peal off the last digit from the number int digit=num % 10; num=(num - digit)/10; // add it's square to the sum sum+=digit*digit; } return sum;
} int main(void) {
unsigned int i=0,result=0, count=0; for (i=1; i<=100000000; i++) { // if not 1 or 89, start the iteration if ((i != 1) || (i != 89)) { result = sum_square_digits(i); } // otherwise we're done already else { result = i; } // while we haven't reached 1 or 89, keep iterating while ((result != 1) && (result != 89)) { result = sum_square_digits(result); } if (result == 89) { count++; } } std::cout << count << std::endl; return 0;
} </lang>
- Output:
85744333
D
A simple memoizing partially-imperative brute-force solution: <lang d>import std.stdio, std.algorithm, std.range, std.functional;
uint step(uint x) pure nothrow @safe @nogc {
uint total = 0; while (x) { total += (x % 10) ^^ 2; x /= 10; } return total;
}
uint iterate(in uint x) nothrow @safe {
return (x == 89 || x == 1) ? x : x.step.memoize!iterate;
}
void main() {
iota(1, 100_000_000).filter!(x => x.iterate == 89).count.writeln;
}</lang>
- Output:
85744333
The run-time is about 10 seconds compiled with ldc2.
A fast imperative brute-force solution: <lang d>void main() nothrow @nogc {
import core.stdc.stdio: printf;
enum uint magic = 89; enum uint limit = 100_000_000; uint[(9 ^^ 2) * 8 + 1] lookup = void;
uint[10] squares; foreach (immutable i, ref x; squares) x = i ^^ 2;
foreach (immutable uint i; 1 .. lookup.length) { uint x = i;
while (x != magic && x != 1) { uint total = 0; while (x) { total += squares[(x % 10)]; x /= 10; } x = total; }
lookup[i] = x == magic; }
uint magicCount = 0; foreach (immutable uint i; 1 .. limit) { uint x = i; uint total = 0;
while (x) { total += squares[(x % 10)]; x /= 10; }
magicCount += lookup[total]; }
printf("%u\n", magicCount);
}</lang> The output is the same. The run-time is less than 3 seconds compiled with ldc2.
A more efficient solution: <lang d>import core.stdc.stdio, std.algorithm, std.range;
enum factorial = (in uint n) pure nothrow @safe @nogc
=> reduce!q{a * b}(1u, iota(1u, n + 1));
uint iLog10(in uint x) pure nothrow @safe @nogc in {
assert(x > 0);
} body {
return (x >= 1_000_000_000) ? 9 : (x >= 100_000_000) ? 8 : (x >= 10_000_000) ? 7 : (x >= 1_000_000) ? 6 : (x >= 100_000) ? 5 : (x >= 10_000) ? 4 : (x >= 1_000) ? 3 : (x >= 100) ? 2 : (x >= 10) ? 1 : 0;
}
uint nextStep(uint x) pure nothrow @safe @nogc {
typeof(return) result = 0;
while (x > 0) { result += (x % 10) ^^ 2; x /= 10; } return result;
}
uint check(in uint[] number) pure nothrow @safe @nogc {
uint candidate = reduce!((tot, n) => tot * 10 + n)(0, number);
while (candidate != 89 && candidate != 1) candidate = candidate.nextStep;
if (candidate == 89) { uint[10] digitsCount; foreach (immutable d; number) digitsCount[d]++;
return reduce!((r, c) => r / c.factorial) (number.length.factorial, digitsCount); }
return 0;
}
void main() nothrow @nogc {
enum uint limit = 100_000_000; immutable uint cacheSize = limit.iLog10;
uint[cacheSize] number; uint result = 0; uint i = cacheSize - 1;
while (true) { if (i == 0 && number[i] == 9) break; if (i == cacheSize - 1 && number[i] < 9) { number[i]++; result += number.check; } else if (number[i] == 9) { i--; } else { number[i]++; number[i + 1 .. $] = number[i]; i = cacheSize - 1; result += number.check; } }
printf("%u\n", result);
}</lang> The output is the same. The run-time is about 0.04 seconds or less. This third version was ported to D and improved from: mathblog.dk/project-euler-92-square-digits-number-chain/
A purely functional version, from the Haskell code. It includes two functions currently missing in Phobos used in the Haskell code.
<lang d>import std.stdio, std.typecons, std.traits, std.typetuple, std.range, std.algorithm;
auto divMod(T)(T x, T y) pure nothrow @safe @nogc {
return tuple(x / y, x % y);
}
auto expand(alias F, B)(B x) pure nothrow @safe @nogc if (isCallable!F &&
is(ParameterTypeTuple!F == TypeTuple!B) && __traits(isSame, TemplateOf!(ReturnType!F), Nullable) && isTuple!(TemplateArgsOf!(ReturnType!F)[0]) && is(TemplateArgsOf!(TemplateArgsOf!(ReturnType!F)[0])[1] == B)) {
alias NAB = ReturnType!F; alias AB = TemplateArgsOf!NAB[0]; alias A = AB.Types[0];
struct Expand { bool first; NAB last;
@property bool empty() pure nothrow @safe @nogc { if (first) { first = false; popFront; } return last.isNull; }
@property A front() pure nothrow @safe @nogc { if (first) { first = false; popFront; } return last.get[0]; }
void popFront() pure nothrow @safe @nogc { last = F(last.get[1]); } }
return Expand(true, NAB(AB(A.init, x)));
}
//------------------------------------------------
uint step(uint x) pure nothrow @safe @nogc {
Nullable!(Tuple!(uint, uint)) f(uint n) pure nothrow @safe @nogc { return (n == 0) ? typeof(return)() : typeof(return)(divMod(n, 10u).reverse); }
return expand!f(x).map!(x => x ^^ 2).sum;
}
uint iter(uint x) pure nothrow @nogc {
return x.recurrence!((a, n) => step(a[n - 1])).filter!(y => y.among!(1, 89)).front;
}
void main() {
iota(1u, 100_000u).filter!(n => n.iter == 89).count.writeln;
}</lang> With a small back-porting (to run it with the Phobos of LDC2 2.065) it runs in about 15.5 seconds.
ERRE
<lang ERRE> PROGRAM ITERATION
BEGIN
PRINT(CHR$(12);) ! CLS INPUT(N) LOOP N$=MID$(STR$(N),2) S=0 FOR I=1 TO LEN(N$) DO A=VAL(MID$(N$,I,1)) S=S+A*A END FOR IF S=89 OR S=1 THEN PRINT(S;) EXIT END IF PRINT(S;) N=S END LOOP PRINT
END PROGRAM </lang> This program verifies a number only. With a FOR..END FOR loop it's possible to verify a number range.
Go
It's basic. Runs in about 30 seconds on an old laptop.
<lang go>package main
import ( "fmt" )
func main() { var d, n, o, u, u89 int64
for n = 1; n < 100000000; n++ { o = n for { u = 0 for { d = o%10 o = (o - d) / 10 u += d*d if o == 0 { break } } if u == 89 || u == 1 { if u == 89 { u89++ } break } o = u } } fmt.Println(u89) }</lang>
- Output:
85744333
Haskell
Basic solution that contains just a little more than the essence of this computation. This runs in less than eight minutes: <lang haskell>import Data.List (unfoldr) import Data.Tuple (swap)
step :: Int -> Int step = sum . map (^ 2) . unfoldr f where
f 0 = Nothing f n = Just . swap $ n `divMod` 10
iter :: Int -> Int iter = head . filter (`elem` [1, 89]) . iterate step
main = do
print $ length $ filter ((== 89) . iter) [1 .. 99999999]</lang>
- Output:
85744333
J
Here's an expression to turn a number into digits:
<lang J>digits=: 10&#.inv</lang>
And here's an expression to square them and find their sum: <lang J>sumdigsq=: +/"1@:*:@digits</lang>
But note that while the task description claims "you always end with either 1 or 89", that claim is somewhat arbitrary.
- But only somewhat the loop is 89 → 145 → 42 → 20 → 4 → 16 → 37 → 58 → 89, so it only ends with 1 or one of the numbers in this loop. 42 is of course far more significant and the one I would choose!!--Nigel Galloway (talk) 10:12, 16 September 2014 (UTC)
<lang J> sumdigsq^:(i.16) 15 15 26 40 16 37 58 89 145 42 20 4 16 37 58 89 145</lang>
You could just as easily claim that you always end with either 1 or 4. So here's a routine which repeats the sum-square process until the sequence converges, or until it reaches the value 4:
<lang J>itdigsq4=:4 = sumdigsq^:(0=e.&4)^:_"0</lang>
But we do not actually need to iterate. The largest value after the first iteration would be:
<lang J> sumdigsq 99999999 648</lang>
So we could write a routine which works for the intended range, and stops after the first iteration: <lang J>digsq1e8=:(I.itdigsq1 i.649) e.~ sumdigsq</lang>
And this is sufficient to find our result. We don't want to compute the entire batch of values in one pass, however, so let's break this up in to 100 batches of one million each:
<lang J> +/+/@:-.@digsq1e8"1(1+i.100 1e6) 85744333</lang>
Of course, there are faster ways of obtaining that result. The fastest is probably this: <lang J> 85744333 85744333</lang>
This might be thought of as representing the behavior of a highly optimized compiled program. We could abstract this further by using the previous expression at compile time, so we would not have to hard code it.
Julia
Brute-force solution, which runs in about 12 seconds on my machine: <lang julia>function iterate(m)
while m != 1 && m != 89 s = 0 while m > 0 # compute sum of squares of digits m, d = divrem(m, 10) s += d*d end m = s end return m
end function itercount(N)
count = 0 for n in 1:N count += iterate(n) == 89 end return count
end</lang> More clever combinatorial solution that loops over all unique sets of digits (runs in < 0.01 seconds): <lang julia>function itercount_combinations(ndigits)
count = 0 f = factorial(ndigits) # loop over all combinations of ndigits decimal digits: for c in combinations([1:(10+ndigits-1)],ndigits) s = 0 perms = 1 prevdigit = -1 repeat = 1 for k = 1:length(c) # sum digits^2 and count permutations digit = c[k]-k s += digit*digit # accumulate number of permutations of repeated digits if digit == prevdigit repeat += 1 perms *= repeat else prevdigit = digit repeat = 1 end end if s > 0 && iterate(s) == 89 count += div(f, perms) # numbers we can get from digits end end return count
end</lang>
- Output:
julia> @time itercount(100_000_000) elapsed time: 12.45469116 seconds (96 bytes allocated) 85744333 julia> @time itercount_combinations(8) elapsed time: 0.007778687 seconds (6223784 bytes allocated) 85744333 julia> @time itercount_combinations(17) elapsed time: 1.97602701 seconds (1299813368 bytes allocated, 39.95% gc time) 87975303595231975
Java
<lang java>import java.util.stream.IntStream;
public class IteratedDigitsSquaring {
public static void main(String[] args) { long r = IntStream.range(1, 100_000_000) .parallel() .filter(n -> calc(n) == 89) .count(); System.out.println(r); }
private static int calc(int n) { while (n != 89 && n != 1) { int total = 0; while (n > 0) { total += Math.pow(n % 10, 2); n /= 10; } n = total; } return n; }
}</lang>
85744333
jq
The algorithm presented here caches the results for 1 ... D*81 (where D is the relevant number of digits) and uses the combinatorial approach, but to keep things relatively brief, the factorials themselves are not cached.
Part 1: Foundations <lang jq>def factorial: reduce range(2;.+1) as $i (1; . * $i);
- Pick n items (with replacement) from the input array,
- but only consider distinct combinations:
def pick(n):
def pick(n; m): # pick n, from m onwards if n == 0 then [] elif m == length then empty elif n == 1 then (.[m:][] | [.]) else ([.[m]] + pick(n-1; m)), pick(n; m+1) end; pick(n;0) ;
- Given any array, produce an array of [item, count] pairs for each run.
def runs:
reduce .[] as $item ( []; if . == [] then [ [ $item, 1] ] else .[length-1] as $last | if $last[0] == $item then (.[0:length-1] + [ [$item, $last[1] + 1] ] ) else . + $item, 1 end end ) ;</lang>
Part 2: The Generic Task
Count how many number chains beginning with n (where 0 < n < 10^D) end with a value 89. <lang jq>def terminus:
# sum of the squared digits def ssdigits: tostring | explode | map(. - 48 | .*.) | add;
if . == 1 or . == 89 then . else ssdigits | terminus end;
- Count the number of integers i in [1... 10^D] with terminus equal to 89.
def task(D):
# The max sum of squares is D*81 so return an array that will instantly # reveal whether n|terminus is 89: def cache: reduce range(1; D*81+1) as $d ([false]; . + [$d|terminus == 89]);
# Compute n / (i1! * i2! * ... ) for the given combination, # which is assumed to be in order: def combinations(n): runs | map( .[1] | factorial) | reduce .[] as $i (n; ./$i);
cache as $cache | (D|factorial) as $Dfactorial | reduce ([range(0;10)] | pick(D)) as $digits (0; ($digits | map(.*.) | add) as $ss | if $cache[$ss] then . + ($digits|combinations($Dfactorial)) else . end) ;</lang>
Part 3: D=8 <lang jq>task(8)</lang>
- Output:
<lang sh>$ jq -M -n -f Iterated_digits_squaring_using_pick.jq 85744333
- Using jq>1.4:
- user 0m2.595s
- sys 0m0.010s
- Using jq 1.4:
- user 0m3.942s
- sys 0m0.009s</lang>
Lua
<lang lua>squares = {}
for i = 0, 9 do
for j = 0, 9 do squares[i * 10 + j] = i * i + j * j end
end
for i = 1, 99 do
for j = 0, 99 do squares[i * 100 + j] = squares[i] + squares[j] end
end
function sum_squares(n)
if n < 9999.5 then return squares[n] else local m = math.floor(n / 10000) return squares[n - 10000 * m] + sum_squares(m) end
end
memory = {}
function calc_1_or_89(n)
local m = {} n = memory[n] or n while n ~= 1 and n ~= 89 do n = memory[n] or sum_squares(n) table.insert(m, n) end for _, i in pairs(m) do memory[i] = n end return n
end
counter = 0
for i = 1, 100000000 do
if calc_1_or_89(i) == 89 then counter = counter + 1 end
end
print(counter)</lang>
- Output:
85744333
Mathematica / Wolfram Language
<lang Mathematica>sumDigitsSquared[n_Integer] := Total[IntegerDigits[n]^2] stopValues = Join[{1}, NestList[sumDigitsSquared, 89, 7]]; iterate[n_Integer] :=
NestWhile[sumDigitsSquared, n, Intersection[stopValues, {#}] == {} &]
numberOfDigits = 8; maxSum = numberOfDigits 9^2; loopVariables =
ToExpression@Table["i" <> ToString[n], {n, numberOfDigits}];
iteratesToOne = Cases[Range@maxSum, _?(iterate[#] == 1 &)]; allIterators =
Flatten[{Reverse@#, 9}] & /@ Partition[loopVariables, 2, 1];
maxCombinations = numberOfDigits!;
ssd =
SparseArray[Table[n^2 -> numberOfDigits, {n, 9}], {maxSum}];
Do[
variables = loopVariables;; digitCount; iterators = allIterators;; digitCount - 1; Do[ssd += SparseArray[ Total[variables^2] -> maxCombinations/ Times @@ (Tally[PadRight[variables, numberOfDigits]][[All, 2]]!), {maxSum}], {i, 9}, Evaluate[Sequence @@ iterators]], {digitCount, 2, numberOfDigits}];
onesCount =
Total[Cases[ ArrayRules[ssd] /. HoldPattern[{a_} -> b_] :> {a, b}, {_?(MemberQ[iteratesToOne, #] &), _}]All, 2];
(10^numberOfDigits - 1) - onesCount</lang>
- Output:
85744333
Oberon-2
]]
} return $n
} for {set i 1} {$i <= 100000000} {incr i} {
incr count [expr {[ids $i] == 89}]
} puts $count</lang>
Intelligent Version
Conversion back and forth between numbers and strings is slow. Using math operations directly is much faster (around 4 times in informal testing). <lang tcl>proc ids n {
while {$n != 1 && $n != 89} {
for {set m 0} {$n} {set n [expr {$n / 10}]} { incr m [expr {($n%10)**2}] } set n $m
} return $n
} for {set i 1} {$i <= 100000000} {incr i} {
incr count [expr {[ids $i] == 89}]
} puts $count</lang>
Substantially More Intelligent Version
Using the observation that the maximum value after 1 step is obtained for 999999999, which is . Thus, running one step of the reduction and then using a lookup table (which we can construct quickly at the start of the run, and which has excellent performance) is much faster overall, approximately 3–4 times than the second version above (and over 12 times faster than the first version).
- Donald, you have 1 too many 9's the value after step 1 is 81*8 = 648. Not that that is the problem here, you can not afford to go around this loop 100 million times. Notice that IDS[21] == IDS[12], IDS[123] == IDS[132] == IDS[213} ... etc, etc. The Ruby version takes about a tenth of a second.--Nigel Galloway (talk) 12:47, 31 August 2014 (UTC)
<lang tcl># Basic implementation proc ids n {
while {$n != 1 && $n != 89} {
for {set m 0} {$n} {set n [expr {$n / 10}]} { incr m [expr {($n%10)**2}] } set n $m
} return $n
}
- Build the optimised version
set body {
# Microoptimisation to avoid an unnecessary alloc in the loop for {set m 0} {$n} {set n [expr {"$n[unset n]" / 10}]} {
incr m [expr {($n%10)**2}]
}
} set map 0 for {set i 1} {$i <= 729} {incr i} {
lappend map [ids $i]
} proc ids2 n [append body "return \[lindex [list $map] \$m\]"]
- Put this in a lambda context for a little extra speed.
apply {{} {
set count 0 for {set i 1} {$i <= 100000000} {incr i} {
incr count [expr {[ids2 $i] == 89}]
} puts $count
}}</lang>
zkl
Using brute force is a never ending process so need to be clever, which takes under a second.
<lang zkl>fcn check(number){ // a list of digits: 13 is L(0,0,0,0,0,0,1,3)
candidate:=number.reduce(fcn(sum,n){ sum*10 + n },0); // digits to int
while(candidate != 89 and candidate != 1) // repeatedly sum squares of digits { candidate = candidate.split().reduce(fcn(sum,c){ sum + c*c },0); } if(candidate == 89){ // count permutations of these digits, they all sum to 89 digitsCount:=List(0,0,0,0,0,0,0,0,0,0); foreach d in (number){ digitsCount[d] += 1; } return(digitsCount.reduce(fcn(r,c){ r/factorial(c) },cacheBang)); // cacheBang==number.len()! } 0 // this number doesn't sum to 89 (ie sums to 1)
} fcn factorial(n) { (1).reduce(n,fcn(N,n){ N*n },1) }
limit:=0d100_000_000; cacheSize:=limit.toFloat().log10().ceil().toInt(); number:=(0).pump(cacheSize,List().write,0); // list of zeros result:=0; i:=cacheSize - 1; var cacheBang=factorial(cacheSize); //== number.len()!
while(True){ // create numbers s.t. no set of digits is repeated
if(i == 0 and number[i] == 9) break; if(i == cacheSize - 1 and number[i] < 9){ number[i] += 1; result += check(number); } else if(number[i] == 9) i -= 1; else{ number[i] += 1; foreach j in ([i + 1 .. cacheSize - 1]){ number[j] = number[i]; } i = cacheSize - 1; result += check(number); }
} println(result);</lang>
- Output:
85744333