NAKED WETWARE

「わがままになるのが怖い奴に宇宙は拓けねェさ」

現状で自己最速の素数チェッカーを作った

去るABC149のC問題で、素数に関する問題が出た。

過去問を解いてただけだけど、久々に素数判定の関数を書いた。

一応そこそこの素数チェッカーは書けたけど、改めて自分にどれくらいの素数チェッカーが書けるのか挑戦してみようと思う。

言語はPython3

ソース

# /usr/bin/env python3
# -*- coding:utf8 -*-

import math

# 自己ベスト更新を狙う素数チェッカー
def is_prime(num):
    if num % 2 == 0:
        # 素数の中で唯一2だけが偶数
        # それ以外の偶数は問答無用で合成数
        return True if num == 2 else False
    # sqrt(num)以下の数字だけ対象にすればOK
    N = int(math.sqrt(num))
    # 偶数は評価済みなので探索範囲を半分にする
    N = N >> 1
    for i in range(N):
        # 1は素数でないことに注意
        n = (i<<1)+3
        if num % n != 0:
            continue
        # 割り切れてしまった => nはnumの約数
        return False
    # 奇数を全部見たけど割り切れなかった => 素数
    return True

# コンテスト(過去問)で実装した素数チェッカー
def is_prime0(num):
    for i in range(2, int(math.sqrt(num))):
        if num % i == 0:
            return False
    return True

# 何も考えずに実装したオーソドックスな素数チェッカー
def is_prime1(num):
    for i in range(2, num):
        if num % i == 0:
            return False
    return True

if __name__ == '__main__':
    N = int(10e5)
    from datetime import datetime
    print("START: {}".format(datetime.now().strftime('%Y/%m%d %H:%M:%S')))
    print()

    # オーソドックスな素数チェッカー
    start = datetime.now()
    for i in range(2,N):
        is_prime1(i)
    end = datetime.now()
    prime1 = (end - start).total_seconds()

    # 実際に使った素数チェッカー
    # ループ上限値を調整した素数チェッカー
    start = datetime.now()
    for i in range(2,N):
        is_prime0(i)
    end = datetime.now()
    prime0 = (end - start).total_seconds()

    # サンダラムの篩を知って改良した
    # 素数チェッカー
    start = datetime.now()
    for i in range(2,N):
        is_prime(i)
    end = datetime.now()
    prime = (end - start).total_seconds()

    print("prime1 : {}".format(prime1))
    print("prime0 : {}".format(prime0))
    print("prime : {}".format(prime))
    print("is_prime1 / is_prime1 : {}".format(prime1/prime1))
    print("is_prime1 / is_prime0 : {}".format(prime1/prime0))
    print("is_prime1 / is_prime  : {}".format(prime1/prime))

    print()
    print("END  : {}".format(datetime.now().strftime('%Y/%m%d %H:%M:%S')))

結果

出力結果は以下。

START: 2020/0114 23:06:07

prime1 : 43.317183
prime0 : 0.229494
prime : 0.238351
is_prime1 / is_prime1 : 1.0
is_prime1 / is_prime0 : 188.7508300870611
is_prime1 / is_prime  : 181.73694677177775

END  : 2020/0114 23:06:50

コンテストで書いたものと、いま本気で書いたものは、微妙にコンテストで書いたもののほうが速い。

「自己最速の素数チェッカー」を実装しようとして、最速になりませんでした。

けどまあ、どっちもそこそこ速い。

おまけ

ちなみに、14桁の素数 67280421310721 で時間を確かめてみる。

(判定ロジックのループを完全に回してみる)

$ time python3 -c "from check import is_prime; is_prime(67280421310721)"

real    0m1.074s
user    0m0.953s
sys     0m0.047s

1秒ぐらい。

コンテストだと時間制限が2秒だから、半分強を素数判定に使ってる計算。

1<N<=14 の数字を使った素数判定問題が出ないことを祈ろう。