【Python】積がxとなり互いに素であるペアの個数を効率的に求めるプログラム
問題の概要
関数 f(x) を考えます。f(x) は、次の条件をすべて満たすペア (p, q) の個数を返します。
- 1 < p ≤ q ≤ x
- p と q は互いに素(最大公約数が 1)
- p × q = x
ここで、正の整数 n が与えられたとき、x を 1 から n まで動かした場合の f(x) の総和を求めるのがこの問題の目的です。
入力例と出力例
たとえば入力が 12 の場合、出力は 3 になります。x が 1〜12 の範囲で条件を満たすのは次の 3 つだけだからです。
- x = 6 のとき:有効なペアは (2, 3) のみ → f(6) = 1
- x = 10 のとき:有効なペアは (2, 5) のみ → f(10) = 1
- x = 12 のとき:有効なペアは (3, 4) のみ → f(12) = 1
一方、たとえば x = 8(= 2 × 4)や x = 4(= 2 × 2)は、分解しても 2 つの数が互いに素にならないため、条件を満たすペアが存在しません。その結果、合計は 3 ペアとなります。
解法のアイデア
各 x について約数をすべて調べる素朴な方法でも解けますが、n が大きくなると計算量が膨れ上がります。そこで、次のような工夫を行います。
- ペア (p, q) のうち小さい方を base(= p)とし、大きい方の q を base で割った余り i に着目します。
- q は「base で割ると i 余れる数」なので、q = i + t × base(t ≥ 1)と表せます。
- gcd(base, i) = 1 であれば、q も base と必ず互いに素になります。したがって、x = base × q = base × i + t × base² という形の x をまとめて数えられます。
- 各 (base, i) の組に対して、x ≤ n を満たす t の個数は ⌊(n − i × base) / base²⌋ で一括して求まります。
この数え方により、ペアを 1 つずつ検証することなく、条件を満たすペアの総数を効率的に計算できます。
アルゴリズムの手順
- count を 0 で初期化します。
- sqr を「n の平方根の整数部分 + 1」とします。
- base を 2 から sqr − 1 まで動かしながら、以下を繰り返します。
- i を 1 以上 min(base, ⌊n / base⌋ − base + 1) 未満の範囲で動かします。
- gcd(base, i) が 1 でない場合は、次の i へスキップします。
- count に ⌊(n − i × base) / (base × base)⌋ を加算します。
- count を返します。
Pythonでの実装例
理解を深めるために、以下の実装を見てみましょう。
from math import sqrt, gcd
def solve(n):
count = 0
sqr = int(sqrt(n)) + 1
for base in range(2, sqr):
for i in range(1, min(base, n // base - base + 1)):
if gcd(base, i) != 1:
continue
count += (n - i * base) // (base * base)
return count
n = 12
print(solve(n))
入力
12
出力
3
処理の流れ(n = 12 の場合)
- sqr = int(√12) + 1 = 3 + 1 = 4 となるため、base の候補は 2 と 3 です。
- base = 2 のとき:i = 1 のみが対象。gcd(2, 1) = 1 なので、count += (12 − 2) // 4 = 2(x = 6 と x = 10 に対応)。
- base = 3 のとき:i = 1 のみが対象。gcd(3, 1) = 1 なので、count += (12 − 3) // 9 = 1(x = 12 に対応)。
- 最終的に count = 3 が出力され、期待どおりの結果が得られます。
-
Pythonで直角三角形の斜辺中点と底辺がなす角度を求める方法
問題概要直角三角形ABCがあり、2つの辺ABとBCの長さが与えられているとします。斜辺ACの中点をMとしたとき、点Mと底辺BCがなす角度を求めるのがこの記事の目的です。例として ab = 6、bc = 4 を入力すると、出力は 56.309932474020215 になります。これは arctan(6/4) ≒ 0.9828 ラジアンであり、これを度数法に変換すると約 56.31 度になるためです。数学的な背景直角三角形では、斜辺の中点Mは外心、つまり3つの頂点から等しい距離にある点になります。したがって MB = MC が成り立ち、三角形MBCは二等辺三角形です。このことから、求めたい角度
-
Pythonで二分木のノードとその子孫の最大絶対差を求めるプログラム
問題概要 二分木が与えられたとき、任意のノードとその子孫との間の絶対差の最大値を求めることを考えます。 例えば、次のような二分木が入力として与えられた場合を考えてみましょう。 この場合、ノード8とノード1の間の差が最も大きくなるため、出力は 7 となります。 解法のアプローチ:DFSを使った追跡 この問題は、DFS(深さ優先探索)を用いることで効率的に解けます。各ノードについて「その部分木内の最小値」と「最大値」を追跡しながら、現在のノードの値との差を順次更新していくのがポイントです。 具体的な手順は以下の通りです。 dfs() 関数を定義します。引数としてノードを受け取ります。 ノード