経緯
表題の通り、Power Appsで素数を判定する仕組みを考えてみました。きっかけとなった動画は以下のゲームアプリでした。
これをPower Appsでも作れないか…と考えたのですが;
- 数字がポップアップで出現する
- 素数でない数字をスライドさせると数字が分裂する
- 素数をタップすると数字が消える
- スコアが表示される
- 連続解答数が記録される
- 確率変動(スコア・アップ)
- 他、諸々ありそう
といった辺りのことまでは現時点での自分の実力では難しいと判断しました。
ただ、素数であるか否かを判定する機能の実装までは自分にもできそうだと思ったので、まずはこの仕組みだけを考えてみることにしました。
素数について整理する
一般的に、素数とは、1よりも大きい自然数であり、1と自分自身でしか割り切れない数をいいます。
10までの数字を例にすると、素数は2、3、5、7です。
「1よりも大きい自然数」と「1と自分自身でしか割り切れない数」であることをPower Appsに判断させるためには、以下のような条件を満たしていなければなりません。
- n=<2である(素数でない場合、nは2未満である)
- nは、2からn-1の整数で割り切れない(素数でない場合、nは2からn-1のいずれかで割り切れる)
- 2からn-1までの数値で割り算を行い、それぞれ余剰が0になるかどうかを確認
- n=7の場合、7÷2、7÷3 […] 7÷6を行う - その結果に0が1つでも含まれていたら合成数、1つも0が無ければ素数である
ということで、以下、解説に移ります(2日もかかってしまいました)
自然数nを宣言し、表示する
- 変数を宣言するためのボタン(btnGenerate)
- 変数を表示するためのラベル(lblNumber)
- 素数を判定するためのラベル(lblJudge)
btnGenerate の OnSelect プロパティに以下の変数を宣言します。
Set(gblNumber,RandBetween(2,999))
今回は、3桁の自然数をランダムで発生させ、その結果を変数 gblNumber としています。2未満の数(=1)は素数ではないので2~999までの数をランダムで発生させます。
数列のテーブルを生成する
次に、1ずつ増えていく、発生させた変数までの数列を生成します。この数列は、除数となるため必要です。除数とは、10÷2であれば、2の部分をいいます。ギャラリー(galDivisor)を配置し、Items プロパティに以下の数式を入力します。
Sequence(gblNumber-1,2,1)
数式を入力したら、テンプレートセルにラベル(命名不要)を1つ置き、Text プロパティを ThisItem.Value としてみましょう。ラベルに表示された数字の分だけ、14であれば2~14の計13行分の単一テーブル(Value)ができます。
次に、作成した数列をコレクション(colPrime)とします。変数を宣言したボタンに以下の数式を入力します。
ClearCollect(
colPrime,
Sequence(
gblNumber - 1,
2,
1
)
)
数式を入力したら、galDivisor を複製し、複製したギャラリー(galPrime)の Items プロパティに colPrime と入力します。入力したら、先ほど作成した galDivisor と同様のテーブルが完成します。これをデータソースとして素数を判定していきます。
なお、galDivisor も galPrime も、わざわざ作る必要はありません。視覚的に分かりやすく作業を進めるために配置しています。作業が完了したら破棄して問題ありません。
生成した数列に応じて除算を行う
さて、整数n(gblNumber)が素数であるかを判断するためには「素数でない場合」を考えると良いです。つまり、素数でない場合は「nは2からn-1のいずれかで割り切れる」ということです。換言すれば、割り切れた場合、整数nは素数ではないということです。
例えば、整数nが10の場合、2と5で割り切れてしまうため、10は素数ではありません。整数nが7の場合、1と7以外で割り切れないため7は素数です。
以上の観点から、Mod 関数を使って余剰が0か0以外の数かを判定します。この計算を生成された除数分で行うので、ForAll 関数を組み合わせます。btnGenerate の OnSelect プロパティに入っていた数式を以下に変更します。
Set(gblNumber,RandBetween(2,999));
ClearCollect(
colPrime,
ForAll(
Sequence(
gblNumber - 1,
2,
1
),
Mod(
gblNumber,
Value
)
)
)
数式を入力したらアプリを実行し、ボタンを押して動作を確認します。
57は2や4で割り切ることはできませんが、3で割り切ることができます(商は19)
素数を判定する
最後に、素数かどうかを判定していきます。余剰が0か0以外かで素数か否かが判断できるということで、CountIf 関数を使用し、colPrime の内の0だけを数えます。まずは、数を確認するために以下の数式を入力します。
CountIf(FirstN(colPrime,gblNumber-2),ThisRecord.Value=0)
数式を入力すると、57は素数ではないため、以下の画像のように「2(※0が2つ含まれています)」と返って来ます。
また、colPrime に FirstN 関数を利用し、第二引数を gblNumber - 2 としているのは、57を含めたくないからです。57を含めると、この後の判定のノイズとなってしまうため、絞り込みをかけてやる必要がありました(下記画像参照)
ここまでできたら、上の数式を If 関数で囲み、lblJudge の Text プロパティに「素数」か「合成数(素数でない数)」を表示していきます。
If(CountIf(FirstN(colPrime,gblNumber-2),ThisRecord.Value=0)=0,"素数","合成数")
結果は以下の通りです。何度かボタンを押して確認してみましょう。チェックが難しい場合、RandBetween 関数の 999 を 10 に書き換えるなどしてみると分かりやすいはずです。
まとめ
今回はランダムで発生させた整数nに対する判定でしたが、テキスト入力コントロールに入力した値を Value 関数で数値化して判定することも可能です。応用してやってみてください。
さて、素数ゲームですが、進捗があったらまたコチラで報告します。お楽しみに。
余談:3桁の素数(動作確認用)
2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151, 157, 163, 167, 173, 179, 181, 191, 193, 197, 199, 211, 223, 227, 229, 233, 239, 241, 251, 257, 263, 269, 271, 277, 281, 283, 293, 307, 311, 313, 317, 331, 337, 347, 349, 353, 359, 367, 373, 379, 383, 389, 397, 401, 409, 419, 421, 431, 433, 439, 443, 449, 457, 461, 463, 467, 479, 487, 491, 499, 503, 509, 521, 523, 541, 547, 557, 563, 569, 571, 577, 587, 593, 599, 601, 607, 613, 617, 619, 631, 641, 643, 647, 653, 659, 661, 673, 677, 683, 691, 701, 709, 719, 727, 733, 739, 743, 751, 757, 761, 769, 773, 787, 797, 809, 811, 821, 823, 827, 829, 839, 853, 857, 859, 863, 877, 881, 883, 887, 907, 911, 919, 929, 937, 941, 947, 953, 967, 971, 977, 983, 991, 997