Pythonで挿入ソート時に必要なシフト(移動)回数を求めるプログラム
配列が与えられ、それに対して挿入ソートを実行することを考えてみましょう。挿入ソートでは、配列内の各要素を順番に取り出し、正しい位置まで移動(シフト)させていきます。この記事では、配列をソートし終えるまでに必要なシフトの総数を求めるプログラムを紹介します。シフトの総数は整数値となり、すでにソート済みの配列に対しては 0 を返します。
たとえば、入力が input_array = [4, 5, 3, 1, 2] の場合、出力は 8 になります。
[4, 5, 3, 1, 2] → 0 シフト [4, 5, 3, 1, 2] → 0 シフト [3, 4, 5, 1, 2] → 2 シフト [1, 3, 4, 5, 2] → 3 シフト [1, 2, 3, 4, 5] → 3 シフト
したがって、シフトの総数は 0 + 0 + 2 + 3 + 3 = 8 となります。
解法のアプローチ
この問題は、転倒数(逆順になっているペアの数)を求める問題として捉えることができます。挿入ソートで要素が移動される回数は、配列中の転倒数と一致するためです。以下の手順で効率的に計算していきます。
length:= 入力配列 input_arr のサイズtemp_arr:= サイズ 1000001 のリストを作成し、すべて 0 で初期化(BIT=フェニック木として機能します)ans:= 0- input_arr の各要素について、次の処理を行う:
val:= 要素の値- val > 0 の間、次を繰り返す:
ans := ans + temp_arr[val]val := val - (val AND -val)
val:= 要素の値- val <= 1000000 の間、次を繰り返す:
temp_arr[val] := temp_arr[val] + 1val := val + (val AND -val)
ans:= 全ペアの総数「length × (length − 1) ÷ 2」から ans を引いた値ansを返す
ここで登場する val & -val という演算は、Binary Indexed Tree(フェニック木)特有のテクニックで、値の最下位ビットを取り出すことで累積和の更新・参照を O(log m) で行えます。そのため、単純な二重ループによる O(n²) の計算よりもはるかに高速に転倒数を求められます。なお、この実装は要素が 1 以上 1000000 以下の正の整数であることを前提としている点に注意してください。
実装例
理解を深めるために、以下の実装を見てみましょう。
def solve(input_arr):
length = len(input_arr)
temp_arr = [0] * 1000001
ans = 0
for item in input_arr:
val = item
while val > 0:
ans += temp_arr[val]
val -= val & -val
val = item
while val <= 1000000:
temp_arr[val] = temp_arr[val] + 1
val += val & -val
ans = length * (length - 1) // 2 - ans
return ans
print(solve([4, 5, 3, 1, 2]))
入力
[4, 5, 3, 1, 2]
出力
8
-
Pythonで配列(リスト)の合計を求める方法をわかりやすく解説
この記事では、配列(リスト)の合計値を求めるという問題に対して、Pythonでの解決策とアプローチをわかりやすく解説します。 問題の定義 配列が入力として与えられたとき、その配列に含まれるすべての要素の合計を計算することを目標とします。 例えば、[1, 2, 3, 4, 5] という配列が与えられた場合、出力は 15 になります。 アプローチ1:ループを使った素朴な方法(総当たり法) 最も基本的な方法は、リストを先頭から順に走査し、各要素を合計用の変数に加算していくやり方です。手順は以下の通りです。 合計を格納する変数を 0 で初期化します。 for ループでリストの各要素を取り出し、順番に
-
Pythonで挿入ソート(Insertion Sort)を実装する方法:アルゴリズムとサンプルコードを徹底解説
この記事では、Python 3.x(およびそれ以前のバージョン)における挿入ソートの実装方法について詳しく解説します。挿入ソートは、トランプの手札を整理するイメージに近い、直感的で理解しやすいソートアルゴリズムです。挿入ソートのアルゴリズム挿入ソートは以下の手順で動作します。入力要素を順番に走査し、各反復ごとにソート済みの配列部分を少しずつ拡張していきます。現在注目している要素(キー)を、ソート済み部分の中で最も大きい値と比較します。キーがその値より大きければ、要素は元の位置のまま次の要素へ進みます。そうでなければ、ソート済み配列内の正しい位置を探し出し、そこへ移動させます。具体的には、ソート