Jump to content

Combinations with repetitions/Square digit chain

From Rosetta Code
Combinations with repetitions/Square digit chain is a draft programming task. It is not yet considered ready to be promoted as a complete task, for reasons that should be found in its talk page.

Iterated digits squaring introduces RC the Project Euler Task #92. Combinations with repetitions introduce RC to the concept of generating all the combinations with repetitions of n types of things taken k at a time.

The purpose of this task is to combine these tasks as follows:

The collections of k items will be taken from [0,1,4,9,16,25,36,49,64,81] and must be obtained using code from Combinations with repetitions. The collection of k zeroes is excluded.
For each collection of k items determine if it translates to 1 using the rules from Iterated digits squaring
For each collection which translates to 1 determine the number of different ways, c say, in which the k items can be uniquely ordered.
Keep a running total of all the values of c obtained
Answer the Project Euler Task #92 question (k=7).
Answer the equivalent question for k=8,11,14.
Optionally answer the question for k=17. These numbers will be larger than the basic integer type for many languages, if it is not easy to use larger numbers it is not necessary for this task.

ALGOL 68

Translation of: Kotlin
BEGIN # Combinations with repetitions: Square digit chain #
      # translated from the Kotlin sample                 #

    PROC ends with one = ( INT n )BOOL:
         IF   n = 0
         THEN FALSE
         ELSE INT  sum     := ABS n;
              WHILE INT nn := sum;
                    sum := 0;
                    WHILE nn > 0 DO
                        INT digit = nn MOD 10;
                        sum +:= digit * digit;
                        nn  OVERAB 10
                    OD;
                    sum /= 1 AND sum /= 89
              DO SKIP OD;
              sum = 1
         FI # ends with one # ;

    BEGIN
        []INT ks = ( 7, 8, 11, 14, 17 );
        FOR k pos FROM LWB ks TO UPB ks DO
           INT k = ks[ k pos ];
           [ 0 : k * 81 + 1 ]LONG INT sums;
           sums[ 0 ] := 1; FOR i FROM 1 TO UPB sums DO sums[ i ] := 0 OD;
           FOR n TO k DO
               FOR i FROM n * 81 BY -1 TO 1 DO
                    FOR j TO 9 WHILE INT s = j * j; s <= i
                    DO sums[ i ] +:= sums[ i - s ]
                    OD
               OD
           OD;
           LONG INT count1 := 0;
           FOR i TO k * 81 DO IF ends with one( i ) THEN count1 +:= sums[ i ] FI OD;
           LONG INT limit = ( LENG 10 ) ^ k - 1;
           print( ( "For k = ", whole( k, -2 ), " in the range 1 to ", whole( limit, 0 ), newline ) );
           print( ( whole( count1, 0 ), " numbers produce 1 and " ) );
           print( ( whole( limit - count1, 0 ), " numbers produce 89", newline, newline ) )
        OD
    END
END
Output:
For k =  7 in the range 1 to 9999999
1418853 numbers produce 1 and 8581146 numbers produce 89

For k =  8 in the range 1 to 99999999
14255666 numbers produce 1 and 85744333 numbers produce 89

For k = 11 in the range 1 to 99999999999
15091199356 numbers produce 1 and 84908800643 numbers produce 89

For k = 14 in the range 1 to 99999999999999
13770853279684 numbers produce 1 and 86229146720315 numbers produce 89

For k = 17 in the range 1 to 99999999999999999
12024696404768024 numbers produce 1 and 87975303595231975 numbers produce 89

D

This example is in need of improvement:

See talk page

// Count how many number chains for Natural Numbers < 10**K end with a value of 1.
//
import std.stdio, std.range;
 
