前回「完全数入門! 数の面白シリーズ!」で完全数について紹介しました。
この記事では、「完全数」という数の不思議な性質を、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のべき乗」という法則を使って、コードを改良していきます。お楽しみに!
