プログラマーの徒然ブログ

プログラミングに関することをはじめ、興味がでたものを雑多に!

【Python】ソートの復讐1:バブルソート

ソートの復習用の記事です。

最初なので実装も簡単なバブルソートについて書きます。

ひとまず結論

  • 最悪・平均計算量: O(n2)
  • Pythonでの実装例
def bubble_sort(input_arr):
    len_arr = len(input_arr)
    update_flag = True
    while update_flag:
        update_flag = False
        for i in range(1,len_arr):
            if input_arr[i-1] > input_arr[i]:
                input_arr[i-1], input_arr[i] = input_arr[i], input_arr[i-1]
                update_flag = True
  
    return input_arr

バブルソート

  • 入力配列を隣接要素の比較・入れ替え操作でソートするアルゴリズム
  • 名前の由来:比較・入れ替え操作により大きい/小さい要素が浮かび上がるようにみえるから(バブル:泡のように)
  • 利点:アルゴリズムが単純 → 実装が容易
  • 欠点:平均計算量が大きい(要素数がNの場合、最大でN2の操作が必要)
  • アルゴリズムの内容:手順(小さい順に並べる場合)
    • 入力配列をin、入力配列サイズをNとする
      1. i = 0とする
      2. in[i]とin[i+1]の値を比較
      3. もし、in[i] > in[i+1]ならば、値の入れ替えを実施
      4. iをインクリメント
      5. i < N ならば操作2に戻る
      6. 操作2-5で、入れ替えを行った場合は操作1に戻る
      7. 終了
  • N=5の配列に対して、バブルソートを実行すると以下図のような処理の流れになる

f:id:t49m1:20200531133804p:plain
バブルソート例(サイズ5)

Pythonでの処理速度

  • 計測環境
    • ASUS ZenBook UX390U (ノートPC)
      • Intel(R) Core(TM) i5-7200U CPU @ 2.50GHz
  • 下記コードを評価に使用
  • 配列サイズNを変化させて評価
import numpy as np
import time

def bubble_sort(input_arr):
    len_arr = len(input_arr)
    update_flag = True
    while update_flag:
        update_flag = False
        for i in range(1,len_arr):
            if input_arr[i-1] > input_arr[i]:
                input_arr[i-1], input_arr[i] = input_arr[i], input_arr[i-1]
                update_flag = True

    return input_arr


N=10
input_arr = np.random.rand(N)

st = time.time()
result_arr = bubble_sort(input_arr)
elapsed = time.time() - st

print("Elapsed time[sec] : ", elapsed)

評価結果

配列サイズ 100 1000 10000
処理時間[sec] 0.009 1.153 48.133

平均計算量が配列サイズの2乗に比例するため、配列サイズの増加量以上に処理時間が伸びている。

サイズ100→1000のときは、サイズが10倍になって処理時間が100倍になっており計算量の増加にあった処理時間の増加である。

ただし、入力配列がソートされた状態に近いほど走査回数は少なくなるため速く完了する。

まとめ

今回は、実装が簡単なバブルソートを復習しました。

関数実装は簡単ですが、評価結果からもわかるように、配列サイズの増加に対する処理時間の増加は恐ろしいものです。

配列サイズが大きいときは、別のアルゴリズムを選択するべきです。

配列サイズがちいさいときには、便利なソートアルゴリズムではないでしょうか?

以上、ありがとうございました。

【Python】総和計算は、自作が速い!?

基本的な処理でもある総和計算、Pythonだと組み込み関数やNumPyの関数などいろいろ関数で求めることができます。

結局、どれが速いのか?どれを使えばいいのか?知らなかったので、 外出自粛期間の暇つぶしに、比較してみました。

総和計算とは

  • 複数の入力値の合計を求める処理
    • 入力: 一日の出費(1週間) → 総和計算の結果:一週間の出費
¥1,200 ¥300 ¥2,500 ¥800 ¥3,000 ¥0 ¥5,000
↓ 総和計算 ↓
総和結果(一週間の出費)
¥12,800

