Pythonで最小の「良い基数(グッドベース)」を二分探索で求める方法
問題の概要
整数 n が与えられたとき、n を k 進法で表した際にすべての桁が 1 になるような k(k ≥ 2)を「良い基数(グッドベース)」と呼びます。数値 n が文字列として与えられるので、最小の良い基数を文字列として返します。
例えば n = 121 の場合、答えは 3 です。121 を 3 進法で表すと 11111 となり、すべての桁が 1 になるためです(実際に 1 + 3 + 9 + 27 + 81 = 121 が成り立ちます)。
解法のアプローチ
この問題は二分探索を用いることで効率的に解けます。考え方のポイントは次の通りです。
- 基数
k・桁長lengthのときの「全桁が 1 の数」は、等比級数の和1 + k + k² + … + k^(length−1)として表せる。 - この和が
nと一致するようなkを、桁長ごとに二分探索で求める。 - 桁長が長いほど対応する基数は小さくなるため、桁長を大きい方(64)から小さい方へ順に調べれば、最初に見つかったものが最小の良い基数となる。
- どの桁長でも見つからない場合は
n − 1を返せばよい。nを(n − 1)進法で表すと必ず11になるため、これが常に解となるからである。
具体的な手順
- getSum(x, length) メソッドを定義する
mainSum := 0、temp := 1と初期化するiを 0 からlength − 1まで繰り返す:mainSum += tempのあとにtemp *= xmainSumを返す
- check(n, length) メソッドを定義する
low := 2、high := nとするhigh ≥ lowの間、以下を繰り返す:mid := low + (high − low) // 2mainSum := getSum(mid, length)mainSum == nならmidを返すmainSum > nならhigh := mid − 1- それ以外は
low := mid + 1
- 見つからなければ
-1を返す
- メイン処理(smallestGoodBase)
- 入力文字列
nを整数に変換する iを 64 から 2 まで降順にループし、x = check(n, i)を計算する。x ≥ 2であれば文字列に変換して返す- ループが終了しても見つからなければ、
str(n − 1)を返す
- 入力文字列
以下の実装例を見ると、より理解が深まるでしょう。
実装例
class Solution(object):
def getSum(self, x, length):
# 1 + x + x^2 + ... + x^(length-1) を計算する
mainSum = 0
temp = 1
for _ in range(length):
mainSum += temp
temp *= x
return mainSum
def check(self, n, length):
# 二分探索で n に一致する基数を探す
low, high = 2, n
while high >= low:
mid = low + (high - low) // 2
mainSum = self.getSum(mid, length)
if mainSum == n:
return mid
elif mainSum > n:
high = mid - 1
else:
low = mid + 1
return -1
def smallestGoodBase(self, n):
n = int(n)
# 桁長が長いほど基数は小さくなるため、大きい桁長から順に調べる
for i in range(64, 1, -1):
x = self.check(n, i)
if x >= 2:
return str(x)
return str(n - 1)
ob = Solution()
print(ob.smallestGoodBase("121"))
入力
"121"
出力
3
補足:なぜ 3 が最小なのか
2 進法で全桁が 1 になる数は 2^m − 1 の形に限られます。2^6 − 1 = 63、2^7 − 1 = 127 であり、121 には該当しないため、基数 2 は使えません。したがって、全桁が 1 になる最小の基数は 3 となります。
計算量について
想定される入力は 64 ビット整数(最大 10^18 程度)であるため、必要な桁長は最大でも 64 程度です。各桁長に対する二分探索は O(log n) ステップ程度で収まるため、アルゴリズム全体としても非常に高速に動作します。
-
【Python】二分探索木(BST)からK番目に小さい要素を求める方法
二分探索木(BST:Binary Search Tree)が与えられたとき、その木の中でK番目に小さい要素を見つける問題を考えてみましょう。例として、次のような二分探索木があるとします。この木から3番目に小さい要素を求めたい場合は k = 3 となり、求める結果は 7 になります。解法のアプローチこの問題は、二分探索木の重要な性質を利用することで簡単に解けます。それは、BSTを中順走査(in-order traversal)すると、値が昇順に並んだリストが得られるというものです。具体的には、以下の手順で解きます。結果を格納するための空のリスト nodes を作成するsolve(root, no
-
Pythonで2次元配列からk番目に小さい要素を求めるプログラム
n×n のユーザー入力による整数行列と値 k が与えられたとき、その2次元配列の中から「k番目に小さい要素」を見つけるのが本記事の目的です。この問題は、Python標準ライブラリの heapq モジュールを使うことで効率的に解決できます。 heapqモジュールとは heapq(ヒープキュー)は、Pythonでヒープ構造(優先度付きキュー)を扱うためのモジュールです。このモジュールの特徴は、毎回ヒープの中で最も小さい要素が取り出される「最小ヒープ(min heap)」として動作する点にあります。また、nsmallest() メソッドを使うことで、データ集合の中から最小のn個の値を簡単に取得するこ