見出し画像

Excelのワークシート関数 で 素数判定

神髄先生の課題でわくわく

こんなお題が出て、「俺のでばんやー!」って、暴走しまして、問題をよく読まず、単純に素数判定やー!って勘違いして、お酒を飲みながら、テンションを上げて、素数判定に向かうおっさんがいました。
今日は、そんな記事です。
(いや、問題読めよって話だけど、勘弁してね)

エラトステネスの篩

素数判定に「エラトステネスの篩」というアルゴリズムがあります。
考え方は、単純です。
素数とは「2以上の自然数でNの平方根以下の整数で割り切れるかどうかをしらべ、途中で割り切れれば、素数ではなく、1まで割り切れない場合は素数と判断する。」という考えです。
ま、この原理を理解できれば、VBAで、簡単に実装できます。
しかし、今回は、ワークシート関数のみですよ(演算子もあるよ)
わくわくしないわけがないです。

巨大素数でなければVBAでも判断可能

ただ、このアルゴリズムは、数が巨大になるとどうなるか未検証なんですよね・・・ご勘弁ください。(だから、暗号化技術に巨大素数が使われている)
もし、Excel VBA で書くと、こんな感じです。

Option Explicit

Private NumA_ As Long
Private IsPrimaryNum_ As Boolean

Public Property Let NumA(Value As Long)
    NumA_ = Value
    Call IsPrimary
End Property

Public Property Get IsPrimaryNum() As Boolean
    IsPrimaryNum = IsPrimaryNum_
End Property

Private Sub IsPrimary()
    Dim i As Long
    Dim j As Long
    Dim Limit As Long
    
    If NumA_ >= 2 Then
        Limit = CLng(Sqr(NumA_))
        For i = Limit To 1 Step -1
            If (NumA_ Mod i) = 0 Then
                Exit For
            End If
        Next i
        If i = 1 Then
            IsPrimaryNum_ = True
        Else
            IsPrimaryNum_ = False
        End If
    ElseIf Not NumA_ <= 0 Then
        IsPrimaryNum_ = True
    Else
        IsPrimaryNum_ = False
    End If
End Sub

申し訳ございません。このコードは、クラスモジュールのコードになります。 よっぱの都合で、過去に遊びで作ったコードでカンベンして下さい。
クラスモジュールを追加して、そのままコピッペすれば、使えます。

Excel の ワークシート関数で実現してみる

よっぱおじさんは、単純に、上に書いたVBAのコードをExcelのワークシート関数の組み合わせで実現できると思い、チャレンジしました。
神髄先生の課題を勘違いして回答した俺に対してはスルーを。
Excelワークシート関数で数式を作り込む難しさは、前にも「引数」の難しさを伝えたつもりですが、次は、「作り方」なんですよね・・・
普通の数式であれば、内から外に向かって作り込むのですが、スピル関数の場合は、逆のような感覚もあったり、なにより「配列」を脳内にイメージしないと、作り込めないです。
これが、スピル関数の最大の難関だと、私は、お酒のみながら思っています。(完全な独断と偏見です・・・)
だから、数式は、VBAより難しい
最新(2022年10月現在)のExcelワークシート関数は、配列を扱える関数が登場しています。だから、関数で実現できると思ったわけで、実践です。
で、下に書いたような考え方で作り込み。

  1. 数値Nの平方根の整数(切り捨て)を求める=>A

  2. 数値NをAからー1減らした自然で割った余剰を求める

  3. 余剰を求めた配列で、余剰0になった個数を調べる

この手順なら関数だけで実現できると。思った。

最初に、完成した数式を紹介します。

=MAP(SEQUENCE(10,10),LAMBDA(_X,IF(LET(_N,_X,IF(_N<=1,FALSE,IF(_N<=3,TRUE,LET(_SQRTN,SEQUENCE(INT(SQRT(_N)),,INT(SQRT(INT(_N))),-1),_MODN,MOD(_N,_SQRTN),_ISNP,OR(TAKE(_MODN,COUNT(_MODN)-1)=SEQUENCE(COUNT(_MODN)-1,,0,0)),IF(_ISNP,FALSE,TRUE))))),_X,"")))

非常に長い数式ですが、正しく判定されています。
分割して読み解けば、簡単です。
なお、この日(2022年10月17日)初めてMAP関数を使いました💦
で、まずは、MAP関数です。これは、夢の関数です。

Docsを読んでいただきたいと思います。要するに、値を一つ一つ作ってくれる夢の関数です。
事例では、FILTER関数の応用で使われていますね。
(理解できませんでした💦)

さて、簡単に説明いたします。

一番初めに作るべき配列は、指定された数値Nの平方根から、-1された配列を作ることです。
こんな感じです。

=SEQUENCE(INT(SQRT( N )),,INT(SQRT(INT( N ))),-1)

この数式で出た結果を仮に「平方根の配列」とします。
これで、平方根の整数の降順の配列が完成します。

次に、指定した数値Nと、「平方根の配列」の余剰配列を準備します。

=MOD(N, 平方根の配列 )

これで、指定した数値Nを「平方根の配列」で割った余剰の配列が完成します。この数式で出た結果を仮に「余剰配列」とします。

次に、余剰0が途中にあったのかどうかを調べます。
OR関数を使います。

=OR(TAKE( 余剰配列 ,COUNT( 余剰配列 )-1)=SEQUENCE(COUNT( 余剰配列 )-1,,0,0))

ここで、重要なポイントがあります。
最後の配列を計算させないことがとても重要です。
最後の配列の分母は、必ず「1」になり、分母が「1」の場合、余剰は必ず「0」になってしまいます。だから、最後の配列は、計算から外す必要があるのです。

FALSE を返すと、数値Nは素数と判定。
TRUE を返せば、素数では無いと判定します。

ただ、完成した数式を作るだけで、2時間かかっています。
ま、苦労した点があるのです・・・
単純にヘタレですね・・・

苦労した点

指定した数値が3以下だと、数式がエラーになってしまう

IF(_N<=1,FALSE,IF(_N<=3

と、数式の最初の方に、変な分岐処理を入れています。
ま、これって、3以下の平方根の整数は、「1」だから、配列を作れないから、その後の数式で、エラーになっちゃうのです。
無理やりエラーにならないようにしているだけです。

それと、課題では「1セルでスピル」なので、どうしても数式が長くなって、読みにくい数式になってしまうことですかね💦
趣味で遊んでいるから許してほしいですが、
実務(業務)では、こんな数式を作っちゃだめですよw
数式の入れ子は3個までですぞ。(勝手な持論)

てなわけで、なんとなく、記事にしたくて、お酒飲みながら書きました。
ハッキリ言って、一般事務員で、「素数」なんて使うことは、絶対にありません。マジで無いです。

ただ、素数って、魅力的ですよねwww

今回は、簡単な繰り返し処理が、以前のESCを使わずして実現できる時代になった感動を共感してほしくて、noteに書きました。

こんなくだらない記事でも読んでいただいた方、感謝感激でございます。
ありがとうございます。
嬉しくて、また数式作っちゃいます。
ありがとうございました。また次回です。

この記事が気に入ったらサポートをしてみませんか?