2
自己紹介・記録解説
文献あり

消しゴムさんの記事『REDUCE関数(Excel)の使い方覚書』の練習問題答案

84
0
$$$$

はじめに

この記事は 消しゴムさんの記事『REDUCE関数(Excel)の使い方覚書』 の練習問題を解いて、自分なりの解説を付けたものです。(間違ってるかもしれません。)
まずはこちらの記事をご覧になってください。分かりやすく、また面白いです。

練習問題も良い難易度で解きごたえがあるので、是非皆様も一度解いてください。Excelがない? Web版 を使いましょう。(使い勝手は良くないけど無料です。)

レギュレーション

今回は各問題に若干の縛りがあります。

  • 各問題について、使えるLAMBDAヘルパー関数はREDUCE関数であり、一度しか使えない。
  • (問題2,4,5,6,8はHSTACK/VSTACK使用禁止。正確に言うと、縛りありの方でしか解いてないものが多く、縛りなしを別途で作るのが面倒くさかった)
    追記:これ例題に対してらしくて練習問題には課されていなかったらしいです。勘違いしてました...すみません。勝手に縛っているんだなと思って記事を見てください。
  • 各問題の指示、解法数に従う。複数解法は独自解釈する。

なお、色々な定理が出てきますが証明はしません。
では、始めます。

第1問

ユークリッドの互除法

正の整数$m,n$が与えられるので、$m,n$の最大公約数と最小公倍数をユークリッドの互除法を使って計算する数式を組んでください。

REDUCE関数で大事なことは、十分性のある上限を予め決定しておくことです。この問題ではラメの定理を使用します。

ラメの定理

$a>b$を満たす2つの正の整数に対して、$b$の十進展開の桁数を$M$、ユークリッドの互除法の最大反復回数を$N$とすると、次の関係が成り立つ。
$$\begin{align*} N \leq 5M\end{align*}$$

コードはこんな感じです。
[コピペ用]

      =LAMBDA(m,n,LET(gcd,INDEX(REDUCE(SORT(HSTACK(m,n),,-1),SEQUENCE(5*INT(LOG10(MIN(m,n))+1)),LAMBDA(array,k,IF(INDEX(array,1,2)=0,array,HSTACK(INDEX(array,1,2),MOD(INDEX(array,1,1),INDEX(array,1,2)))))),1,1),HSTACK(VSTACK("gcd : ","lcm : "),VSTACK(gcd,m*n/gcd)))
)
    

[見やすい用]

      LAMBDA(m, n,
    LET(
        comment_1, "ユークリッドの互除法による最大公約数の算出",
        init_val, SORT(HSTACK(m, n), , -1),
        comment_2, "ラメの定理より、計算回数は桁数から十分な回数(5*桁数)を確保",
        iter_num, 5 * INT(LOG10(MIN(m, n)) + 1),
        gcd, INDEX(
            REDUCE(init_val, SEQUENCE(iter_num), LAMBDA(array, k, 
                IF(INDEX(array, 1, 2) = 0, 
                    array, 
                    HSTACK(INDEX(array, 1, 2), MOD(INDEX(array, 1, 1), INDEX(array, 1, 2)))
                )
            )), 
            1, 1
        ),
        HSTACK(VSTACK("gcd : ", "lcm : "), VSTACK(gcd, m * n / gcd))
    )
)
    

まず最初に、$m,n$を降順で並べて、大きい方(INDEX(array,1,1))を割られる数、小さい方(INDEX(array,1,2))を割る数にします。またREDUCEで構造が壊れないように、つまり位置関係と役割がブレないようにするのが組み立てるコツです。

REDUCE内でINDEX(array,1,2)が何回も出てくるので、それを別の変数で置く方が何となく好きです。

      LAMBDA(m, n,
    LET(
        iter_num, 5 * INT(LOG10(MIN(m, n)) + 1),
        gcd, INDEX(
            REDUCE(SORT(HSTACK(m, n), , -1), SEQUENCE(iter_num), LAMBDA(array, k, 
                LET(
                    b, INDEX(array, 1, 2),
                    IF(b = 0, array, HSTACK(b, MOD(INDEX(array, 1, 1), b)))
                )
            )), 
            1, 1
        ),
        HSTACK(VSTACK("gcd : ", "lcm : "), VSTACK(gcd, m * n / gcd))
    )
)
    

第2問

オイラーのトーシェント関数

正の整数$n$が与えられるので、$n$以下の正の整数であって、$n$との最大公約数が$1$であるものの個数を計算する数式を組んでください。
(VSTACK/HSTACK禁止)

[コピペ用]

      =LAMBDA(n,REDUCE(0,SEQUENCE(n-1),LAMBDA(temp,k,IF(GCD(k,n)=1,1,0)+temp))
)
    

[見やすい用]

      LAMBDA(n,
    LET(
        comment_1, "オイラーのφ関数: nと互いに素なkの数をカウント",
        REDUCE(0, SEQUENCE(n - 1), LAMBDA(sum_count, k, 
            IF(GCD(k, n) = 1, 1, 0) + sum_count
        ))
    )
)
    

これは分かりやすいですね。互いに素なら+1、そうじゃないなら+0です。上限も明快です。

第3問

素数計数関数

正の整数$n$が与えられるので、$n$以下の素数の個数を計算する数式を組んでください。
(VSTACK/HSTACK使用禁止)

これは愚直に試し割りをすることでVSTACK/HSTACKの使用を回避できます。
[コピペ用]

      =LAMBDA(n,IF(n<=1,0,IF(n<=3,n-1,LET(seq,SEQUENCE(SQRT(n)-1,,2),REDUCE(1,SEQUENCE(n-1,,2),LAMBDA(temp,k,temp+IF(AND(MOD(k,TAKE(seq,SQRT(k)))),1,0))))))
)
    

[見やすい用]

      LAMBDA(n,
    IF(n <= 1, 0, 
    IF(n <= 3, n - 1, 
    LET(
        comment_1, "平方根までの整数で割れるかチェックする試し割り法",
        limit, SQRT(n) - 1,
        check_seq, SEQUENCE(limit, , 2),
        REDUCE(1, SEQUENCE(n - 1, , 2), LAMBDA(prime_count, k, 
            prime_count + IF(AND(MOD(k, TAKE(check_seq, SQRT(k) - 1))), 1, 0)
        ))
    )))
)
    

これは親記事にも書かれていた分岐法の応用です。Excelの論理関数において、数値では0以外なら真、0なら偽を返します。それらをANDでくるむことで、MODがすべて0ではないなら真(+1)、そうでないなら偽(+0)としてカウンター代わりに機能させることが出来るわけです。