既存関数の処理速度比較

単純なforループの実装を基準に、Pythonの組み込み関数sumと、 数値計算のための拡張モジュールNumpPyのsum関数の速度を評価します。

基準:forループによる総和計算

sum_val = 0    # 総和結果を格納する変数
for item in input_arr:    # input_arrは複数の入力を格納している配列
    sum_val += item

組み込み関数sumによる総和計算

# sum_valに総和結果が格納される
sum_val = sum(input_arr)

NumPyのsum関数による総和計算

import numpy as np
# sum_valに総和結果が格納される
sum_val = np.sum(input_arr)

計測プログラム

import time
import numpy as np

# Generate an input array
N = 1024* 1024 * 256         # The number of elements
input_arr = np.random.rand(N)

print('Init : done (# of elements : ', N, ' )\n', flush=True)

print('###### Simple loop summation ######')
st = time.time()
sum_val = 0
for item in input_arr:
    sum_val += item
elapsed_time = time.time() - st

print('\tSummation result\t: ', sum_val)
print('\tElapsed time[sec]\t: ', elapsed_time)

print('###### Default sum function ######')
st = time.time()
sum_val = sum(input_arr)
elapsed_time = time.time() - st

print('\tSummation result\t: ', sum_val)
print('\tElapsed time[sec]\t: ', elapsed_time)

print('###### NumPy sum function ######')
st = time.time()
sum_val = np.sum(input_arr)
elapsed_time = time.time() - st

print('\tSummation result\t: ', sum_val)
print('\tElapsed time[sec]\t: ', elapsed_time)

処理時間比較

  • 計測環境
    • ASUS ZenBook UX390U (ノートPC)
      • Intel(R) Core(TM) i5-7200U CPU @ 2.50GHz
  • 計測条件
    • 入力要素数:256M(=256 * 1024 * 1024)
    • 入力配列:NumPy配列
関数 処理時間[sec] 高速化率(基準の何倍速いか)
基準:forループ実装 143.377 -
組み込み関数sum 64.546 2.22
NumPyのsum関数 0.144 995.67

結局、簡単に思いつく既存関数だとNumPyが爆速!

基準としたPythonのforループ実装の約1000倍速い!

とりあえず、総和計算が出てきたらNumPyのsum関数に渡せば問題ないということがわかりました!

NumPyの大半が、高速処理のためにC言語ベースで開発されているから当たり前の結果か、、

NumPyの総和計算速度に追いつく

forループの実装をベースに、自作関数でNumPyの総和計算速度に追いつくために頑張ります。

まずは、Pythonとの別れ

Pythonで実装していては、C言語で書かれた関数と同程度の速度を出すことは困難です。

そのため、forループ実装の部分を関数として切り出し、 JITコンパイルが可能であるNumbaを使って高速に処理ができる機械語に変換します。

t49m1.hatenablog.com

1 . 関数として書き直す

def summation(input_arr):
    sum_val = 0
    for item in input_arr:
        sum_val += item
    return sum_val

2 . Numbaの適用

import numba 
@numba.njit
def summation(input_arr):
    sum_val = 0
    for item in input_arr:
        sum_val += item
    return sum_val

NumPyとNumbaの比較

一回目の評価結果に追記する形式で行う。

  • 計測環境
    • ASUS ZenBook UX390U (ノートPC)
      • Intel(R) Core(TM) i5-7200U CPU @ 2.50GHz
  • 計測条件
    • 入力要素数:256M(=256 * 1024 * 1024)
    • 入力配列:NumPy配列
関数 処理時間[sec] 高速化率(基準の何倍速いか)
基準:forループ実装 143.377 -
組み込み関数sum 64.546 2.22
NumPyのsum関数 0.144 995.67
forループ実装+Numba 0.354 405.01

Numbaを使うだけで、NumPyのsum関数の40%ぐらいの性能になりました!

