
Excelのワークシート関数 で 素数判定
神髄先生の課題でわくわく
【エクセル問題】※MAP編(使ってねw)
— エクセルの神髄 (@yamaoka_ss) October 17, 2022
A1=SEQUENCE(10,10)
素数は特別らしい。
どう特別かわからないので、前後の数値と素数だけ残して他の数値を消してみることにしました。
23は素数なので前後の22,24と共に残す。
25,26,27は自身も前後も素数ではないので消します。
※素数判定は適当に探して😅 pic.twitter.com/mcqfJl663B
こんなお題が出て、「俺のでばんやー!」って、暴走しまして、問題をよく読まず、単純に素数判定やー!って勘違いして、お酒を飲みながら、テンションを上げて、素数判定に向かうおっさんがいました。
今日は、そんな記事です。
(いや、問題読めよって話だけど、勘弁してね)
エラトステネスの篩
素数判定に「エラトステネスの篩」というアルゴリズムがあります。
考え方は、単純です。
素数とは「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ワークシート関数は、配列を扱える関数が登場しています。だから、関数で実現できると思ったわけで、実践です。
で、下に書いたような考え方で作り込み。
数値Nの平方根の整数(切り捨て)を求める=>A
数値NをAからー1減らした自然で割った余剰を求める
余剰を求めた配列で、余剰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に書きました。
こんなくだらない記事でも読んでいただいた方、感謝感激でございます。
ありがとうございます。
嬉しくて、また数式作っちゃいます。
ありがとうございました。また次回です。