MojoでProject Euler 92

https://projecteuler.net/problem=92

 10^7までとすると、次は 7 \times 9^2までになります。だから、そこまでの1か89のどちらにたどり着くかの表を作っておきます。そうしておいて重複組み合わせの一つ一つがどちらにたどり着くかを調べます。例えば、1111222と1212121は同じところにたどり着くのは明らかでしょう。1111222の並び替えは、
 \displaystyle \frac{7!}{4!3!}
個あります。
こうすると、 10^{18}でも時間内に処理できます。

from math import sqrt
import sys


#################### List ####################

fn initialize_list[T: CollectionElement](N: Int, init: T) -> List[T]:
    var a = List[T](capacity=N)
    for n in range(N):
        a.append(init)
    return a

trait Printable(CollectionElement, Stringable):
    pass

fn print_list[T: Printable](a: List[T]):
    if a.size > 0:
        var s = "[" + str(a[0])
        for i in range(1, a.size):
            s += ", " + str(a[i])
        s += "]"
        print(s)
    else:
        print("[]")

fn duplicate_combinations(a: List[Int], n: Int) -> List[List[Int]]:
    var v = List[List[Int]]()
    if n == 1:
        for e in a:
            var w = List[Int](e[])
            v.append(w)
    elif n % 2 == 1:
        var v1 = duplicate_combinations(a, n-1)
        for e in a:
            for w1 in v1:
                if e[] > w1[][0]:
                    continue
                var w = List[Int](e[])
                w.extend(w1[])
                v.append(w)
    else:
        var m = n // 2
        var v1 = duplicate_combinations(a, m)
        for w1 in v1:
            for w2 in v1:
                if w1[][m-1] > w2[][0]:
                    continue
                var w = w1[]
                w.extend(w2[])
                v.append(w)
    return v

fn count_perm(a: List[Int]) -> Int:
    var L = len(a)
    var n = 1
    var m = 1
    var prev = a[0]
    for i in range(1, L):
        if a[i] != prev:
            for j in range(m):
                n *= L - i + j + 1
            for j in range(m):
                n //= j + 1
            m = 1
            prev = a[i]
        else:
            m += 1
    return n


#################### process ####################

fn sum_squares(owned n: Int) -> Int:
    var s = 0
    while n > 0:
        var d = n % 10
        n //= 10
        s += d**2
    return s

fn sum_squares_list(ds: List[Int]) -> Int:
    var s = 0
    for d in ds:
        s += d[]**2
    return s

fn make_destination_table(N: Int) -> List[Int]:
    var a = initialize_list(N+1, 0)
    for n in range(1, N+1):
        if a[n] != 0:
            continue
        var stack = List[Int](n)
        while True:
            n = sum_squares(n)
            if n == 1 or n == 89:
                break
            elif a[n] != 0:
                n = a[n]
                break
            stack.append(n)
        for e in stack:
            a[e[]] = n
    return a

fn f(E: Int) -> Int:
    var N = 10**E
    var table = make_destination_table(E*9**2)
    var ds = List[Int](0, 1, 2, 3, 4, 5, 6, 7, 8, 9)
    var counter = 0
    for ds1 in duplicate_combinations(ds, E):
        var s = sum_squares_list(ds1[])
        if table[s] == 89:
            counter += count_perm(ds1[])
    return counter

fn main() raises:
    var args = sys.argv()
    var E = atol(args[1])
    print(f(E))