あと、2.5倍ぐらいするとNumPyのsum関数に勝てます!

やはり、速度を出すためにはコンパイルすることは重要ですね。

処理の並列化(マルチコアの活用)

現在のCPUは、内部に複数の演算器(コア)を持っています。

Pythonとかで単純にコードを書いて実行すると、 1コアしか使いません。

しかし、このコアを2、4個と複数利用できるようにコードを書くと 理想的には処理時間が1/2、1/4になります。

NumPyは、もちろん、複数のコアを利用できるように書かれています。

そのため、さきほどNumba化した関数もマルチコアが活かせるように 並列処理に書きかえます。

Numbaの機能の中に、処理の並列化もあるため、結構簡単に並列処理に変更できます。

  • 並列処理化
@numba.njit(parallel=True)
def summation_parallel( input_arr ):
    sum_val = 0
    for i in numba.prange(len(input_arr)):
        sum_val += input_arr[i]
    return sum_val

前回からの変更点 - デコレータ@numba.jit@numba.njit(parallel=True)に変更 - 関数内のfor文のレンジを表す部分をnumba.prange(len(input_arr))に変更 - 並列処理を行うfor文は、prangeを使う必要があるみたい、、

NumPyとNumbaの比較

一回目の評価結果に追記する形式で行う。

  • 計測環境
    • ASUS ZenBook UX390U (ノートPC)
      • Intel(R) Core(TM) i5-7200U CPU @ 2.50GHz
  • 計測条件
    • 入力要素数:256M(=256 * 1024 * 1024)
    • 入力配列:NumPy配列
関数 処理時間[sec] 高速化率(基準の何倍速いか)
基準:forループ実装 143.377 -
組み込み関数sum 64.546 2.22
NumPyのsum関数 0.144 995.67
forループ実装+Numba 0.354 405.01
forループ実装+Numba(parallel) 0.093 1541.68

Numbaの並列処理機能を利用すると、NumPyの総和計算より速くなりました!

(今回使ったPCに限った話かも、、、NumPyが遅いとは信じがたい、、、使っているコンパイラの違いとか??)

なんにせよ!

NumPyと同等以上の速度の総和計算関数が作れたので満足です!

まぁ、実際使うなら、NumPyのほうが簡単に使えるのでNumPyで十分な気がします!

最後に、計測に使ってプログラム(+Numba)

import time
import numpy as np
import numba

@numba.njit
def summation( input_arr ):
    sum_val = 0
    for item in input_arr:
        sum_val += item
    return sum_val

@numba.njit(parallel=True)
def summation_parallel( input_arr ):
    sum_val = 0
    for i in numba.prange(len(input_arr)):
        sum_val += input_arr[i]
    return sum_val

N = 1024* 1024 * 256
input_arr = np.random.rand(N)

print('Init : done (# of elements : ', N, ' )\n', flush=True)

print('###### Simple loop summation ######')
st = time.time()
sum_val = 0
for item in input_arr:
    sum_val += item
elapsed_time = time.time() - st

print('\tSummation result\t: ', sum_val)
print('\tElapsed time[sec]\t: ', elapsed_time)

print('###### Default sum function ######')
st = time.time()
sum_val = sum(input_arr)
elapsed_time = time.time() - st

print('\tSummation result\t: ', sum_val)
print('\tElapsed time[sec]\t: ', elapsed_time)

print('###### Numpy sum function ######')
st = time.time()
sum_val = np.sum(input_arr)
elapsed_time = time.time() - st

print('\tSummation result\t: ', sum_val)
print('\tElapsed time[sec]\t: ', elapsed_time)

print('###### Simple loop summation with Numba ######')
sum_val = summation(np.ones(1))
st = time.time()
sum_val = summation(input_arr)
elapsed_time = time.time() - st

print('\tSummation result\t: ', sum_val)
print('\tElapsed time[sec]\t: ', elapsed_time)