この縛り下でも速度が気になる方は、こちらの$6m\pm 1$で試し割る方法も良いでしょう。

      =LAMBDA(n,
    IF(n<=1,0,
    IF(n<=3,n-1,
    IF(n<=10,MIN(INT((n-1)/2)+1,4),
    REDUCE(4,SEQUENCE(n-10,,11),
        LAMBDA(temp,k,
            temp+IF(
                AND(
                    MOD(k,SEQUENCE(SQRT(k)/3)*6+{1,-1}),
                    MOD(k,2),
                    MOD(k,3)
                ),
                1,0
            )
        )
    ))))
)

    

第4問

エラトステネスの篩

正の整数$n$が与えられるので、エラトステネスの篩を使って$n$以下の素数を列挙する数式を組んでください。ただし、FILTER関数を使った場合は,使わない方法を$2$通り考えてみましょう。
(VSTACK/HSTACK使用禁止)

まずはREDUCEの反復回数の上限の議論をしましょう。
一般に、$n(\geq 1)$以下の素数の個数$\pi(n)$について、以下が成り立ちます。

素数計数関数の上限評価

$$\begin{align*}\pi(n)< \dfrac{n}{\ln (n)}\left(1+\dfrac{1.2762}{\ln(n)}\right)\\ \end{align*}$$

これを使って、$\sqrt{n}$以下の素数の個数の評価が得られるので、これを反復回数とします。

FILTER関数を使う場合はこうなります。
[コピペ用]

      =LAMBDA(n,REDUCE(SEQUENCE(n-1,,2),SEQUENCE(2*SQRT(n)/LN(n)*(1+2.5524/LN(n))),LAMBDA(array,k,LET(div,INDEX(array,k,1),IF(div>SQRT(n),array,FILTER(array,MOD(array,div)+(array<=div))))))
)
    

[見やすい用]

      =LAMBDA(n,
    REDUCE(SEQUENCE(n - 1, , 2), SEQUENCE(2 * SQRT(n) / LN(n) * (1 + 2.5524 / LN(n))), LAMBDA(array, k, 
        LET(
            comment_1, "現在の素数を取得し、√nを超えていなければその倍数を削除",
            div, INDEX(array, k, 1),
            IF(div > SQRT(n), 
                array, 
                FILTER(array, MOD(array, div) + (array <= div))
            )
        )
    ))
)
    

FILTER関数と同様の働きをする関数がもう1種あります。TOCOL/TOROW関数です。TOCOL(array,2)の状態ではエラーになったセルを消しながら1列ないし1行にするというモードになります。
つまり、真理値が0のときにそれを0で割ったり0^0の形を作ってあげれば、そのセルはエラーになるのでフィルタリングできるという訳です。

      =LAMBDA(n,
    REDUCE(
        SEQUENCE(n - 1, , 2),
        SEQUENCE(2 * SQRT(n) / LN(n) * (1 + 2.5524 / LN(n))),
        LAMBDA(array, k, 
            LET(
                div, INDEX(array, k, 1),
                IF(div > SQRT(n), 
                    array, 
                    TOCOL(array * (MOD(array, div) + (array <= div))^0, 2)
                )
            )
        )
    )
)
    

このTOCOL関数によるフィルタリングは、FILTER関数とは違って、arrayは二次元配列でも処理できるという点で優れています。この後の問題でも活躍します。

さて、FILTER関数を使わない手法をもう一つ考えなければなりません。といっても、私は似たような変な縛りをつけた問題を考えたことがあります。

Excelの関数のみで$n$以下の素数リストを生成する関数を作成してください。ただし、REDUCE関数を使う際には、lambdaの項に四則演算ないしMOD関数を使ってはいけません。

この時の私の解答にはFILTER関数を使ってなかったので、これをそのまま解答とさせていただきます。

      =LAMBDA(n,
    IF(n=2,2,
      LET(
        m, -1,
        A, REDUCE(HSTACK(2, SEQUENCE(n, 1, 1)), SEQUENCE(IF(n<=12,n/3,SQRT(n) / LN(SQRT(n)) * (1 + 1.2762 / LN(SQRT(n))))), 
            LAMBDA(p, q,
            LET(
                comment_1, "左列に素数リスト、右列に篩を保持して回す",
                a, TOCOL(CHOOSECOLS(p, 1), 3),
                b, CHOOSECOLS(p, 2),
                c, INDEX(a, q, 1),
                comment_2, "c列で折り返し、倍数である右端1列を削って平坦化",
                d, TOCOL(EXPAND(DROP(WRAPROWS(b, c), , m), , c)),
                HSTACK(VSTACK(a, SMALL(TOCOL(d, 3), 2)), d)
            )
        )),
        comment_3, "確定した素数リストと、最後に残った篩(1を除去)を結合",
        VSTACK(DROP(TOCOL(CHOOSECOLS(A, 1), 3), m), TOCOL(DROP(CHOOSECOLS(A, 2), 1), 3))
    )
  )
)
    

特に言う事はありません。効率性重視なら絶対に組まないやり方です。
...そういえばこの問題VSTACK/HSTACK使用禁止でしたね。
なら四則演算は入りますがこのような形で解答しましょう。

      =LAMBDA(n,
    IF(n<=2,2,
       LET(
        result, REDUCE(SEQUENCE(n), SEQUENCE(IF(n<=12,n/3,2 * SQRT(n) / LN(n) * (1 + 2.5524 / LN(n)))), LAMBDA(array,k,
            LET(
                comment_1, "エラーを除いたリストから、次に判定する素数を取得",
                div, INDEX(TOCOL(array, 2), k + 1, 1),
                comment_2, "配列をdiv列で折り返し、各行のdiv列目(倍数)をエラーにする",
                comment_3, "(ただし、最初の1回目(素数自身)は除外する)",
                mask, IF(SEQUENCE(, div) = div, EXPAND(1, ROUNDUP(n / div, 0), , 0), EXPAND(1, ROUNDUP(n / div, 0), , 1)),
                TOCOL(WRAPROWS(array, div) / mask)
            )
        )),
        comment_4, "最後に残ったエラー(消された数)と1を除去して素数リストを完成",
        DROP(TOCOL(result, 2), 1)
    )
  )
)
    

第5問

素因数分解

$2$以上の整数$n$が与えられるので、$n$を素因数分解する数式を組んでください。
(VSTACK/HSTACK使用禁止)

普通この問題はVSTACK/HSTACKを使うものですがそれを禁じられるとは。
となるとTEXTJOIN/TEXTSPLIT/TEXTAFTER/TEXTBEFOREなどの文字操作関数を駆使してこなすしかありません。

[コピペ用]

      =LAMBDA(n,TEXTSPLIT(TEXTAFTER(REDUCE("2,"&n,SEQUENCE(SQRT(n),,2),LAMBDA(text,k,LET(div,TEXTBEFORE(text,",",1)+0,m,TEXTAFTER(text,",",-1)+0,new_m,IF(MOD(m,div)=0,m/div,m),IF(new_m=m,TEXTJOIN(",",TRUE,div+1,TEXTAFTER(text,",",1)),IF(div=m,text,TEXTJOIN(",",TRUE,TEXTBEFORE(text,",",-1),div,new_m)))))),",",1),,",")+0
)
    

