Pythonで辞書式順序の最初のn個の数を生成するプログラム
はじめに
数値 n が与えられたとき、「辞書式順序(lexicographic order)」に従って並べ替えられた最初の n 個の数を求めることを考えます。辞書式順序とは、数値を文字列として扱い、左から1文字ずつ比較して並べる方式のことです。
例えば、入力が n = 15 の場合、出力は次のようになります。
[1, 10, 11, 12, 13, 14, 15, 2, 3, 4, 5, 6, 7, 8, 9]
これは通常の数値の大小順(1, 2, 3, …)ではなく、「1」の次に「10」「11」「12」と続く、文字列として比較したときの順序になっている点に注目してください。
解決アプローチ
この問題は、次の手順で解くことができます。
- 変数 count を 1 で初期化します
- 答えを格納するリスト ans を、count(=1)のみを含む状態で作成します
- ans の要素数が n 未満である間、以下を繰り返します
- count を 10 倍します
- count が n より大きい間、以下を繰り返します
- count を 10 で割った商に更新します
- count に 1 を加えます
- count を 10 で割った余りが 0 である間、さらに count を 10 で割った商に更新します
- count を ans の末尾に追加します
- 最後に ans を返します
実装例
以下がPythonでの実装例です。
class Solution:
def solve(self, n):
count = 1
ans = [count]
while len(ans) < n:
count *= 10
while count > n:
count //= 10
count += 1
while count % 10 == 0:
count //= 10
ans.append(count)
return ans
ob = Solution()
n = 15
print(ob.solve(n))
入力
15
出力
[1, 10, 11, 12, 13, 14, 15, 2, 3, 4, 5, 6, 7, 8, 9]
アルゴリズムのポイント
このアルゴリズムの動きを簡単に整理すると、次のとおりです。
- まず 1 から始めて、可能な限り桁を増やしていきます(1 → 10 → 100 …)。これにより「1」の直後に来るべき「10」系の数を先に生成できます。
- count が n を超えたら、一度 10 で割って桁を戻し、1 を足すことで次の数へ進みます。
- 末尾が 0 の数(例:20、30)は辞書式順序の途中には現れないため、余りが 0 である限り桁を削り落とします。
- この操作を繰り返すことで、ソート処理なしに辞書式順序の数列を直接構築できます。
計算量
各ステップで定数回の演算しか行わないため、全体の時間計算量は O(n) です。n 個の数を生成してからソートする方法(O(n log n))よりも効率的で、追加のメモリも結果リスト以外ほぼ不要です。
-
Pythonで配列の反転数(転倒数)をカウントする方法
はじめに この記事では、配列内の反転(インバージョン)をカウントする問題とその解決策について詳しく解説します。 問題定義 問題: リストが与えられたとき、その中に含まれる反転の数をカウントして表示します。 反転数とは、配列を昇順にソートされた状態にするために必要な入れ替え(スワップ)の回数を表す指標です。具体的には、i < j かつ arr[i] > arr[j] を満たす要素のペア(i, j)の総数として定義されます。 実装例 # 反転数をカウントする関数 def InvCount(arr, n): inv_count = 0 for i in range(n
-
Pythonで複素数を扱う方法:complex型の基本からcmathモジュールまで
複素数の基礎知識 正の数には必ず2つの実数平方根が存在します。たとえば x² = 25 のとき、x = ±5 です。しかし x² = -25 のような場合は実数解が存在しません。負の数の平方根は、絶対値の平方根に虚数単位 j = √−1 を掛けた形で定義されます。 したがって、√−25 = √25 × √−1 = 5j となります。 複素数は実部と虚部から構成され、「x + yj」という形式で表されます。x と y はどちらも実数であり、y に虚数単位 j を掛けた部分が虚部となります。 記述例:3+2j、10-5.5J、9.55+2.3j、5.11e-6+4j Pythonにおける複素数型