const struct CombRep {
    immutable uint nt, nc;
    private const ulong[] combVal;
 
    this(in uint numType, in uint numChoice) pure nothrow @safe
    in {
        assert(0 < numType && numType + numChoice <= 64,
               "Valid only for nt + nc <= 64 (ulong bit size)");
    } body {
        nt = numType;
        nc = numChoice;
        if (nc == 0)
            return;
        ulong v  = (1UL << (nt - 1)) - 1;
 
        // Init to smallest number that has nt-1 bit set
        // a set bit is metaphored as a _type_ seperator.
        immutable limit = v << nc;
 
        ulong[] localCombVal;
        // Limit is the largest nt-1 bit set number that has nc
        // zero-bit a zero-bit means a _choice_ between _type_
        // seperators.
        while (v <= limit) {
            localCombVal ~= v;
            if (v == 0)
                break;
            // Get next nt-1 bit number.
            immutable t = (v | (v - 1)) + 1;
            v = t | ((((t & -t) / (v & -v)) >> 1) - 1);
        }
        this.combVal = localCombVal;
    }
 
    uint length() @property const pure nothrow @safe {
        return combVal.length;
    }
 
    uint[] opIndex(in uint idx) const pure nothrow @safe {
        return val2set(combVal[idx]);
    }
 
    int opApply(immutable int delegate(in ref uint[]) pure nothrow @safe dg)
    pure nothrow @safe {
        foreach (immutable v; combVal) {
            auto set = val2set(v);
            if (dg(set))
                break;
        }
        return 1;
    }
 
    private uint[] val2set(in ulong v) const pure nothrow @safe {
        // Convert bit pattern to selection set
        immutable uint bitLimit = nt + nc - 1;
        uint typeIdx = 0;
        uint[] set;
        foreach (immutable bitNum; 0 .. bitLimit)
            if (v & (1 << (bitLimit - bitNum - 1)))
                typeIdx++;
            else
                set ~= typeIdx;
        return set;
    }
}
 
// For finite Random Access Range.
auto combRep(R)(R types, in uint numChoice) /*pure*/ nothrow @safe
if (hasLength!R && isRandomAccessRange!R) {
    ElementType!R[][] result;
 
    foreach (const s; CombRep(types.length, numChoice)) {
        ElementType!R[] r;
        foreach (immutable i; s)
            r ~= types[i];
        result ~= r;
    }
 
    return result;
}
 
void main() {
    int K = 17;
    ulong[] F = [1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880, 3628800, 39916800, 479001600, 6227020800, 87178291200, 1307674368000, 20922789888000, 355687428096000];
    int[] N = [0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0];

    ulong z = 0;
    foreach (const e; combRep([0,1,4,9,16,25,36,49,64,81], K)) {
      int s = 0;
      foreach (const g; e) s += g;
      if (N[s] == 0) continue;
      int [int] n;
      foreach (const g; e) n[g] += 1;
	ulong gn = F[K];
      foreach (const g; n.byValue()) gn /= F[g];
	z += gn;
      }
      writefln ("\n(k=%d) In the range 1 to %d\n%d translate to 1 and %d translate to 89\n", K, (cast (ulong) (10))^^K-1,z,(cast (ulong) (10))^^K-1-z);
}
Output:
//(k=7) In the range 1 to 9999999
//1418853 translate to 1 and 8581146 translate to 89

//(k=8) In the range 1 to 99999999
//14255666 translate to 1 and 85744333 translate to 89

//(k=11) In the range 1 to 99999999999
//15091199356 translate to 1 and 84908800643 translate to 89

//(k=14) In the range 1 to 99999999999999
//13770853279684 translate to 1 and 86229146720315 translate to 89

//(k=17) In the range 1 to 99999999999999999
//12024696404768024 translate to 1 and 87975303595231975 translate to 89

EasyLang

Translation of: Julia
func fac n .
   r = 1
   for i = 2 to n
      r *= i
   .
   return r
.
fastfunc ends89 n .
   repeat
      s = 0
      while n > 0
         d = n mod 10
         s += d * d
         n = n div 10
      .
      n = s
      if n = 89
         return 1
      .
      until n = 1
   .
   return 0
.
items[] = [ 0 1 2 3 4 5 6 7 8 9 ]
global comb[] sum .
# 
proc docomb . .
   ncomb = fac len comb[]
   for i = 1 to len comb[]
      h = items[comb[i]]
      cnt += 1
      if i = len comb[] or h <> items[comb[i + 1]]
         ncomb /= fac cnt
         cnt = 0
      .
      v = v * 10 + h
   .
   if v > 0 and ends89 v = 1
      sum += ncomb
   .