[見やすい用]

      =LAMBDA(n,
    TEXTSPLIT(TEXTAFTER(
        REDUCE("2," & n, SEQUENCE(SQRT(n), , 2), 
            LAMBDA(text, k, 
            LET(
                comment_1, "文字列から除数(div)と残り(m)を抽出",
                div, TEXTBEFORE(text, ",", 1) + 0,
                m, TEXTAFTER(text, ",", -1) + 0,
                new_m, IF(MOD(m, div) = 0, m / div, m),
                
                comment_2, "割り切れない場合は除数を+1し、割り切れる場合は因数を記録してmを更新",
                IF(new_m = m, 
                    TEXTJOIN(",", TRUE, div + 1, TEXTAFTER(text, ",", 1)), 
                    IF(div = m, 
                        text, 
                        TEXTJOIN(",", TRUE, TEXTBEFORE(text, ",", -1), div, new_m)
                    )
                )
            )
        )), ",", 1), , ",") + 0
)
    

文字の先頭に除数、中間に素因数、最後に現在の$n$の状態を","でくっつけています。(TEXTBEFORE/TEXTAFTER関数で切り出しやすくするため)

第6問

算術的微分 (素微分)

写像$D : \mathbb{Z}_{\geq 0}\to \mathbb{Z}_{\geq 0}$は以下をみたすとします。
$$\begin{align*} \begin{dcases}D(0) = D(1) =0,\\D(p) = 1 \: (p\text{は素数}),\\ D(ab) = aD(b) + bD(a)\end{dcases}\\ \end{align*}$$
正の整数$n$が与えられるので、$D(n)$を計算する数式を$2$通りの方法で組んでください。
(VSTACK/HSTACK使用禁止)

1つ目の解法は第5問を解いた後ならパッと思いつきます。

$n = \prod p_i^{e_i}$に対して、
$$\begin{align*} D(n) = n\sum_{i}\dfrac{e_i}{p_i}\end{align*}$$

よって、素因数分解した後の列の逆数和に$n$を掛ければ終わりですね。
[コピペ用]

      =LAMBDA(n,IF(n<=1,0,SUM(n/TEXTSPLIT(TEXTAFTER(REDUCE("2,"&n,SEQUENCE(SQRT(n),,2),LAMBDA(text,k,LET(div,TEXTBEFORE(text,",",1)+0,m,TEXTAFTER(text,",",-1)+0,new_m,IF(MOD(m,div)=0,m/div,m),IF(new_m=m,TEXTJOIN(",",TRUE,div+1,TEXTAFTER(text,",",1)),IF(div=m,text,TEXTJOIN(",",TRUE,TEXTBEFORE(text,",",-1),div,new_m)))))),",",1),,",")+0))
)
    

[見やすい用]

      =LAMBDA(n,
    IF(n <= 1, 0, 
        SUM(n / TEXTSPLIT(TEXTAFTER(
            REDUCE("2," & n, SEQUENCE(SQRT(n), , 2),
              LAMBDA(text, k, 
                LET(
                    div, TEXTBEFORE(text, ",", 1) + 0,
                    m, TEXTAFTER(text, ",", -1) + 0,
                    new_m, IF(MOD(m, div) = 0, m / div, m),
                    IF(new_m = m, 
                        TEXTJOIN(",", TRUE, div + 1, TEXTAFTER(text, ",", 1)), 
                        IF(div = m, text, TEXTJOIN(",", TRUE, TEXTBEFORE(text, ",", -1), div, new_m))
                    )
                )
            )), ",", 1), , ",") + 0
        )
    )
)
    

もう一つの方法は$D(n) = pD(n/p) + n/p$の様に処理する方法ですね。
[コピペ用]

      =LAMBDA(n,IF(n<=1,0,REDUCE("2.0."&n,SEQUENCE(SQRT(n),,2),LAMBDA(text,k,IF(ISNUMBER(text+0),text,LET(array,TEXTSPLIT(text,"."),div,INDEX(array,1,1),m,INDEX(array,1,3),new_m,IF(MOD(m,div)=0,m/div,m),flag,k=INT(SQRT(n))+1,IF((new_m=m)*NOT(flag),TEXTJOIN(".",TRUE,div+1,TEXTAFTER(text,".",1)),IF((m=div)+flag,INDEX(array,1,2)+n/m,TEXTJOIN(".",TRUE,div,INDEX(array,1,2)+new_m*n/m,new_m))))))))
)
    

[見やすい用]

      =LAMBDA(n,
    IF(n <= 1, 0, 
        REDUCE("2.0." & n, SEQUENCE(SQRT(n), , 2), LAMBDA(text, k, 
            IF(ISNUMBER(text + 0), text, 
                LET(
                    array, TEXTSPLIT(text, "."),
                    div, INDEX(array, 1, 1),
                    m, INDEX(array, 1, 3),
                    new_m, IF(MOD(m, div) = 0, m / div, m),
                    flag, k = INT(SQRT(n)) + 1,
                    
                    comment_1, "ライプニッツ則に従い、因数が見つかるたびに",
                    comment_2," n/p*(これまで見つかった因数の積) を現在の和に加算",
                    IF((new_m = m) * NOT(flag), 
                        TEXTJOIN(".", TRUE, div + 1, TEXTAFTER(text, ".", 1)), 
                        IF((m = div) + flag, 
                            INDEX(array, 1, 2) + n / m, 
                            TEXTJOIN(".", TRUE, div, INDEX(array, 1, 2) + new_m * n / m, new_m)
                        )
                    )
                )
            )
        ))
    )
)
    

どちらの方法も良いですが、嵩張らない2個目の方法の方が若干優秀なのですかね。

第7問

$\textbf{A}^{T}\textbf{A}$とその成分和

各成分が実数の$1\times n$配列$A$が与えられるので、$A$から重複をゆるしてとった$2$つの実数の積$n^2$個を列挙し、その総和を計算する数式を組んでください。ただし、それぞれに対してREDUCE関数を使う方法とそうでない方法の$2$通りを考えてください。

まずはREDUCE版から
[コピペ用]

      =LAMBDA(A,IF(COUNT(A)=1,A^2,REDUCE(A*INDEX(A,1,1),DROP(A,,1),LAMBDA(array,Ak,VSTACK(array,A*Ak))))
)
    

[見やすい用]

      =LAMBDA(A,
    IF(COUNT(A) = 1, A^2,
    LET(
        comment_1, "ベクトルAの各要素Akについて、A全体を掛けてスタックする",
        REDUCE(A * INDEX(A, 1, 1), DROP(A, , 1), LAMBDA(array, Ak, 
            VSTACK(array, A * Ak)
        ))
    ))
)
    