print('###### Simple loop summation with Numba + parallel ######')
sum_val = summation_parallel(np.ones(1))
st = time.time()
sum_val = summation_parallel(input_arr)
elapsed_time = time.time() - st

print('\tSummation result\t: ', sum_val)
print('\tElapsed time[sec]\t: ', elapsed_time)

【Counting 1-bits】高速にビット列の1を数える!

外出自粛中に、急に気になった"ビット列の1を数える"アルゴリズムの忘却録!

ビットカウント:ビット列の1を数えるとは?

population countなんかともいわれる処理で、やりたいことは簡単!

整数を2進数としてみたときに、1の出現回数を数えること!

(本当のことを書くと、レジスタに格納された64-bitだったり、32-bitだったりするビット列の中に1が出現する回数を得る処理かな、、)

例:8までの整数のビットカウント

10進数表現 2進数表現 population count結果
0 0000 0
1 0001 1
2 0010 1
3 0011 2
4 0100 1
5 0101 2
6 0110 2
7 0111 3
8 1000 1

最初に、結論

手書きするなら、以下のコードが速い!

(下記コードはPythonで書いたもの)

def bit_count( number ):
    number = (number & 0x55555555) + (number >> 1 & 0x55555555)
    number = (number & 0x33333333) + (number >> 2 & 0x33333333)
    number = (number & 0x0f0f0f0f) + (number >> 4 & 0x0f0f0f0f)
    number = (number & 0x00ff00ff) + (number >> 8 & 0x00ff00ff)
    return (number & 0x0000ffff) + (number >>16 & 0x0000ffff)

ビット演算で、分割統治法的な処理で、固定回数で32-bitの整数のビットカウントができ、高速!

複数アルゴリズムの速度比較

簡単に思いつくアルゴリズム~結論のアルゴリズムまで、3種類を比較

Straightforward method

まず、思いつくのは以下のコードでしょう。

単純に

  1. 最下位ビットが1である場合、カウントアップ
  2. 入力値を1ビット右シフト

を入力値が0になるまでループする。

count = 0
while bit_string != 0:
    if bit_string&1 :
        count += 1
    bit_string = bit_string >> 1

Improved straightforward method

2つ目は、1つ目の処理を改善した方法である。

1つ目の方法では、1000000000011のように間に0が続く場合でも、 "1"なのか"0"なのか1つずつ確認してしまう。

2つ目の下記コードで示す方法では、連続で"0"が続く場合、一気にスキップできるため、1つ目の方法より高速になる。

しかし、1111111のように、"1"が続く入力だとあまり速度が変わらない。

count = 0
while bit_string != 0:
    count += 1
    bit_string = bit_string & (bit_string - 1)

Optimized method

ビット演算でカウントするアルゴリズム

ループを使わず、どんな入力でも固定回数の操作でカウントできる!

画期的なアルゴリズムです。

工学部の方とかで、ビット演算を知っている方は、図示してみるとアハ体験になると思います。

def bit_count_numba( number ):
    number = (number & 0x55555555) + (number >> 1 & 0x55555555)
    number = (number & 0x33333333) + (number >> 2 & 0x33333333)
    number = (number & 0x0f0f0f0f) + (number >> 4 & 0x0f0f0f0f)
    number = (number & 0x00ff00ff) + (number >> 8 & 0x00ff00ff)
    return (number & 0x0000ffff) + (number >>16 & 0x0000ffff)

速度比較

それぞれのアルゴリズムPythonで速度比較します。

ついでに、3つ目の方法を関数にしてnumba化してさらに高速化します! (最初のコンパイル時間を省略するために計測外で一回実行するチートをしています。。。)

numbaについては、以下の記事をどうぞ!

t49m1.hatenablog.com

  • 計測環境
    • ASUS ZenBook UX390U (ノートPC)
      • Intel(R) Core(TM) i5-7200U CPU @ 2.50GHz
  • 計測プログラム
import time
from numba import njit

