Python
 Computer >> コンピューター >  >> プログラミング >> Python

Pythonで等差数列(AP)の中から素数Pの倍数となる最初の項を効率的に求める方法

初項 A と公差 D を持つ等差数列(AP)と、素数 P が与えられたとき、その等差数列の中で初めて素数 P の倍数になる項の位置(何番目の項か)を求める問題を考えてみましょう。

問題の例

たとえば、A = 3、D = 4、P = 5 という入力の場合、答えは 3 になります。これは、この等差数列の第4項(インデックス3)が素数 5 の倍数であるためです。

  • 第1項 = 3
  • 第2項 = 3 + 4 = 7
  • 第3項 = 3 + 2×4 = 11
  • 第4項 = 3 + 3×4 = 15(5 の倍数)

解法のアプローチ

k 番目の項は A + k×D で表されます。これが P の倍数になる条件は、次の合同式で表せます。

A + k×D ≡ 0 (mod P)

これを変形すると、k ≡ −A × D⁻¹ (mod P) となります。ここで必要になるのが D のモジュラ逆元です。P が素数であるため、フェルマーの小定理を利用すると、逆元は D^(P−2) mod P として高速に計算できます。

アルゴリズムの手順

  • 繰り返し二乗法で冪乗を高速に計算する関数 get_pow(x, y, p) を定義する
    • ans := 1 で初期化
    • x := x mod p
    • y > 0 の間、以下を繰り返す
      • y の最下位ビットが 1 なら ans := (ans × x) mod p
      • y := y / 2(右シフト)
      • x := (x × x) mod p
    • ans を返す
  • メイン処理では以下を実行する
    • A := A mod P、D := D mod P
    • A が 0 の場合 → 初項自体が P の倍数なので 0 を返す
    • D が 0 の場合 → 数列の値が変わらないため解は存在せず -1 を返す
    • それ以外の場合 → X := get_pow(D, P − 2, P) で逆元を求め、(X × (P − A)) mod P を返す

実装コード

それでは、実際のPython実装を見てみましょう。

def get_pow(x, y, p) :
    ans = 1
    x = x % p
    while y > 0 :
        if y & 1 :
            ans = (ans * x) % p
        y = y >> 1
        x = (x * x) % p
    return ans

def get_nearest(A, D, P) :
    A %= P
    D %= P
    if A == 0 :
        return 0
    elif D == 0 :
        return -1
    else :
        X = get_pow(D, P - 2, P)
        return (X * (P - A)) % P

A = 3
D = 4
P = 5
print(get_nearest(A, D, P))

入力

A = 3, D = 4, P = 5

出力

3

計算量について

この手法の優れた点は、数列の各項を順番に確認する必要がないことです。get_pow による繰り返し二乗法の計算量は O(log P) であり、全体として非常に効率的に動作します。仮に素朴な方法で各項を順にチェックすると、最悪の場合 O(P) 回の反復が必要になりますが、本手法では対数時間で答えが得られます。

  1. Pythonで2次元配列からk番目に小さい要素を求めるプログラム

    n×n のユーザー入力による整数行列と値 k が与えられたとき、その2次元配列の中から「k番目に小さい要素」を見つけるのが本記事の目的です。この問題は、Python標準ライブラリの heapq モジュールを使うことで効率的に解決できます。 heapqモジュールとは heapq(ヒープキュー)は、Pythonでヒープ構造(優先度付きキュー)を扱うためのモジュールです。このモジュールの特徴は、毎回ヒープの中で最も小さい要素が取り出される「最小ヒープ(min heap)」として動作する点にあります。また、nsmallest() メソッドを使うことで、データ集合の中から最小のn個の値を簡単に取得するこ

  2. Pythonでリスト内の要素のインデックス(位置)を取得する方法

    Pythonでは、リスト(シーケンス型全般)に含まれる要素の位置を取得するには、index()メソッドを使用します。このメソッドは、指定した要素が最初に出現するインデックスを返します。index()メソッドの基本的な使い方リストに対してindex()を呼び出し、引数に検索したい要素を指定します。>>> L1=[45, 32, 100, 10, 24, 56] >>> L1.index(24) 4この例では、リストL1の中から値24を検索し、その位置であるインデックス4が返されています。Pythonのインデックスは0から始まるため、5番目の要素がインデックス4