これ全体にSUM付ければ総和になるので総和の方は省略。
REDUCEなし版は

      =LAMBDA(A,A*TOCOL(A))
    

これで終わりです。これにSUM付ければ総和にもなります。

第8問

整数環$\mathbb{Z}$におけるイデアルの根基

整数環$\mathbb{Z}$は単項イデアル整域であることが知られています。正の整数$n$が与えられるので、$n$によって生成される$\mathbb{Z}$のイデアル$I:= \langle n\rangle$に対して$\langle m \rangle = \sqrt{I}$をみたす正の整数$m$を計算する数式を組んでください。
余裕のある人はもう$1$通りの方法を考えてみましょう。
(VSTACK/HSTACK使用禁止)

専門用語抜きにして端的に話すと、素因数をそのままでsquare-freeにしろという事です。
$\displaystyle n = \prod p_i^{e_i}$ならば、$\displaystyle m = \prod p_i$です。
これも素因数分解さえ出来ればUNIQUEとPRODUCTで解決しますね。
[コピペ用]

      =LAMBDA(n,PRODUCT(UNIQUE(TEXTSPLIT(TEXTAFTER(REDUCE("2,"&n,SEQUENCE(SQRT(n),,2),LAMBDA(text,k,LET(div,TEXTBEFORE(text,",",1)+0,m,TEXTAFTER(text,",",-1)+0,new_m,IF(MOD(m,div)=0,m/div,m),IF(new_m=m,TEXTJOIN(",",TRUE,div+1,TEXTAFTER(text,",",1)),IF(div=m,text,TEXTJOIN(",",TRUE,TEXTBEFORE(text,",",-1),div,new_m)))))),",",1),,",")+0))
)
    

[見やすい用]

      =LAMBDA(n,
    LET(
        comment_1, "文字列を使ってnを素因数分解し、因数のリストを作成",
        factors_text, TEXTAFTER(
            REDUCE("2," & n, SEQUENCE(SQRT(n), , 2), LAMBDA(text, k, 
                LET(
                    div, TEXTBEFORE(text, ",", 1) + 0,
                    m, TEXTAFTER(text, ",", -1) + 0,
                    new_m, IF(MOD(m, div) = 0, m / div, m),
                    IF(new_m = m, 
                        TEXTJOIN(",", TRUE, div + 1, TEXTAFTER(text, ",", 1)), 
                        IF(div = m, 
                            text, 
                            TEXTJOIN(",", TRUE, TEXTBEFORE(text, ",", -1), div, new_m)
                        )
                    )
                )
            )), 
            ",", 1
        ),
        comment_2, "リストから重複を除去して総乗をとる",
        PRODUCT(UNIQUE(TEXTSPLIT(factors_text, , ",") + 0))
    )
)
    

もう一つの方法としては最大の平方因子$s^2$を検出して、$s$で割り続ける方法です。
[コピペ用]

      =LAMBDA(n,REDUCE(n,SEQUENCE(LOG(n,2)),LAMBDA(m,k,m/MAX(IF(MOD(m,SEQUENCE(SQRT(m))^2)=0,SEQUENCE(SQRT(m)),0))))
)
    

[見やすい用]

      =LAMBDA(n,
    REDUCE(n, SEQUENCE(LOG(n, 2)), LAMBDA(m, k,
        LET(
            comment_1, "現在のmに含まれる最大の平方因子(s^2)を見つけて割る",
            s, MAX(IF(MOD(m, SEQUENCE(SQRT(m))^2) = 0, SEQUENCE(SQRT(m)), 0)),
            m / s
        )
    ))
)
    

第9問

メビウス変換

Excel数式によって実装された数論的関数$f$と正の整数$n$が与えられるので、$\displaystyle f(m) = \sum_{d|m, 0< d}g(d) \:\:(\forall m)$をみたすような関数$g$に対して$g(n)$を計算する数式を組んでください。

一応これにはメビウスの反転公式という公式があります。

メビウスの反転公式

数論的関数$f$に対して、$\displaystyle f(n) = \sum_{d|n, 0< d}g(d) \:\:(\forall n)$をみたすような関数$g$を考える。この関係は次と同値である。
$$ g(n) = \sum_{d|n,0< d}\mu(d)f(n/d) \:\: (\forall n)$$

メビウスの反転公式を使う解法を想定しているものとは思いますが、包除原理と約数DPで処理しました。

[コピペ用]

      =LAMBDA(f,n,TAKE(LET(seq,SEQUENCE(SQRT(n)),r,TOCOL(seq/(MOD(n,seq)=0),2),div,SORT(UNIQUE(TOCOL(HSTACK(r,n/r)))),REDUCE(f(1),SEQUENCE(COUNT(div)-1),LAMBDA(array,k,LET(d,INDEX(div,k+1),VSTACK(array,f(d)-SUM(TOCOL(array/(MOD(d,TAKE(div,k))=0),2))))))),-1)
)
    

[見やすい用]

      =LAMBDA(f, n,
    TAKE(LET(
        comment_1, "nの約数(div)をすべて列挙してソート",
        seq, SEQUENCE(SQRT(n)), r, TOCOL(seq/(MOD(n,seq)=0),2),
        div, SORT(UNIQUE(TOCOL(HSTACK(r,n/r)))),
        
        comment_2, "約数dについて g(d) = f(d) - Σ g(k) [k|d, k<d] を動的計画法(DP)で計算",
        REDUCE(f(1), SEQUENCE(COUNT(div)-1), LAMBDA(array, k,
            LET(
                d, INDEX(div, k+1),
                current_val, f(d) - SUM(TOCOL(array/(MOD(d, TAKE(div, k)) = 0), 2)),
                VSTACK(array, current_val)
            )
        ))
    ), -1)
)
    

地味に約数の列挙をREDUCE関数なしで処理しています。これが出来ないと詰みます...。

第10問

整数の分割

整数の分割を考えます。4の分割は
$$\begin{align*} 4 &= 1+1+1+1\\&=1+1+2\\&=2+2\\&= 1+3\\&=4\end{align*}$$
$5$通りあります.正の整数$n$が与えられるので,$n$の分割の仕方を列挙する数式を組んでください。また、FILTER関数を使った場合は使わない方法を考えてください。余裕のある人はもう$1$通りの方法を考えてみましょう。

直ぐに思いつくのは全列挙して和が$n$になったものを足していく戦法ですね。$n$が大きくなると処理が重くなるので注意してください。