def bit_count( number ):
    number = (number & 0x55555555) + (number >> 1 & 0x55555555)
    number = (number & 0x33333333) + (number >> 2 & 0x33333333)
    number = (number & 0x0f0f0f0f) + (number >> 4 & 0x0f0f0f0f)
    number = (number & 0x00ff00ff) + (number >> 8 & 0x00ff00ff)
    return (number & 0x0000ffff) + (number >>16 & 0x0000ffff)

@njit
def bit_count_numba( number ):
    number = (number & 0x55555555) + (number >> 1 & 0x55555555)
    number = (number & 0x33333333) + (number >> 2 & 0x33333333)
    number = (number & 0x0f0f0f0f) + (number >> 4 & 0x0f0f0f0f)
    number = (number & 0x00ff00ff) + (number >> 8 & 0x00ff00ff)
    return (number & 0x0000ffff) + (number >>16 & 0x0000ffff)


N = 1048576

st = time.time()
count = 0
for bit_string in range(N):
    while bit_string != 0:
        if bit_string&1 :
            count += 1
        bit_string = bit_string >> 1

elapsed_time = time.time() - st

print('Count: ', count)
print('Elapsed time[sec] (Straghtforward) : ', elapsed_time)


st = time.time()
count = 0
for bit_string in range(N):
    while bit_string != 0:
        count += 1
        bit_string = bit_string & (bit_string-1)

elapsed_time = time.time() - st

print('Count: ', count)
print('Elapsed time[sec] (Improved straghtforward) : ', elapsed_time)

st = time.time()
count = 0
for bit_string in range(N):
    count += bit_count( bit_string )

elapsed_time = time.time() - st

print('Count: ', count)
print('Elapsed time[sec] (Optimized) : ', elapsed_time)

st = time.time()
count = 0
for bit_string in range(N):
    while bit_string != 0:
        if bit_string&1 :
            count += 1
        bit_string = bit_string >> 1

elapsed_time = time.time() - st

print('Elapsed time[sec] (Straghtforward) : ', elapsed_time)

st = time.time()
count = 0
for bit_string in range(N):
    while bit_string != 0:
        count += 1
        bit_string = bit_string & (bit_string-1)

elapsed_time = time.time() - st

print('Elapsed time[sec] (Improved straghtforward) : ', elapsed_time)

st = time.time()
count = 0
for bit_string in range(N):
    count += bit_count( bit_string )

elapsed_time = time.time() - st

print('Elapsed time[sec] (Optimized) : ', elapsed_time)

bit_count_numba( 0 )
st = time.time()
count = 0
for bit_string in range(N):
    count += bit_count_numba( bit_string )

elapsed_time = time.time() - st

print('Elapsed time[sec] (Optimized + numba) : ', elapsed_time)
結果
アルゴリズム 処理時間[sec]
Straightforward method 4.581
Improved straightforward method 2.794
Optimized method method 1.030
Optimized method + numba 0.341

やはり、固定回数のビット演算で行えるだけあって、3つ目の方法が一番速いですね!

そして、Pythonは遅いですね。

numbaを使うことで3倍速くなりました。 つまり、ほかの方法もnumbaを使うことで1秒を切れそうですね。

今回は、Pythonのループ処理とかを速くする話ではないのでこの辺で!

【Python】リストの初期化は[None]*n

Pythonでリストは頻繁に使うと思います。

固定長のリストの初期化は、どの方法が速いのか気になったので検証してみました。

ひとまず結論

list1 = [None] * N

この書き方が速い!(N:リスト長)

速度検証を行う記法

検証対象は以下の3通り

パターン1

list1 = []
for i in range(N):
    list1.append(None)

pythonを習うと最初にかけるようになるパターンかなと思い、検証パターンに採用した。

十中八九、遅いと思われる噛ませ犬。

パターン2

list2 = [None for _ in range(N) ]

リストの生成は、このワンライナーが最も速いと思っていた。。。

パターン3

list3 = [None] * N

少し前に、この記法を知りました。

速度検証

テストコード

import time

# num of elements in list
N = 10000000