.
proc combine pos val . .
   if pos > len comb[]
      docomb
      return
   .
   for i = val to len items[]
      comb[pos] = i
      combine pos + 1 i
   .
.
for h in [ 7 8 11 14 ]
   len comb[] h
   sum = 0
   combine 1 1
   print sum
.
Output:
8581146
85744333
84908800643
86229146720315

FreeBASIC

Translation of: Go
Function endsWithOne(n As Longint) As Boolean
    Dim As Longint digit, sum
    Do
        sum = 0
        Do While n > 0
            digit = n Mod 10 
            sum += (digit * digit)
            n \= 10
        Loop
        If sum = 1 Then Return True
        If sum = 89 Then Return False
        n = sum
    Loop
End Function

Dim As Integer k, n, j
Dim As Longint i, s, count1, limit
Dim ks(4) As Integer = {7, 8, 11, 14, 17}

For k = Lbound(ks) To Ubound(ks)
    Dim sums(ks(k) * 81 + 1) As Longint
    sums(0) = 1
    sums(1) = 0
    For n = 1 To ks(k)
        For i = n * 81 To 1 Step -1
            For j = 1 To 9
                s = j * j
                If s > i Then Exit For
                sums(i) += sums(i - s)
            Next
        Next
    Next
    count1 = 0
    For i = 1 To ks(k) * 81
        If endsWithOne(i) Then count1 += sums(i)
    Next
    limit = 10 ^ ks(k) - 1
    Print "For k ="; ks(k); " in the range 1 to"; limit
    Print count1; " numbers produce 1 and"; limit - count1; " numbers produce 89"
    Print
Next

Sleep
Output:
For k = 7 in the range 1 to 9999999
 1418853 numbers produce 1 and 8581146 numbers produce 89

For k = 8 in the range 1 to 99999999
 14255666 numbers produce 1 and 85744333 numbers produce 89

For k = 11 in the range 1 to 99999999999
 15091199356 numbers produce 1 and 84908800643 numbers produce 89

For k = 14 in the range 1 to 99999999999999
 13770853279684 numbers produce 1 and 86229146720315 numbers produce 89

For k = 17 in the range 1 to 100000000000000000
 12024696404768024 numbers produce 1 and 87975303595231976 numbers produce 89

Go

Translation of: Kotlin
package main

import (
    "fmt"
    "math"
)

func endsWithOne(n int) bool {
    sum := 0
    for {
        for n > 0 {
            digit := n % 10
            sum += digit * digit
            n /= 10
        }
        if sum == 1 {
            return true
        }
        if sum == 89 {
            return false
        }
        n = sum
        sum = 0
    }
}

func main() {
    ks := [...]int{7, 8, 11, 14, 17}
    for _, k := range ks {
        sums := make([]int64, k*81+1)
        sums[0] = 1
        sums[1] = 0
        for n := 1; n <= k; n++ {
            for i := n * 81; i > 0; i-- {
                for j := 1; j < 10; j++ {
                    s := j * j
                    if s > i {
                        break
                    }
                    sums[i] += sums[i-s]
                }
            }
        }
        count1 := int64(0)
        for i := 1; i <= k*81; i++ {
            if endsWithOne(i) {
                count1 += sums[i]
            }
        }
        limit := int64(math.Pow10(k)) - 1
        fmt.Println("For k =", k, "in the range 1 to", limit)
        fmt.Println(count1, "numbers produce 1 and", limit-count1, "numbers produce 89\n")
    }
}
Output:
For k = 7 in the range 1 to 9999999
1418853 numbers produce 1 and 8581146 numbers produce 89

For k = 8 in the range 1 to 99999999
14255666 numbers produce 1 and 85744333 numbers produce 89

For k = 11 in the range 1 to 99999999999
15091199356 numbers produce 1 and 84908800643 numbers produce 89

For k = 14 in the range 1 to 99999999999999
13770853279684 numbers produce 1 and 86229146720315 numbers produce 89

For k = 17 in the range 1 to 99999999999999999
12024696404768024 numbers produce 1 and 87975303595231975 numbers produce 89