[コピペ用]

      =LAMBDA(n,IF(n=1,1,VSTACK(UNIQUE(DROP(REDUCE("",SEQUENCE(n^n),LAMBDA(array,k,LET(a,MOD(QUOTIENT(k,n^SEQUENCE(,LOG(k,n)+1,0)),n)+1,IF(SUM(a)=n,VSTACK(array,TEXTJOIN(",",,SORT(a,,-1,TRUE))),array)))),1)),REPT("1,",n-1)&"1"))
)
    

[見やすい用]

      =LAMBDA(n,
    IF(n = 1, 1,
    LET(
        comment_1, "n^nの巨大な空間を探索して和がnになる組み合わせを抽出(非常に重い)",
        combinations, UNIQUE(DROP(REDUCE("", SEQUENCE(n^n), LAMBDA(array, k,
            LET(
                a, MOD(QUOTIENT(k, n^SEQUENCE(, LOG(k, n) + 1, 0)), n) + 1,
                IF(SUM(a) = n, VSTACK(array, TEXTJOIN(",", , SORT(a, , -1, TRUE))), array)
            )
        )), 1)),
        VSTACK(combinations, REPT("1,", n-1) & "1")
    ))
)
    

FILTER関数は使っていないのでクリアですね。
この方法、重い上に順番が結構バラバラです。どうせなら順序よく出力させたい...ということで辞書式順序で次々に生成する方法があります。まずはコードを見せましょう。

[コピペ用]

      =LAMBDA(n,REDUCE(n&"",SEQUENCE(IF(n<=2,n,EXP(PI()*SQRT(2*n/3))/(4*n*SQRT(3)))),LAMBDA(array,k,LET(text,INDEX(TAKE(array,-1),1,1),split,TEXTSPLIT(text,",")+0,t,SUM((split=1)+0),m,COUNTA(split),w,m-t,d,INDEX(split,1,w)-1,IF(w=0,array,LET(q,QUOTIENT(t+1,d),r,MOD(t+1,d),add,IF(q=0,","&r,IF(r=0,REPT(","&d,q),REPT(","&d,q)&","&r)),IF(w=1,VSTACK(array,d&add),VSTACK(array,TEXTBEFORE(text,",",w-1)&","&d&add)))))))
)
    

[見やすい用]

      =LAMBDA(n,
    REDUCE(n & "", SEQUENCE(IF(n <= 2, n, EXP(PI()*SQRT(2*n/3))/(4*n*SQRT(3)))), LAMBDA(array, k,
        LET(
            comment_1, "現在の最新の分割(text)を取得",
            text, INDEX(TAKE(array, -1), 1, 1),
            split, TEXTSPLIT(text, ",") + 0,
            comment_2, "末尾の1の個数をカウント",
            t, SUM((split = 1) + 0), m, COUNTA(split), w, m - t,
            
            comment_3, "分割の最後の非1要素を減らし、残りを貪欲に再分配",
            d, INDEX(split, 1, w) - 1,
            IF(w = 0, array,
                LET(
                    q, QUOTIENT(t + 1, d), r, MOD(t + 1, d),
                    add, IF(q = 0, "," & r, IF(r = 0, REPT("," & d, q), REPT("," & d, q) & "," & r)),
                    IF(w = 1, VSTACK(array, d & add), VSTACK(array, TEXTBEFORE(text, ",", w - 1) & "," & d & add))
                )
            )
        )
    ))
)
    

まず気にしなければいけないのがREDUCEの反復回数です。こちらにはハーディー・ラマヌジャンの漸近公式を適用しました。

ハーディー・ラマヌジャンの漸近公式

整数の分割の個数を$p(n)$とする。$n\to \infty$において、
$$\begin{align*} p(n)\sim \dfrac{1}{4n\sqrt{3}}\exp \left(\pi\sqrt{\dfrac{2n}{3}}\right)\end{align*}$$

この漸近展開は次項が負のため反復回数として十分性をもつ公式です。
肝心のREDUCEの中身について見ていきましょう。
まず配列の一番下を取得します。例として$5,3,1,1$とします。
末尾の1の数$t$を数えます。今の例なら$t=2$ですね。
その後、最後の非1の数を取得してその数を1つ減らします。今の例なら2個目の$3$$2$に変えます。
現在$5,2$の状態で、分配可能な残り数$w$$t+1=3$です。これを貪欲に再分配します。つまり、現在の状態の最後尾を数以下を並べられるだけ並べて、余ったらあまりを最後に付けます。今回なら$5,2,2,1$です。これを繰り返せば全列挙できます。
因みに今回はREDUCE関数メインなので使えませんが、REDUCEの代わりにSCAN関数が使えて、そちらの方が大きい$n$では高速化します。

第11問

既約じゃない方

素数$p$と正の整数$n$が与えられるので、有限体$\mathbb{F}_p$上の可約多項式であって、次数が$n$以下であるものの個数を計算する数式を$2$通りの方法で組んでください。

多分モニック多項式という制限もないので$(p-1)$を掛けることを忘れないようにしましょう。
ということでこれまた有名な公式を引っ張り出して考えます。

ネックレス多項式

素数$p$個の元からなる有限体$\mathbb{F}_p$上のモニックな$n$次既約多項式の個数$I_p(n)$に対して、以下が成り立つ。
$$ p^n = \sum_{d|n, 0< d}d I_p(d)$$

これは第9問において、$f(n) = p^n$としたとき、$g(n)=nI_p(n)$という事になるので、第9問の数式がそのまま利用できます。
一般の可約多項式の個数は$(p-1)(p^n - I_p(n))$と表されます。
(解き方2個って書いてありましたが有意に異なる2個目の解法が思いつかなくて、約数DP[$I_p(d)$を順次生成]とメビウス反転[$\mu(d)p^{n/d}$を順次生成]の両方の処理法という解釈とします。)
まず約数DP(第9問の流用)から
[コピペ用]

      =LAMBDA(p,n,(p-1)*(p^n-TAKE(LET(seq,SEQUENCE(SQRT(n)),r,TOCOL(seq/(MOD(n,seq)=0),2),div,SORT(UNIQUE(TOCOL(HSTACK(r,n/r)))),REDUCE(p,SEQUENCE(COUNT(div)-1),LAMBDA(array,k,LET(d,INDEX(div,k+1),VSTACK(array,p^d-SUM(TOCOL(array/(MOD(d,TAKE(div,k))=0),2))))))),-1)/n)
)
    

[見やすい用]

      =LAMBDA(p, n,
    (p - 1) * (p^n - TAKE(
        LET(
            comment_1, "nの約数divを列挙",
            seq, SEQUENCE(SQRT(n)),
              r, TOCOL(seq / (MOD(n, seq) = 0), 2),
            div, SORT(UNIQUE(TOCOL(HSTACK(r, n / r)))),
            
            comment_2, "モニック既約多項式の数 I_p(n) を求めるための反転計算",
            REDUCE(p, SEQUENCE(COUNT(div) - 1), LAMBDA(array, k, 
                VSTACK(array, p^INDEX(div, k + 1) - SUM(TOCOL(array / (MOD(INDEX(div, k + 1), TAKE(div, k)) = 0), 2)))
            ))
        ), 
    -1) / n)
)
    