st = time.time()
list1 = []
for i in range(N):
    list1.append(None)
elapsed1 = time.time() - st

st = time.time()
list2 = [ None for _ in range(N)]
elapsed2 = time.time() - st

st = time.time()
list3 = [None] * N
elapsed3 = time.time() - st

print("Elapsed (Empty): ", elapsed1, " sec")
print("Elapsed (One liner): ", elapsed2, " sec")
print("Elapsed (Mul): ", elapsed3, " sec")

結果

  • 検証環境
    • ASUS ZenBook UX390U (ノートPC)
      • Intel(R) Core(TM) i5-7200U CPU @ 2.50GHz
パターン 実行時間[sec]
1 0.9249
2 0.3912
3 0.0912

はい![None] * Nが爆速でした!

まとめ

リストを固定長で初期化するときは、[None] * Nの一択!

【Python】イテレータを知ってみる

Pythonにあるイテレータiterator)について書こうと思います。

存在は知っていましたが、使ったことがなかったのでよく知らない機能でした。

外出自粛中なので、これを機に使いこなせるようになろうと調べました。

イテレータ

反復して要素を取り出すことのできるインターフェイス

また、イテレータ化されたオブジェクトをイテレータオブジェクトと呼ぶそうです。

特徴

  1. オブジェクトのイテレータ化には__iter__()メソッドが必要
  2. イテレータオブジェクトは__next__()メソッドが呼ばれる度に新しい値(次の値)を返す
  3. __next__()メソッドは、最後の値を返したあとStopIteration例外を返すようになる
    • 自作するときは、終了条件を作成するときはStopIterationを返すように作ること

__iter__()メソッドをもつオブジェクト例

Pythonの既存オブジェクトの中で、以下のオブジェクトは__iter__()メソッドを持ちます。 代表例なので、他にもあると思います。

  • list
  • tuple
  • set
  • dict

確認方法:イテレータオブジェクトか?

オブジェクトをobjとすると、下記のコードで確認することができます。

 hasattr(obj, '__iter__')

イテレータオブジェクトは何ができる?

ここが一番知りたいところかと思います。 しかし、多くの人が使っているんです。

最たるものが、次の例です。

例:forループ + リスト

nums = list((1,2,3))

for i in nums:
    print(i)

結果

1
2
3

上記例のように、for _ in リストの形式でforループをよく作るんじゃないでしょうか?

これが、イテレータオブジェクトを使っている例です。

forループに入るときに、リストのnumsからイテレータオブジェクトが生成されます。

イテレータオブジェクトに対して__next__()を繰り返して、要素を一つずつ取り出します。

そして、StopIteration例外の発生でforループを終了します。

この例を__iter__()メソッドと__next__()メソッドを明にして書くと、以下のコードになります。

nums = [1,2,3]

nums_iter = nums.__iter__()
while True:
    try:
        tmp = nums_iter.__next__()
    except StopIteration:
        break
    print(tmp)

結果

1
2
3

明に書くと、__iter__()メソッドと__next__()メソッドの意味が分かりやすくなったでしょうか? しかし、例外処理が必要になるのでコードが長くなってしまいます。 forループ+リストを使ったほうが簡潔で読み易いです。

そのほかに、イテレータオブジェクトが潜んでいそうな場所

  • 集合を扱う関数(set, max, .. etc)

forループの例以外にも、集合内の最大値を算出するmax関数などは、引数のオブジェクトをイテレータ化し処理を実行します。

つまり、イテレータ化できるようにした自作のオブジェクトも渡すことができます。


今回はここまで。

イテレータPythonを書いている人の多くが意識せずに使っているものでした。

次は、よく出てくるジェネレータについて調べたいと思ってます。。

では!

【caps2ctrl】CapsLockキーは邪魔!Ctrlキーに変える!(windows10)

皆さんは、CapsLockキーって使ってます?

何それって人も多いんじゃないですかね? 赤枠で囲ってるキーボードのaの左のキーです。

