前回「完全数入門! 数の面白シリーズ!」で完全数について紹介しました。
この記事では、「完全数」という数の不思議な性質を、Pythonを使って実際にコードで体感していきます。
数学が好きな方や、プログラミングを学び始めた方に向けて、シンプルなコードで分かりやすく解説します。
完全数とは (復習)
自身以外の正の約数の和が、ちょうど自分自身に等しい数のこと
たとえば、以下は完全数の例です。
- 6 (1+2+3 = 6)
- 28 (1+2+4+7+14 = 28)
- 496
- 8128
1から10000までの完全数を探そう
pythonでプログラムを組むことで1から10000までの完全数を列挙します。
# このコードでは、1から10000までの自然数を順に調べ、完全数かどうかを`is_perfect()`関数で判定しています。 # 見つかった完全数だけを、`print()`関数で出力しています。 # 完全数かどうかを判定する関数 def is_perfect(n): divisors = [] # 約数を保存するためのリスト for i in range(1, n): # 1からn-1までの数を調べる if n % i == 0: # iがnの約数なら divisors.append(i) # リストに追加する return sum(divisors) == n # 自分を除く約数の和がnに等しければ完全数 # プログラムの本体部分 if __name__ == '__main__': for i in range(2, 10001): # 2から10000まで調べる if is_perfect(i): print(f"{i} は完全数です")
このソースコードを実行すると次のような結果が表示されます。
6は完全数です 28は完全数です 496は完全数です 8128は完全数です
上のソースコードは完全数の定義に沿って作成しています。
is_perfect関数では次のような手順で与えられた引数nが完全数かどうかを判断しています。
- 空のリスト
divisors
を用意します。 1
からn-1
までの整数の中で、n
を割り切れるもの(=約数)を探し、divisors
に追加していきます。- 集めた約数の合計を求め、それが
n
自身と一致していればTrue
(完全数)を返します。そうでなければFalse
を返します。
プログラム本体では2から10000までの数で完全数になるものを調べています。
is_perfect関数が引数に与えた数が完全数ならTrueを返すのでif文で分岐し、完全数であるものだけを出力しています。
※ print(f"{i} は完全数です")
のように、文字列の前に f
を付けると、
中括弧 {}
の中に変数や式を埋め込むことができます。これを f-string(フォーマット済み文字列リテラル) と呼びます。
(おまけ) Rustで完全数を探してみよう!
上で作成したソースコードをRustで作成します。
// 関数 is_perfect:n が完全数かどうかを判定する fn is_perfect(n: u32) -> bool { // 約数を格納するベクタ(Pythonのリストに相当) let mut divisors = Vec::new(); // 1 から n-1 までの数を順に調べる for i in 1..n { // i が n の約数であれば if n % i == 0 { // 約数リストに追加 divisors.push(i); } } // 約数の合計を求める(Pythonの sum(divisors) に相当) let sum: u32 = divisors.iter().sum(); // 合計が n に等しければ完全数 sum == n } fn main() { // 2 から 10000 までの数を調べる for i in 2..=10000 { // i が完全数であれば表示 if is_perfect(i) { println!("{} は完全数です", i); } } }
やっていることは基本的にはPythonと同じです。
ただ、実行してみるとPythonよりRustの方が実行速度が速いことに気づくと思います。
実際にpythonとRustにそれぞれ以下を入れて計測するとRustの方が1秒ほど速いことが分かります。
import time # 途中は同じ if __name__ == '__main__': start = time.time() # 開始時間の記録 for i in range(2,10001):# 2から10000まで調べる if is_perfect2(i): print(f"{i}は完全数です") end = time.time() print(f"かかった時間: {end - start:.2f}秒")
use std::time::Instant; // 時間計測のためのモジュールをインポート // 途中同じ fn main() { // 開始時間の記録 let start = Instant::now(); // 2 から 10000 までの数を調べる for i in 2..=10000 { // i が完全数であれば表示 if is_perfect(i) { println!("{} は完全数です", i); } } // 経過時間を計算 let duration = start.elapsed(); println!("かかった時間: {:.2}秒", duration.as_secs_f64()); }
最後に
シンプルな定義からはじめた完全数探索ですが、これはまだ序章にすぎません。
次回の記事では、「完全数 = メルセンヌ素数 × 2のべき乗」という法則を使って、コードを改良していきます。お楽しみに!