Pythonで数字を反時計回りの螺旋状に表示するアルゴリズム
こんにちは勇気ある加藤です。
指定された巻き数の、渦巻きを表示するプログラムをPythonで書きました。
ちなみに数字をらせん状に並べると、素数の分布に規則性があるとかないとかというのが、下図の「ウラムの螺旋」になりますが、ちょっと何言ってんのか分かんないので、このブログでは数字を並べるまでにとどめておきます。
使用するモジュールについて
itertools
itertools
モジュールはイテレータに関するさまざまなコンテナを提供してくれる、めちゃくそ便利なモジュールです。すべてのPythonistaにとっての頼れる兄貴的存在ですね。今回は循環リストitertools.cycle
を使うためにインポートします。
math
math
モジュールは、math.log10
で数値の桁数をもとめるためにインポートしています。
実装
import math import itertools def print_spiral(spiral): """ 螺旋の二次元リストを整形して表示する args: [[4, 3, 2], [5, 0, 1], [6, 7, 8]] output: 4 3 2 5 0 1 6 7 8 """ # 最大値の桁数を求める end = spiral[-1][-1] digit = int(math.log10(end) + 1) # 桁数分だけ右詰めする書式をつくる base = "{: >" + str(digit) + "} " for i in spiral: for j in i: print(base.format(j), end="") print("\n", end="") if __name__ == "__main__": LOOP = 4 WIDTH = (2*LOOP) + 1 E = (1, 0) N = (0, -1) W = (-1, 0) S = (0, 1) DIRECTION = itertools.cycle((E, N, W, S)) x = LOOP y = LOOP step = 1 # 進んだ距離 corner = 1 # まがり角の位置 # 二次元リストを初期化 spiral = [] for i in range(WIDTH): spiral.append([0 for j in range(WIDTH)]) for i in range(WIDTH * WIDTH): # まがり角に到達したら方向転換 if step >= corner: step = 1 direction = next(DIRECTION) dx, dy = direction # X方向に進むとき、まがり角が遠くなる if direction == E or direction == W: corner += 1 spiral[y][x] = i step += 1 x += dx y += dy # 螺旋を表示する print_spiral(spiral)
LOOP
の値を変えると巻き数が変わります。step
とcorner
という変数が、このプログラムのみそで、以下の画像のように、ある角から次の角までの距離は、東方向と西方向へ方向転換する際に増加しています。そのため現在の移動量をstep
、次の曲がり角までの距離をcorner
として比較しています。
まとめ
私は過去にプログラムクイズにハマっていた時期がありまして、今回の問題も過去のソースをあさっていたら出てきたものです。最初に書いた時から3~4年たっているので、さすがに前よりはうまく書けるだろうと思い書き直してみたのですが、書き直したコードも全然よくできたアルゴリズムではないですね。まあ要件は満たしているし、私は自分に優しいタイプなのでこれで良しとしておきます。さよなら