f:id:t49m1:20200329004757p:plain

私にとっては、たまたま指が当たって半角になったりして、イラッとさせるだけのキーです。

今回は、このCaps LockキーをCtrlキーに変更する方法を紹介します。

変更は簡単で、以下の3ステップで 問題なければ数分で終わります。

(デバイスの設定を変更する行為なので、自己責任でお願いします。)

1. レジストリ変更のためのファイル作成(caps2ctrl.reg)

下記内容の.regファイルを作成してください。 私は、caps2ctrl.regとしましたが好きな名前で構いません。

Windows Registry Editor Version 5.00
 
[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Keyboard Layout]
"Scancode Map"=hex:00,00,00,00,00,00,00,00,02,00,00,00,1d,00,3a,00,00,00,00,00

2. caps2ctrl.regを実行

作成した.regファイルをダブルクリックしてください。

すると、ユーザーアカウント制御から、

デバイスに変更を加えることを許可しましか?

と聞かれるので、はいを押してください。

その後、レジストリエディターからWarningが表示されます。

信頼しない場合は、レジストリに追加しないでください。継続しますか?

と聞かれるので、はいを押してください。

問題なければ、

正常に追加されました。

と表示されます。

3. 再起動

正常に追加されたら、再起動してください。

以上で、設定終了です。

メモ帳等でCaps + cCaps + vのコピペが使えたら、正しく設定できてます。

お疲れ様でした。

【vim】これだけは覚えとけ的なコマンド

vimをエディタとして使い続けていますが、未だに慣れない未熟者です。。

vimマスターになれば、音楽を奏でるかのようにコーディングができるらしいですが、 未だにコマンドを覚えられない私には程遠い存在です。

これから少しずつコマンドを覚えていこうと思います。 まずは、最低限必要だと思ったコマンドをまとめます。

モード切替

コマンドモードへ

コマンド 意味
ESC コマンドモードへ

挿入モードへ

コマンド 意味
i 挿入モードへ
a カーソルを右側にずらし、挿入モードへ
o 新しい行を挿入後、挿入モードへ
s カーソルの右の文字を削除後、挿入モードへ
cc カーソル行の文字を削除後、挿入モードへ

ビジュアルモードへ

コマンド 意味
v ビジュアルモードへ
Ctrl + v 矩形選択のビジュアルモードへ
V カーソルがある行を選択後、ビジュアルモードへ

カーソル移動

ちょっとした移動

コマンド 意味
h 右に移動
j 下に移動
k 上に移動
l 右に移動
w 次の単語の先頭に移動
b 現在、または、前の単語の先頭に移動
e 単語の最後の文字まで移動
^ 行の頭に移動
$ 行末に移動
% 対応するカッコに移動

ウィンドウ単位の移動

コマンド 意味
H ウィンドウの一番上の行に移動
M ウィンドウの中央行に移動
L ウィンドウの一番下の行に移動
gg ファイルの先頭行に移動
G ファイルの最終行に移動
Ctrl + u ウィンドウの1/2分上の行に移動
Ctrl + d ウィンドウの1/2分下の行に移動
Ctrl + b ウィンドウ分上の行に移動
Ctrl + f ウィンドウ分下の行に移動

削除

コマンド 意味
x カーソルの右の一文字を削除
diw カーソルの単語を削除
D カーソル位置から行末まで削除
dd カーソル行の削除

Undo / Redo

コマンド 意味
u Undo
Ctrl + r Redo

検索

コマンド 意味
/word wordを検索
n カーソルから次にヒットした場所に移動
N カーソル以前で最も近くでヒットした場所に移動

コピペ

コマンド 意味
yy カーソル行をヤンク(コピー)
p カーソル位置にペースト

単語補完

コマンド 意味
Ctrl + p 単語補完(前方探索)

ファイル操作

コマンド 意味
:w 上書き保存
:q 閉じる
:wq 上書き保存後、閉じる
:q! 編集を反映せず閉じる