jq

Translation of: Wren

Works with jq (*)

Works with gojq, the Go implementation of jq

(*) For the given values of k up to and including 14, the C implementation of jq has sufficient precision, but for k==17, the unbounded precision integer arithmetic of gojq would be required. The output shown below is taken from a gojq run.

# For gojq:
def power($b): . as $in | reduce range(0;$b) as $i (1; . * $in);

def endsWithOne:
  . as $start
  | { n: ., sum: 0 }
  | until(.stop; 
        until(.n <= 0;
             (.n % 10) as $digit
            | .sum +=  $digit * $digit
            | .n = (.n / 10 | floor)
        )
        | if .sum == 1 then .stop = 1
          elif .sum == 89 then .stop = 0
	  else .n = .sum
	  | .sum = 0
	  end )
  | .stop == 1 ;
 
def ks: [7, 8, 11, 14, 17];

ks[] as $k
| {sums: [1,0]}
| reduce range(1; $k+1) as $n (.;
        reduce range( $n*81; 0; -1) as $i (.;
    	    .emit = false
	    | .j = 0
            | until(.emit or (.j == 9);
	        .j+=1
                | (.j * .j) as $s
                | if ($s > $i) then .emit = true
                  else .sums[$i] = .sums[$i] + .sums[$i-$s]
                  end) ))
| .count1 = 0
| reduce range(1; 1 + $k*81) as $i (.; if $i|endsWithOne then .count1 = .count1 + .sums[$i] else . end)
| ((10|power($k)) - 1) as $limit
| "For k = \($k) in the range 1 to \($limit)",
  "\(.count1) numbers produce 1 and \($limit - .count1) numbers produce 89.\n"
Output:
For k = 7 in the range 1 to 9999999
1418853 numbers produce 1 and 8581146 numbers produce 89.

For k = 8 in the range 1 to 99999999
14255666 numbers produce 1 and 85744333 numbers produce 89.

For k = 11 in the range 1 to 99999999999
15091199356 numbers produce 1 and 84908800643 numbers produce 89.

For k = 14 in the range 1 to 99999999999999
13770853279684 numbers produce 1 and 86229146720315 numbers produce 89.

For k = 17 in the range 1 to 99999999999999999
12024696404768024 numbers produce 1 and 87975303595231975 numbers produce 89.

Julia

using Combinatorics

function iterate(m::Integer)
    while m != 1 && m != 89
        s = 0
        while m > 0 # compute sum of squares of digits
            m, d = divrem(m, 10)
            s += d ^ 2
        end
        m = s
    end
    return m
end