次にメビウス反転の方法です。
[コピペ用]

      =LAMBDA(p,n,IF(n=1,0,LET(seq,SEQUENCE(SQRT(n)),r,TOCOL(seq/(MOD(n,seq)=0),2),div,DROP(SORT(UNIQUE(TOCOL(HSTACK(r,n/r)))),1),IF(COUNT(div)=1,((n-1)*p^n+p)/n,p^n-SUM(p^n,REDUCE(-p^(n/INDEX(div,1,1)),SEQUENCE(COUNT(div)-1),LAMBDA(array,k,LET(d,INDEX(div,k+1,1),list,TAKE(div,k),r_1,INDEX(TOCOL(list/(MOD(d,list)=0),2),1,1),r_2,d/r_1,VSTACK(array,IF(ISERROR(r_1),-1*p^(n/d),IF(GCD(r_1,r_2)=1,PRODUCT(SIGN(INDEX(array,MATCH(VSTACK(r_1,r_2),div))))*p^(n/d),0)))))))/n)))*(p-1)
)
    

[見やすい用]

      =LAMBDA(p, n,
    IF(n = 1, 0,
        LET(
            comment_1, "約数divを列挙し、1を除外",
            seq, SEQUENCE(SQRT(n)),
              r, TOCOL(seq / (MOD(n, seq) = 0), 2),
            div, DROP(SORT(UNIQUE(TOCOL(HSTACK(r, n / r)))), 1),
            
            comment_2, "素数(約数1つ)の場合は直接計算し、それ以外はメビウス関数の乗法性で決定",
            IF(COUNT(div) = 1, ((n - 1) * p^n + p) / n, 
                p^n - SUM(p^n, REDUCE(-p^(n / INDEX(div, 1, 1)), SEQUENCE(COUNT(div) - 1), LAMBDA(array, k, 
                    LET(
                        d, INDEX(div, k + 1, 1),
                        r_1, INDEX(TOCOL(TAKE(div, k) / (MOD(d, TAKE(div, k)) = 0), 2), 1, 1),
                        VSTACK(array, IF(ISERROR(r_1), -1 * p^(n / d), IF(GCD(r_1, d / r_1) = 1, PRODUCT(SIGN(INDEX(array, MATCH(VSTACK(r_1, d / r_1), div)))) * p^(n / d), 0)))
                    )
                ))) / n
            ) * (p - 1)
        )
    )
)
    

第12問

〇と|で置き換えるでお馴染みの

正の整数$n,k$が与えられるので、$n$個から重複をゆるして$k$個選ぶ組み合わせを列挙する数式を組んでください。

高校数学と同じノリで計算すればよいでしょう。まずコードを見せます。
[コピペ用]

      =LAMBDA(n,k,LET(m,n+k-1,mm,2^m,array,MID(BASE(IF(n=1,2^k-1,REDUCE(2^k-1,SEQUENCE(COMBIN(m,k)-1),LAMBDA(array,b,LET(a,TAKE(array,-1),c,BITAND(a,mm-a),w,a+c,VSTACK(array,BITOR(INT(BITRSHIFT(BITXOR(w,a),2)/c),w)))))),2,m),SEQUENCE(,m),1)*SEQUENCE(,m),barray,WRAPROWS(TOCOL(array/(array<>0),2),k),barray-SEQUENCE(,k,0))
)
    

[見やすい用]

      =LAMBDA(n, k,
    LET(
        comment_1, "ビット列の長さを m = n+k-1 とし、組み合わせの総数を計算",
        m, n + k - 1,
        mm, 2^m,
        
        comment_2, "Gosper's hack: ビット演算で「1の数が等しい次の整数」を順次生成",
        bit_patterns, REDUCE(2^k - 1, SEQUENCE(COMBIN(m, k) - 1), LAMBDA(array, b, 
            LET(
                a, TAKE(array, -1),
                c, BITAND(a, mm - a),
                w, a + c,
                VSTACK(array, BITOR(INT(BITRSHIFT(BITXOR(w, a), 2) / c), w))
            )
        )),
        
        comment_3, "生成した数値を2進数文字列に変換し、1が立っている位置を配列化",
        bin_array, MID(BASE(IF(n = 1, 2^k - 1, bit_patterns), 2, m), SEQUENCE(, m), 1) * SEQUENCE(, m),
        
        comment_4, "1の位置から「仕切り」のインデックスを計算して最終的な分割結果を導出",
        barray, WRAPROWS(TOCOL(bin_array / (bin_array <> 0), 2), k),
        barray - SEQUENCE(, k, 0)
    )
)
    

まず$k$個の〇と$n-1$個の|の並びを用意しなければなりません。ここではGosper's Hackという手法を用います。(教えていただいたのは Y.K.さん です。ありがとうございます。)
詳しくは こちらの記事 などなどに載っているため、気になる人は確認して下さい。ビット演算で逐次的に求められます。
その後、〇の位置(左から何番目にあるかをSEQUENCE(,m)を掛けることで明確にします。)
この位置番号から、$i$番目の〇なら$i-1$を引いてあげると、それが何番目を選んでいるのかに戻すことが出来ます。TOCOLで|の除去、WRAPROWSで$k$列ごとに整列させてSEQUENCE(,k,0)を引けば完了です。

第13問

約数に何か恨みが

正の整数$n$と非負整数$k$が与えられるので、$n$の正の約数すべてを根にもつ整数係数最小多項式$f(x)$$k$次の項の係数を計算する数式を$3$通りの方法で組んでください。

根(ね)ではなく根(こん)です。
これに対する$3$つの解法ははっきりしています。
まず解と係数の関係による愚直計算です。
[コピペ用]

      =LAMBDA(n,k,LET(seq,SEQUENCE(,SQRT(n)),r,TOCOL(seq/(MOD(n,seq)=0),2),mdiv,-UNIQUE(TOROW(HSTACK(r,n/r)),TRUE),m,COUNT(mdiv),mm,2^m,IF(k=m,1,IF(k=0,PRODUCT(mdiv),SUM(DROP(REDUCE(VSTACK(PRODUCT(TAKE(mdiv,,-(m-k))),2^(m-k)-1),SEQUENCE(COMBIN(m,m-k)-1),LAMBDA(array,b,LET(a,INDEX(TAKE(array,-1),1,1),c,BITAND(a,mm-a),w,a+c,d,BITOR(INT(BITRSHIFT(BITXOR(w,a),2)/c),w),bits,MID(BASE(d,2,m),SEQUENCE(,m),1)+0,VSTACK(DROP(array,-1),PRODUCT(FILTER(bits*mdiv,bits=1)),d)))),-1)))))
)
    

