LoginSignup
1
0

More than 3 years have passed since last update.

配列の範囲指定をうっかり間違えたために本当にあった怖い話

Last updated at Posted at 2019-08-11

要点

  • Rubyの配列の要素取り出しは ary[p..q]ary[p...q] という表記があって、Python の ary[p:q] に相当するのは後者。
  • オーディオサンプリング周波数 44100 を 4 倍して 1 足すと素数になる (←知ってました?)。
  • FFT の一番広く使われている Cooley-Tukey のアルゴリズムはデータを因数分解した数に分けて細切れにしていく。

以上で何がいいたいかわかった方はそれで十分です。気をつけましょう…

本当にあった怖くて恥ずかしい話

Python の numpy で 4 s のオーディオ信号を FFT するプログラムがありました。

ぼくは Python が嫌いなので、それを mrkn 氏の素晴らしい Pycall (https://github.com/mrkn/pycall.rb) を使って Ruby に移植しました。その際、Python で 4 s 切り出す data[ptr:ptr + 44100 * 4] という感じの部分を、うっかりdata[ptr..(ptr + 44100 * 4)] と書いてしまった のです。もちろん正解は .. ではなく ... です。

その結果 謎のフリーズ。いや実際は単に計算に時間がかかっていただけなんですが、10 s 以上も固まれば、そりゃ ^C 押しますって。そして何は原因なんだろうpycallかな、などとあらぬ疑いを頓珍漢な方向に向けたりして リアルに3-4時間くらい時間を無駄にしました。(恥ずかしい。)

衝撃の再現コード

巷では Python の方が人気と聞くので Python のみで動くように書きました。実行すれば驚きを体験できます。

#!/usr/bin/python3
import numpy as np
fs = 44100
duration = 4
data = np.random.randn(fs * 10)           # dummy data
print("test 1")
y = np.fft.fft(data[0:fs * duration])
print("test 2")
y = np.fft.fft(data[0:fs * duration + 1])

本当はいろいろ Ruby で書きたいんだけど numpy とか keras とかがないから必要悪と割り切って Python を使っている人は pycall.rb (やそれを使った numpy.rb) を使いましょう!上記のコードならこんな↓感じにほぼベタ移植できます。すごい > pycall

#!/usr/bin/ruby
require "numpy"                           # gem install numpy
fs = 44100
duration = 4
data = Numpy.random.randn(fs * 10)        # dummy data
puts "test 1"
y = Numpy.fft.fft(data[0...fs * duration])
puts "test 2"
y = Numpy.fft.fft(data[0..fs * duration])

おわりに

  • Ruby は文法がきれいで書きやすくて好きなんですが、まさかこういうハマり方をするとは思ってもいませんでした。自分が悪いんですけど。
  • $44100\times4+1$ という大きめの覚えやすい素数が手に入ったのは収穫。ちなみに $44100\times4-1 = 419\times 421$ も ふたつの素数の積なので注意。
1
0
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0