function testitersquares(numdigits)
    items =  [0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
    onecount, eightyninecount = 0, 0
    for combo in with_replacement_combinations(items, numdigits)
        if any(x -> x != 0, combo)
            pcount = Int(factorial(length(combo)) / 
                prod(y -> factorial(sum(x -> x == y, combo)), unique(combo)))
            if iterate(sum(combo)) == 89
                eightyninecount += pcount
            else
                onecount += pcount
            end
        end
    end
    println("For k = $numdigits, in the range 1 to $("9" ^ numdigits),\n" *
        "$onecount numbers produce 1 and $eightyninecount numbers produce 89.\n")
end

for i in [7, 8, 11, 14, 17]
    testitersquares(i)
end
Output:
For k = 2, in the range 1 to 99,
19 numbers produce 1 and 80 numbers produce 89.

For k = 7, in the range 1 to 9999999,
1418853 numbers produce 1 and 8581146 numbers produce 89.

For k = 8, in the range 1 to 99999999,
14255666 numbers produce 1 and 85744333 numbers produce 89.

For k = 11, in the range 1 to 99999999999,
15091199356 numbers produce 1 and 84908800643 numbers produce 89.

For k = 14, in the range 1 to 99999999999999,
13770853279684 numbers produce 1 and 86229146720315 numbers produce 89.

For k = 17, in the range 1 to 99999999999999999,
12024696404768024 numbers produce 1 and 87975303595231975 numbers produce 89.

Kotlin

To achieve reasonable performance, the Kotlin entry for the Iterated digits squaring task already used a similar approach to that required by this task for k = 8.

So the following generalizes that code to deal with values of k up to 17 (which requires 64 bit integers) and to count numbers where the squared digits sum sequence eventually ends in 1 rather than 89, albeit the sum of both must of course be 10 ^ k - 1.

// version 1.1.51

fun endsWithOne(n: Int): Boolean {
    var digit: Int
    var sum = 0
    var nn = n
    while (true) {
        while (nn > 0) {
            digit = nn % 10
            sum += digit * digit
            nn /= 10
        }
        if (sum == 1) return true
        if (sum == 89) return false
        nn = sum
        sum  = 0
    }
}

fun main(args: Array<String>) {
    val ks = intArrayOf(7, 8, 11, 14, 17)
    for (k in ks) {
        val sums = LongArray(k * 81 + 1)
        sums[0] = 1
        sums[1] = 0
        var s: Int
        for (n in 1 .. k) {
            for (i in n * 81 downTo 1) {
                for (j in 1 .. 9) {
                    s = j * j
                    if (s > i) break
                    sums[i] += sums[i - s]
                }
            }
        }
        var count1 = 0L
        for (i in 1 .. k * 81) if (endsWithOne(i)) count1 += sums[i]
        val limit = Math.pow(10.0, k.toDouble()).toLong() - 1
        println("For k = $k in the range 1 to $limit")
        println("$count1 numbers produce 1 and ${limit - count1} numbers produce 89\n")
    }
}
Output:
For k = 7 in the range 1 to 9999999
1418853 numbers produce 1 and 8581146 numbers produce 89

For k = 8 in the range 1 to 99999999
14255666 numbers produce 1 and 85744333 numbers produce 89

For k = 11 in the range 1 to 99999999999
15091199356 numbers produce 1 and 84908800643 numbers produce 89

For k = 14 in the range 1 to 99999999999999
13770853279684 numbers produce 1 and 86229146720315 numbers produce 89

For k = 17 in the range 1 to 99999999999999999
12024696404768024 numbers produce 1 and 87975303595231975 numbers produce 89

Nim

Translation of: Kotlin
import math, strformat

func endsWithOne(n: Natural): bool =
  var n = n
  while true:
    var sum = 0
    while n > 0:
      let digit = n mod 10
      sum += digit * digit
      n = n div 10
    if sum == 1: return true
    if sum == 89: return false
    n = sum

const Ks = [7, 8, 11, 14, 17]

for k in Ks:
  var sums = newSeq[int64](k * 81 + 1)  # Initialized to 0s.
  sums[0] = 1
  for n in 1..k:
    for i in countdown(n * 81, 1):
      for j in 1..9:
        let s = j * j
        if s > i: break
        sums[i] += sums[i - s]

  var count1 = 0i64
  for i in 1..k*81:
    if i.endsWithOne(): count1 += sums[i]
  let limit = 10^k - 1
  echo &"For k = {k} in the range 1 to {limit}"
  echo &"{count1} numbers produce 1 and {limit - count1} numbers produce 89\n"
Output:
For k = 7 in the range 1 to 9999999
1418853 numbers produce 1 and 8581146 numbers produce 89

For k = 8 in the range 1 to 99999999
14255666 numbers produce 1 and 85744333 numbers produce 89

For k = 11 in the range 1 to 99999999999
15091199356 numbers produce 1 and 84908800643 numbers produce 89

For k = 14 in the range 1 to 99999999999999
13770853279684 numbers produce 1 and 86229146720315 numbers produce 89

For k = 17 in the range 1 to 99999999999999999
12024696404768024 numbers produce 1 and 87975303595231975 numbers produce 89

Perl

Translation of: Raku
use strict;
use warnings;
use feature 'say';
use Math::AnyNum qw(:overload);

sub comma { reverse ((reverse shift) =~ s/(.{3})/$1,/gr) =~ s/^,//r }

sub endsWithOne {
    my($n) = @_;
    my $digit;
    my $sum = 0;
    my $nn  = $n;
    while () {
        while ($nn > 0) {
            $digit = $nn % 10;
            $sum  += $digit**2;
            $nn    = int $nn / 10;
        }
        return 1 if $sum ==  1;
        return 0 if $sum == 89;
        $nn = $sum;
        $sum = 0;
    }
}

my @ks = <7 8 11 14 17>;

for my $k (@ks) {
    my @sums = <1 0>;
    my $s;
    for my $n (1 .. $k) {
        for my $i (reverse 1 .. $n*81) {
            for my $j (1 .. 9) {
                no warnings 'uninitialized';
                last if ($s = $j**2) > $i;
                $sums[$i] += $sums[$i-$s];
            }
        }
   }
   my $count1 = 0;
   for my $i (1 .. $k*81) { $count1 += $sums[$i] if endsWithOne($i) }
   my $limit = 10**$k - 1;
   say "For k = $k in the range 1 to " . comma $limit;
   say comma($count1) . ' numbers produce 1 and ' . comma($limit-$count1) . " numbers produce 89\n";
}
Output:
For k = 7 in the range 1 to 9,999,999
1,418,853 numbers produce 1 and 8,581,146 numbers produce 89

For k = 8 in the range 1 to 99,999,999
14,255,666 numbers produce 1 and 85,744,333 numbers produce 89

For k = 11 in the range 1 to 99,999,999,999
15,091,199,356 numbers produce 1 and 84,908,800,643 numbers produce 89

For k = 14 in the range 1 to 99,999,999,999,999
13,770,853,279,684 numbers produce 1 and 86,229,146,720,315 numbers produce 89

For k = 17 in the range 1 to 99,999,999,999,999,999
12,024,696,404,768,024 numbers produce 1 and 87,975,303,595,231,975 numbers produce 89

Phix

Translation of: Wren
with javascript_semantics
include mpfr.e

function endsWithOne(integer n)
    integer total = 0
    while true do
        while n>0 do
            integer digit = remainder(n,10)
            total += digit * digit
            n = floor(n/10)
        end while
        if total==1 then return true end if
        if total==89 then return false end if
        n = total
        total = 0
    end while
end function
 
constant ks = {7, 8, 11, 14, 17}
mpz count1 = mpz_init(0),
        si = mpz_init(),
     limit = mpz_init()
for ki=1 to length(ks) do
    integer k = ks[ki]
    sequence sums = repeat(0,k*81+1)
    sums[1] = 1
    sums[2] = 0
    for n=1 to k do
        for i=n*81+1 to 2 by -1 do
            for j=1 to 9 do
                integer s = j * j
                if s>i-1 then exit end if
                sums[i] += sums[i-s]
            end for
        end for
    end for
    mpz_set_si(count1,0)
    for i=1 to k*81 do
        if endsWithOne(i) then
            mpz_set_d(si,sums[i+1])
            mpz_add(count1,count1,si)
        end if
    end for
    mpz_ui_pow_ui(limit, 10, k)
    mpz_sub_si(limit,limit,1)
    mpz_sub(si,limit,count1)
    string l = mpz_get_str(limit,comma_fill:=true),
           c = mpz_get_str(count1,comma_fill:=true),
           s = mpz_get_str(si,comma_fill:=true)
    printf(1,"For k = %d in the range 1 to %s\n",{k,l})
    printf(1,"%s numbers produce 1 and %s numbers produce 89\n\n",{c,s})
end for
Output:
For k = 7 in the range 1 to 9,999,999
1,418,853 numbers produce 1 and 8,581,146 numbers produce 89

For k = 8 in the range 1 to 99,999,999
14,255,666 numbers produce 1 and 85,744,333 numbers produce 89

For k = 11 in the range 1 to 99,999,999,999
15,091,199,356 numbers produce 1 and 84,908,800,643 numbers produce 89

For k = 14 in the range 1 to 99,999,999,999,999
13,770,853,279,684 numbers produce 1 and 86,229,146,720,315 numbers produce 89

For k = 17 in the range 1 to 99,999,999,999,999,999
12,024,696,404,768,024 numbers produce 1 and 87,975,303,595,231,975 numbers produce 89

Also, Iterated_digits_squaring#Phix produces some of the same numbers (just not so high).

Raku

(formerly Perl 6)

Translation of: Kotlin
use v6;

sub endsWithOne($n --> Bool) {
   my $digit;
   my $sum = 0;
   my $nn = $n;
   loop {
      while ($nn > 0) {
         $digit = $nn % 10;
         $sum += $digit²;
         $nn = $nn div 10;
      }
      ($sum == 1) and return True;
      ($sum == 89) and return False;
      $nn = $sum;
      $sum = 0;
   }
}

my @ks = (7, 8, 11, 14, 17);

for @ks -> $k {
   my @sums is default(0) = 1,0;
   my $s;
   for (1 .. $k) -> $n {
      for ($n*81 ... 1) -> $i {
         for (1 .. 9) -> $j {
            $s = $j²;
            if ($s > $i) { last };
            @sums[$i] += @sums[$i-$s];
         }
      }
   }
   my $count1 = 0;
   for (1 .. $k*81) -> $i { if (endsWithOne($i)) {$count1 += @sums[$i]} }
   my $limit = 10**$k - 1;
   say "For k = $k in the range 1 to $limit";
   say "$count1 numbers produce 1 and ",$limit-$count1," numbers produce 89";
}
Output:
For k = 7 in the range 1 to 9999999
1418853 numbers produce 1 and 8581146 numbers produce 89
For k = 8 in the range 1 to 99999999
14255666 numbers produce 1 and 85744333 numbers produce 89
For k = 11 in the range 1 to 99999999999
15091199356 numbers produce 1 and 84908800643 numbers produce 89
For k = 14 in the range 1 to 99999999999999
13770853279684 numbers produce 1 and 86229146720315 numbers produce 89
For k = 17 in the range 1 to 99999999999999999
12024696404768024 numbers produce 1 and 87975303595231975 numbers produce 89

Ruby

# Count how many number chains for Natural Numbers < 10**K end with a value of 1.
#
#  Nigel_Galloway
#  August 26th., 2014.
K = 17
F = Array.new(K+1){|n| n==0?1:(1..n).inject(:*)}   #Some small factorials
g = -> n, gn=[n,0], res=0 { while gn[0]>0
                              gn = gn[0].divmod(10)
                              res += gn[1]**2
                            end
                            return res==89?0:res
                           }
#An array: N[n]==1 means that n translates to 1, 0 means that it does not.
N = (G=Array.new(K*81+1){|n| n==0? 0:(i=g.call(n))==89 ? 0:i}).collect{|n| while n>1 do n = G[n] end; n }
z = 0   #Running count of numbers translating to 1
(0..9).collect{|n| n**2}.repeated_combination(K).each{|n|   #Iterate over unique digit combinations
    next if N[n.inject(:+)] == 0                            #Count only ones
    nn = Hash.new{0}                                        #Determine how many numbers this digit combination corresponds to
    n.each{|n| nn[n] += 1}                                  #and
    z += nn.values.inject(F[K]){|gn,n| gn/F[n]}             #Add to the count of numbers terminating in 1
}
puts "\nk=(#{K}) in the range 1 to #{10**K-1}\n#{z} numbers produce 1 and #{10**K-1-z} numbers produce 89"
Output:
#(k=7) in the range 1 to 9999999
#1418853 numbers produce 1 and 8581146 numbers produce 89

#(k=8) in the range 1 to 99999999
#14255666 numbers produce 1 and 85744333 numbers produce 89

#(k=11) in the range 1 to 99999999999
#15091199356 numbers produce 1 and 84908800643 numbers produce 89

#(k=14) in the range 1 to 99999999999999
#13770853279684 numbers produce 1 and 86229146720315 numbers produce 89

#(k=17) in the range 1 to 99999999999999999
#12024696404768024 numbers produce 1 and 87975303595231975 numbers produce 89

Wren

Translation of: Kotlin
Library: Wren-big

As Wren doesn't have 64 bit integers, it is necessary to use BigInt here to process k = 17.

import "./big" for BigInt

var endsWithOne = Fn.new { |n|
    var sum = 0
    while (true) {
        while (n > 0) {
            var digit = n % 10
            sum = sum + digit * digit
            n = (n/10).floor
        }
        if (sum == 1)  return true
        if (sum == 89) return false
        n = sum
        sum = 0
    }
}

var ks = [7, 8, 11, 14, 17]
for (k in ks) {
    var sums = List.filled(k * 81 + 1, 0)
    sums[0] = 1
    sums[1] = 0
    for (n in 1..k) {
        for (i in n*81..1) {
            for (j in 1..9) {
                var s = j * j
                if (s > i) break
                sums[i] = sums[i] + sums[i-s]
            }
        }
    }
    var count1 = BigInt.zero
    for (i in 1..k*81) if (endsWithOne.call(i)) count1 = count1 + sums[i]
    var limit = BigInt.ten.pow(k) - 1
    System.print("For k = %(k) in the range 1 to %(limit)")
    System.print("%(count1) numbers produce 1 and %(limit - count1) numbers produce 89\n")
}
Output:
For k = 7 in the range 1 to 9999999
1418853 numbers produce 1 and 8581146 numbers produce 89

For k = 8 in the range 1 to 99999999
14255666 numbers produce 1 and 85744333 numbers produce 89

For k = 11 in the range 1 to 99999999999
15091199356 numbers produce 1 and 84908800643 numbers produce 89

For k = 14 in the range 1 to 99999999999999
13770853279684 numbers produce 1 and 86229146720315 numbers produce 89

For k = 17 in the range 1 to 99999999999999999
12024696404768024 numbers produce 1 and 87975303595231975 numbers produce 89

zkl

Translation of: Ruby
fcn countNumberChains(K){
   F:=(K+1).pump(List,fcn(n){ (1).reduce(n,'*,1) });   #Some small factorials
   g:=fcn(n){
      gn,res:=L(n,0),0;
      while(gn[0]>0){ gn=gn[0].divr(10); res+=gn[1].pow(2); }
      if(res==89) 0 else res
   };
   #An array: N[n]==1 means that n translates to 1, 0 means that it does not.
   n,G:=K*81+1,n.pump(List,g);
   N:=n.pump(List,'wrap(n){ n=g(n); while(n>1){ n=G[n] } n });
   z:=([0..9].pump(List,fcn(n){ n*n }):Utils.Helpers.combosKW(K,_)) #combos of (0,1,4,9,16,25,36,49,64,81)
   .reduce('wrap(z,ds){				#Iterate over unique digit combinations
       if(N[ds.sum(0)]==0) return(z);		#Count only ones
       nn:=Dictionary();                        #Determine how many numbers this digit combination corresponds to
       ds.pump(Void,nn.incV);                   #and (eg (0,0,0,0,0,1,9)-->(0:5, 1:1, 9:1)
       z + nn.values.reduce( 			#Add to the count of numbers terminating in 1
	   'wrap(gn,n){ gn/F[n] },F[K]);
   },0);
   println("\nk=(%d) in the range 1 to %,d".fmt(K,(10).pow(K)-1));
   println("%,d numbers produce 1 and %,d numbers produce 89".fmt(z,(10).pow(K)-1-z));
   z
}

combosKW(k,sequence) is lazy, which, in this case, is quite a bit faster than the non-lazy version.

foreach K in (T(7,8,11,14,17)){ countNumberChains(K) }
Output:
k=(7) in the range 1 to 9,999,999
1,418,853 numbers produce 1 and 8,581,146 numbers produce 89

k=(8) in the range 1 to 99,999,999
14,255,666 numbers produce 1 and 85,744,333 numbers produce 89

k=(11) in the range 1 to 99,999,999,999
15,091,199,356 numbers produce 1 and 84,908,800,643 numbers produce 89

k=(14) in the range 1 to 99,999,999,999,999
13,770,853,279,684 numbers produce 1 and 86,229,146,720,315 numbers produce 89

k=(17) in the range 1 to 99,999,999,999,999,999
12,024,696,404,768,024 numbers produce 1 and 87,975,303,595,231,975 numbers produce 89
Cookies help us deliver our services. By using our services, you agree to our use of cookies.