[見やすい用]

      =LAMBDA(n, k, 
    LET(
        seq, SEQUENCE(, SQRT(n)),
        r, TOCOL(seq / (MOD(n, seq) = 0), 2),
        mdiv, -UNIQUE(TOROW(HSTACK(r, n / r)), TRUE),
        m, COUNT(mdiv),
        mm, 2^m,
        
        comment_1, "kが最高次や定数項なら即決。それ以外はGosper's Hackで組合せを生成",
        IF(k = m, 1, IF(k = 0, PRODUCT(mdiv), 
            SUM(DROP(REDUCE(VSTACK(PRODUCT(TAKE(mdiv, , -(m - k))), 2^(m - k) - 1), SEQUENCE(COMBIN(m, m - k) - 1), LAMBDA(array, b, 
                LET(
                    a, INDEX(TAKE(array, -1), 1, 1),
                    c, BITAND(a, mm - a),
                    w, a + c,
                    d, BITOR(INT(BITRSHIFT(BITXOR(w, a), 2) / c), w),
                    bits, MID(BASE(d, 2, m), SEQUENCE(, m), 1) + 0,
                    comment_2, "ビットが立っている根の積を累計(根と係数の関係)",
                    VSTACK(DROP(array, -1), PRODUCT(FILTER(bits * mdiv, bits = 1)), d)
                )
            )), -1))
        ))
    )
)
    

Gosper's Hack再来です。$\sigma_0(n)$個の中から$k$個選ぶのと同じなので使えます。

次の方法は、実際に多項式を展開する手法です。
[コピペ用]

      =LAMBDA(n,k,LET(seq,SEQUENCE(SQRT(n)),r,TOCOL(seq/(MOD(n,seq)=0),2),mdiv,-SORT(UNIQUE(TOCOL(HSTACK(r,n/r)))),INDEX(REDUCE(HSTACK(INDEX(mdiv,1,1),1),DROP(mdiv,1),LAMBDA(array,d,HSTACK(0,array)+d*HSTACK(array,0))),1,k+1))
)
    

[見やすい用]

      =LAMBDA(n, k, 
    LET(
        seq, SEQUENCE(SQRT(n)),
        r, TOCOL(seq / (MOD(n, seq) = 0), 2),
        mdiv, -SORT(UNIQUE(TOCOL(HSTACK(r, n / r)))),
        
        comment_1, "初期多項式 (x + d1) から順に (x + di) を掛けて係数配列を更新",
        INDEX(REDUCE(HSTACK(INDEX(mdiv, 1, 1), 1), DROP(mdiv, 1), LAMBDA(array, d, 
            comment_2, "HSTACKでの0付与は次数を1つ上げる(x倍する)操作に相当",
            HSTACK(0, array) + d * HSTACK(array, 0)
        )), 1, k + 1)
    )
)
    

一番わかりやすいし、多分一番速いと思います。

3つ目の方法はニュートンの恒等式を用いる方法です。

ニュートンの恒等式

$n$個の変数$w_1,w_2,...,w_n$について、$i$次基本対称式を$e_i$、その$i$乗和を$p_i$と表すと
$$ ke_k = \sum_{i=1}^k e_{k-i}p_i$$
ただし、$e_0=1$$k>n$の時$e_k=0$とする。

多項式の$k$次の係数は$(-1)^{n-k}e_{n-k}$と表されます。
[コピペ用]

      =LAMBDA(n,k,LET(seq,SEQUENCE(SQRT(n)),r,TOCOL(seq/(MOD(n,seq)=0),2),div,SORT(UNIQUE(TOCOL(HSTACK(r,n/r)))),m,COUNT(div),IF(k=0,PRODUCT(-div),IF(k=m,1,IF(k=m-1,SUM(-div),(-1)^(m-k)*INDEX(REDUCE(SUM(div)+{0,0},SEQUENCE(m-k-1,,2),LAMBDA(array,k,LET(p_k,SUM(div^k),a_1,CHOOSECOLS(array,1),a_2,CHOOSECOLS(array,2),HSTACK(VSTACK(SUM(VSTACK(a_1*a_2,p_k)*(-1)^SEQUENCE(k,,0))/k,a_1),VSTACK(a_2,p_k))))),1,1)))))
)
    

[見やすい用]

      =LAMBDA(n, k, 
    LET(
        seq, SEQUENCE(SQRT(n)),
        r, TOCOL(seq / (MOD(n, seq) = 0), 2),
        div, SORT(UNIQUE(TOCOL(HSTACK(r, n / r)))),
        m, COUNT(div),
        
        IF(k = 0, PRODUCT(-div), IF(k = m, 1, IF(k = m - 1, SUM(-div), 
            (-1)^(m - k) * INDEX(REDUCE(SUM(div) + {0, 0}, SEQUENCE(m - k - 1, , 2), LAMBDA(array, k_idx, 
                LET(
                    p_k, SUM(div^k_idx),
                    a_1, CHOOSECOLS(array, 1),
                    a_2, CHOOSECOLS(array, 2),
                    comment_1, "ニュートンの恒等式を用いて、k次のべき乗和からk次の基本対称式を算出",
                    HSTACK(VSTACK(SUM(VSTACK(a_1 * a_2, p_k) * (-1)^SEQUENCE(k_idx, , 0)) / k_idx, a_1), VSTACK(a_2, p_k))
                )
            )), 1, 1)
        )))
    )
)
    

では最後の問題に移りましょう。

第14問

ニム

各成分が非負整数の$1\times n$配列$A$が与えられるので、これを$i$番目の山に石が$A_{1,i}$個あるとみて、正規形$n$山ニムのグランディ数を計算する数式を組んでください。ただし、計算にはよく知られたグランディ数の周期性や和公式は使わず、定義通りに計算してください。$1$回の着手で取れる石の最大数を可変にできるとなお良いですね。

本格的なDPです。まずコードを見せます。
[コピペ用]

      =LAMBDA(A,[max],LET(L,IF(ISOMITTED(max),MAX(A),max),mex,LAMBDA(V,LET(cV,COUNT(V),XMATCH(FALSE,EXPAND(SORT(V,,1,TRUE),,cV+1,cV+2)=SEQUENCE(,cV+1,0),,1)-1)),f,FILTER(A,A<>0),s,SUM(f),c,COUNT(f),TAKE(REDUCE(HSTACK(0,1),SEQUENCE(PRODUCT(f+1)+c-2,,-c+2),LAMBDA(array,k,IF(k<=0,HSTACK(0,VSTACK(INDEX(array,0,2),TAKE(array,-1,-1)*(INDEX(f,1,k+c-1)+1))),LET(a_1,TOCOL(INDEX(array,0,1),2),a_2,TOROW(INDEX(array,0,2),2),x,MOD(QUOTIENT(k,a_2),f+1),move,k-TOROW(IF(SEQUENCE(MIN(MAX(x),L))<=IF(x<=L,x,L),SEQUENCE(MIN(MAX(x),L))*a_2,NA()),2)+1,HSTACK(VSTACK(a_1,mex(UNIQUE(INDEX(a_1,move),TRUE))),TOCOL(a_2)))))),-1,1))
)
    

