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

Pythonで「1」をn個並べた数をmで割った余りを効率的に求める方法

2つの整数 n と m が与えられたとき、「1」を n 個並べた数(例:n = 4 なら 1111)を m で割った余りを求めます。

たとえば入力が n = 4、m = 27 の場合、出力は 4 になります。これは 1111 ÷ 27 の余りが 4 であるためです。

アプローチ

n が大きくなると、「1」を n 個並べた数は桁数が膨大になり、そのまま整数として扱うのは非現実的です。そこで、この種の数(レピュニット)が次の式で表せることを利用します。

R(n) = (10n − 1) / 9

つまり、R(n) mod m を求めるには、10n mod 9m を高速に計算できれば十分です。ここで法として 9m を使う理由は、10n は必ず 9 で割ると 1 余る数だからです。そのため、10n mod 9m の結果を 9 で整数除算すると、そのまま答えが得られます。

べき乗の高速な計算には繰り返し二乗法(バイナリ法)を使用します。これは指数を2進数として分解しながら平方を繰り返すことで、計算量を O(log n) に抑える定番の手法です。

アルゴリズムの手順

  • 関数 util(x, n, m) を定義する。xn mod m を繰り返し二乗法で計算して返す
  • y を 1 で初期化する
  • n > 0 の間、次を繰り返す:
    • n が奇数なら、y = (y × x) mod m と更新する
    • x = (x × x) mod m と更新する
    • n を半分にする(右シフト)
  • y を返す

メインの solve 関数では、util(10, n, 9 × m) の結果を 9 で整数除算して返します。

実装例

以下のコードで実際の動きを確認できます。

def util(x, n, m):
    y = 1
    while n > 0:
        if n & 1:
            y = (y * x) % m
        x = (x * x) % m
        n >>= 1
    return y

def solve(n, m):
    return util(10, n, 9 * m) // 9

n = 4
m = 27
print(solve(n, m))

入力

4, 27

出力

4

計算量

時間計算量は O(log n)、空間計算量は O(1) です。巨大な数を一度も生成しないため、n が非常に大きい場合でも高速かつ安定して動作します。

  1. Pythonで二分探索木(BST)の指定範囲内にあるノード数を求める方法

    問題の概要 二分探索木(BST)が与えられ、さらに左側の境界値 l と右側の境界値 r が指定されます。このとき、木に含まれるすべてのノードの中で、値が l 以上 r 以下の範囲内にあるノードの個数を求めるのが目的です。 例えば、次のような木が与えられたとします。 このとき l = 7、r = 13 とすると、範囲内に含まれるノードは 8、10、12 の3つなので、出力は 3 になります。 アルゴリズムの考え方 スタックを使った反復的な深さ優先探索(DFS)で木をたどります。重要なポイントは、二分探索木の性質を活かして枝刈り(pruning)を行うことです。値が境界より小さいノードの左側の

  2. Pythonでリスト内の最小値を見つける方法を解説

    この記事では、リストの中から最小の数値を見つける方法について、具体的なサンプルコードとともに詳しく解説します。問題の概要問題: 数値のリストが与えられたとき、その中に含まれる最も小さい数値を画面に表示すること。この問題を解くアプローチは主に2つあります。ひとつは sort() メソッドを使ってリストを昇順に並べ替え、先頭の要素(インデックス0)を取得する方法。もうひとつは、Pythonに標準で用意されている組み込み関数 min() を使う方法です。それぞれ順番に見ていきましょう。方法1:sort()メソッドで並べ替えて最小値を取得するまずはリストを昇順にソートし、先頭の要素を取り出す方法です。