[見やすい用]

      =LAMBDA(A, [max], 
    LET(
        L, IF(ISOMITTED(max), MAX(A), max),
        comment_1, "mex関数: 集合に含まれない最小の非負整数を特定する",
        mex, LAMBDA(V, LET(cV, COUNT(V), XMATCH(FALSE, EXPAND(SORT(V, , 1, TRUE), , cV + 1, cV + 2) = SEQUENCE(, cV + 1, 0), , 1) - 1)),
        f, FILTER(A, A <> 0),
        c, COUNT(f),
        
        comment_2, "全状態空間を走査。初期段階(k<=0)で混合進数の1~i番目までの積である重み配列(a_2)を作成",
        TAKE(REDUCE(HSTACK(0, 1), SEQUENCE(PRODUCT(f + 1) + c - 2, , -c + 2), LAMBDA(array, k, 
            IF(k <= 0, 
                HSTACK(0, VSTACK(INDEX(array, 0, 2), TAKE(array, -1, -1) * (INDEX(f, 1, k + c - 1) + 1))), 
                LET(
                    a_1, TOCOL(INDEX(array, 0, 1), 2),
                    a_2, TOROW(INDEX(array, 0, 2), 2),
                    comment_3, "現在の各山の数xから、移動可能な次の状態(move)を全て特定",
                    x, MOD(QUOTIENT(k, a_2), f + 1),
                    move, k - TOROW(IF(SEQUENCE(MIN(MAX(x), L)) <= IF(x <= L, x, L), SEQUENCE(MIN(MAX(x), L)) * a_2, NA()), 2) + 1,
                    comment_4, "移動先状態のGrundy値集合に対し、そのmex値を現在の状態の値とする",
                    HSTACK(VSTACK(a_1, mex(UNIQUE(INDEX(a_1, move), TRUE))), TOCOL(a_2))
                )
            )
        )), -1, 1)
    )
)
    

グランディ数は、今の盤面から遷移できる全ての盤面の状態のグランディ数のmex演算によって定義されます。
mex演算はその集合に含まれない最小の非負整数値を返します。
UNIQUE配列かつSORTした状態で{0,1,2,...}と比較して合わない箇所を返すようにすれば良いでしょう。

これREDUCEが1回しか使えないのが少々辛い所です。まずREDUCEですが、$k\leq 0 $のときに混合進数を計算するための重み配列a_2を作成するところから始めています。REDUCEが1回しか使えないとなると2次元で扱うことが難しくなるので、状態を一列で扱いたくなります。そのとき、現在のインデックスが盤面の状態1つを表せるようにしておきたいのです。この重み配列もREDUCEを使って計算しなければなりませんが、それを負のときに無理やり処理しています。
その後、その混合進数を計算し、そこから取れる分の石を取った後の盤面の状態番号を全取得します。そのグランディ数を参照してmex演算、これをこのインデックス$k$が表す状態$x$のグランディ数とします。

$k$$0$$1$$\cdots$$A_{1,1}$$A_{1,1}+1$$\cdots$
$x$$\{0,0,\cdots,0 \}$$\{1,0,\cdots,0 \}$$\cdots$$\{A_{1,1},0,\cdots,0 \}$$\{0,1,\cdots,0 \}$$\cdots$

(みたいな感じです。)
変数maxは入れたときはその数値を1回で取れる石の最大数とします。入れなかったときは$\max (A)$とします。

おまけ問題

本来のVSTACK/HSTACK禁止だった例題を解きます。

正の約数の列挙
      =LAMBDA(n,
    TEXTSPLIT(REDUCE(1, SEQUENCE(n - 1, , 2), LAMBDA(a, b, 
        comment_1, "bがnを割り切るならaに連結する",
        IF(MOD(n, b), a, TEXTJOIN(",", , a, b))
    )), ",")
)
    
二項間漸化式 - 1
      =LAMBDA(n,
    IF(n = 1, 1, 
        TEXTSPLIT(REDUCE(1, SEQUENCE(n - 1, , 2), LAMBDA(a, b, 
            comment_1, "最後の項を抽出し 3x + 1 を計算して連結",
            TEXTJOIN(",", , a, 3 * IF(b = 2, a, TEXTAFTER(a, ",", -1)) + 1)
        )), ",")
    )
)
    
二項間漸化式 - 2
      =LAMBDA(n,
    IF(n = 1, 1, 
        TEXTSPLIT(REDUCE(1, SEQUENCE(n - 1, , 1), LAMBDA(a, b, 
            comment_1, "前の項に現在のインデックスの2乗を足していく",
            TEXTJOIN(",", , a, IF(b = 1, a, TEXTAFTER(a, ",", -1)) + b^2)
        )), ",")
    )
)
    
割り切れる回数の数え上げ
      =LAMBDA(p, n,
    TEXTBEFORE(
        REDUCE(n, SEQUENCE(LOG(n, p) + 1), LAMBDA(a, b, 
            comment_1, "pで割り切れなくなったらその時のループ回数(b-1)を@でマーク",
            IFERROR(IF(MOD(a, p), (b - 1) & "@", a / p) , a )
        )), 
    "@") + 0
)
    
三項間漸化式
      =LAMBDA(n,
    IF(n <= 1, 1, 
        TEXTAFTER(
            REDUCE("1,1", SEQUENCE(n - 1, , 2), LAMBDA(text, k, 
                LET(
                    comment_1, "文字列から a_{n-2} と a_{n-1} を分離",
                    a_1, TEXTBEFORE(text, ","),
                    a_2, TEXTAFTER(text, ","),
                    comment_2, "次の項 a_n = a_{n-1} + 2*a_{n-2} を作り、ペアを更新",
                    TEXTJOIN(",", , a_2, 2 * a_1 + a_2)
                )
            )), 
        ",") + 0
    )
)
    

おわりに

かなり面白い問題の勢ぞろいなので是非解いてみて下さい。制限を無くして処理を高速化するという解き方もありでしょう。恐らく別解もあると思います。

間違い等あればコメントよろしくお願いします。

参考文献

投稿日:2日前
更新日:1日前
数学の力で現場を変える アルゴリズムエンジニア募集 - Mathlog served by OptHub

この記事を高評価した人

高評価したユーザはいません

この記事に送られたバッジ

バッジはありません。

投稿者

vunu
vunu
56
6705
「西」の「西東南」の部分

コメント

他の人のコメント

コメントはありません。
読み込み中...
読み込み中