大浦版FFTをJavaに移植する
今回は、高速な処理で有名な大浦版FFTをJavaに移植してみました。
大浦版FFTとは、Cで書かれた高速フーリエ変換(FFT)で、1次元・2次元・3次元のバージョンがあります。
FFT Package 1-dim / 2-dim
ググってみると、Java移植の話題はちょくちょくあるのですが、ソースコードが載っているものが少ないので、書いてみました。
正直言いますと、こちらのソースをかなり参考にさせていただいております。
要するに、配列をずらしたりする処理を書き換えればいいのです。
大浦版FFTのJava移植
移植での変更点
Javaではポインタが使えないため、配列をそのまま引数として渡さなければなりません。
また、配列をずらす、という動作を行うための処理部分を変更しなければなりません。
Cでは例えば、配列 a [ n ] があったなら、a+2で配列aを2つずらすことができ、これをそのまま関数へ渡すことができます。しかし、Javaにはそのような機能はなく、配列を2つずらすなら、a [ n + 2 ]と書かなければなりません。
この二つの違いを直すと、正常に動くようになります。
今回移植したのは、FFT4gだけです。手抜きですいません。
間違ってたらごめんなさい。勘弁してください。
ということで、ソースコード全体はこちら。
FFT4g.java
package …; import static java.lang.Math.atan; import static java.lang.Math.cos; import static java.lang.Math.sin; public class FFT4g { private int[] ip; private double[] w; private int n; private double t[]; FFT4g(int n) { this.n = n; ip = new int[2+(int)Math.sqrt((double)n/2.0)+1]; w = new double[n*5/4]; t = new double[n/2]; ip[0] = 0; } public void cdft(int isgn, double[] a) { if (n > (ip[0] << 2)) { makewt(n >> 2); } if (n > 4) { if (isgn >= 0) { bitrv2(n, a); cftfsub(n, a); } else { bitrv2conj(n, a); cftbsub(n, a); } } else if (n == 4) { cftfsub(n, a); } } public void rdft(int isgn, double[] a) { int nw, nc; double xi; nw = ip[0]; if (n > (nw << 2)) { nw = n >> 2; makewt(nw); } nc = ip[1]; if (n > (nc << 2)) { nc = n >> 2; makect(nc,w, nw); } if (isgn >= 0) { if (n > 4) { bitrv2(n, a); cftfsub(n, a); rftfsub(n, a, nc, w, nw); } else if (n == 4) { cftfsub(n, a); } xi = a[0] - a[1]; a[0] += a[1]; a[1] = xi; } else { a[1] = 0.5 * (a[0] - a[1]); a[0] -= a[1]; if (n > 4) { rftbsub(n, a, nc, w, nw); bitrv2(n, a); cftbsub(n, a); } else if (n == 4) { cftfsub(n, a); } } } public void ddct(int isgn, double[] a){ int j, nw, nc; double xr; nw = ip[0]; if (n > (nw << 2)) { nw = n >> 2; makewt(nw); } nc = ip[1]; if (n > nc) { nc = n; makect(nc, w, nw); } if (isgn < 0) { xr = a[n - 1]; for (j = n - 2; j >= 2; j -= 2) { a[j + 1] = a[j] - a[j - 1]; a[j] += a[j - 1]; } a[1] = a[0] - xr; a[0] += xr; if (n > 4) { rftbsub(n, a, nc, w, nw); bitrv2(n, a); cftbsub(n, a); } else if (n == 4) { cftfsub(n, a); } } dctsub(n, a, nc, w, nw); if (isgn >= 0) { if (n > 4) { bitrv2(n, a); cftfsub(n, a); rftfsub(n, a, nc, w, nw); } else if (n == 4) { cftfsub(n, a); } xr = a[0] - a[1]; a[0] += a[1]; for (j = 2; j < n; j += 2) { a[j - 1] = a[j] - a[j + 1]; a[j] += a[j + 1]; } a[n - 1] = xr; } } public void ddst(int isgn, double[] a) { int j, nw, nc; double xr; nw = ip[0]; if (n > (nw << 2)) { nw = n >> 2; makewt(nw); } nc = ip[1]; if (n > nc) { nc = n; makect(nc, w, nw); } if (isgn < 0) { xr = a[n - 1]; for (j = n - 2; j >= 2; j -= 2) { a[j + 1] = -a[j] - a[j - 1]; a[j] -= a[j - 1]; } a[1] = a[0] + xr; a[0] -= xr; if (n > 4) { rftbsub(n, a, nc, w, nw); bitrv2(n, a); cftbsub(n, a); } else if (n == 4) { cftfsub(n, a); } } dstsub(n, a, nc, w, nw); if (isgn >= 0) { if (n > 4) { bitrv2(n, a); cftfsub(n, a); rftfsub(n, a, nc, w, nw); } else if (n == 4) { cftfsub(n, a); } xr = a[0] - a[1]; a[0] += a[1]; for (j = 2; j < n; j += 2) { a[j - 1] = -a[j] - a[j + 1]; a[j] -= a[j + 1]; } a[n - 1] = -xr; } } public void dfct(double[] a) { int j, k, l, m, mh, nw, nc; double xr, xi, yr, yi; nw = ip[0]; if (n > (nw << 3)) { nw = n >> 3; makewt(nw); } nc = ip[1]; if (n > (nc << 1)) { nc = n >> 1; makect(nc, w, nw); } m = n >> 1; yi = a[m]; xi = a[0] + a[n]; a[0] -= a[n]; t[0] = xi - yi; t[m] = xi + yi; if (n > 2) { mh = m >> 1; for (j = 1; j < mh; j++) { k = m - j; xr = a[j] - a[n - j]; xi = a[j] + a[n - j]; yr = a[k] - a[n - k]; yi = a[k] + a[n - k]; a[j] = xr; a[k] = yr; t[j] = xi - yi; t[k] = xi + yi; } t[mh] = a[mh] + a[n - mh]; a[mh] -= a[n - mh]; dctsub(m, a, nc, w, nw); if (m > 4) { bitrv2(m, a); cftfsub(m, a); rftfsub(m, a, nc, w, nw); } else if (m == 4) { cftfsub(m, a); } a[n - 1] = a[0] - a[1]; a[1] = a[0] + a[1]; for (j = m - 2; j >= 2; j -= 2) { a[2 * j + 1] = a[j] + a[j + 1]; a[2 * j - 1] = a[j] - a[j + 1]; } l = 2; m = mh; while (m >= 2) { dctsub(m, t, nc, w, nw); if (m > 4) { bitrv2(m, t); cftfsub(m, t); rftfsub(m, t, nc, w, nw); } else if (m == 4) { cftfsub(m, t); } a[n - l] = t[0] - t[1]; a[l] = t[0] + t[1]; k = 0; for (j = 2; j < m; j += 2) { k += l << 2; a[k - l] = t[j] - t[j + 1]; a[k + l] = t[j] + t[j + 1]; } l <<= 1; mh = m >> 1; for (j = 0; j < mh; j++) { k = m - j; t[j] = t[m + k] - t[m + j]; t[k] = t[m + k] + t[m + j]; } t[mh] = t[m + mh]; m = mh; } a[l] = t[0]; a[n] = t[2] - t[1]; a[0] = t[2] + t[1]; } else { a[1] = a[0]; a[2] = t[0]; a[0] = t[1]; } } public void dfst(double[] a) { int j, k, l, m, mh, nw, nc; double xr, xi, yr, yi; nw = ip[0]; if (n > (nw << 3)) { nw = n >> 3; makewt(nw); } nc = ip[1]; if (n > (nc << 1)) { nc = n >> 1; makect(nc, w, nw); } if (n > 2) { m = n >> 1; mh = m >> 1; for (j = 1; j < mh; j++) { k = m - j; xr = a[j] + a[n - j]; xi = a[j] - a[n - j]; yr = a[k] + a[n - k]; yi = a[k] - a[n - k]; a[j] = xr; a[k] = yr; t[j] = xi + yi; t[k] = xi - yi; } t[0] = a[mh] - a[n - mh]; a[mh] += a[n - mh]; a[0] = a[m]; dstsub(m, a, nc, w, nw); if (m > 4) { bitrv2(m, a); cftfsub(m, a); rftfsub(m, a, nc, w, nw); } else if (m == 4) { cftfsub(m, a); } a[n - 1] = a[1] - a[0]; a[1] = a[0] + a[1]; for (j = m - 2; j >= 2; j -= 2) { a[2 * j + 1] = a[j] - a[j + 1]; a[2 * j - 1] = -a[j] - a[j + 1]; } l = 2; m = mh; while (m >= 2) { dstsub(m, t, nc, w, nw); if (m > 4) { bitrv2(m, t); cftfsub(m, t); rftfsub(m, t, nc, w, nw); } else if (m == 4) { cftfsub(m, t); } a[n - l] = t[1] - t[0]; a[l] = t[0] + t[1]; k = 0; for (j = 2; j < m; j += 2) { k += l << 2; a[k - l] = -t[j] - t[j + 1]; a[k + l] = t[j] - t[j + 1]; } l <<= 1; mh = m >> 1; for (j = 1; j < mh; j++) { k = m - j; t[j] = t[m + k] + t[m + j]; t[k] = t[m + k] - t[m + j]; } t[0] = t[m + mh]; m = mh; } a[l] = t[0]; } a[0] = 0; } /* -------- initializing routines -------- */ private void makewt(int nw) { int j, nwh; double delta, x, y; ip[0] = nw; ip[1] = 1; if (nw > 2) { nwh = nw >> 1; delta = atan(1.0) / nwh; w[0] = 1; w[1] = 0; w[nwh] = cos(delta * nwh); w[nwh + 1] = w[nwh]; if (nwh > 2) { for (j = 2; j < nwh; j += 2) { x = cos(delta * j); y = sin(delta * j); w[j] = x; w[j + 1] = y; w[nw - j] = y; w[nw - j + 1] = x; } bitrv2(nw, w); } } } private void makect(int nc, double[] c, int nw) { int j, nch; double delta; ip[1] = nc; if (nc > 1) { nch = nc >> 1; delta = atan(1.0) / nch; c[nw + 0] = cos(delta * nch); c[nw + nch] = 0.5 * c[0]; for (j = 1; j < nch; j++) { c[nw + j] = 0.5 * cos(delta * j); c[nw + nc - j] = 0.5 * sin(delta * j); } } } /* -------- child routines -------- */ private void bitrv2(int n, double[] a) { int j, j1, k, k1, l, m, m2; double xr, xi, yr, yi; ip[2 + 0] = 0; l = n; m = 1; while ((m << 3) < l) { l >>= 1; for (j = 0; j < m; j++) { ip[2 + m + j] = ip[2 + j] + l; } m <<= 1; } m2 = 2 * m; if ((m << 3) == l) { for (k = 0; k < m; k++) { for (j = 0; j < k; j++) { j1 = 2 * j + ip[2 + k]; k1 = 2 * k + ip[2 + j]; xr = a[j1]; xi = a[j1 + 1]; yr = a[k1]; yi = a[k1 + 1]; a[j1] = yr; a[j1 + 1] = yi; a[k1] = xr; a[k1 + 1] = xi; j1 += m2; k1 += 2 * m2; xr = a[j1]; xi = a[j1 + 1]; yr = a[k1]; yi = a[k1 + 1]; a[j1] = yr; a[j1 + 1] = yi; a[k1] = xr; a[k1 + 1] = xi; j1 += m2; k1 -= m2; xr = a[j1]; xi = a[j1 + 1]; yr = a[k1]; yi = a[k1 + 1]; a[j1] = yr; a[j1 + 1] = yi; a[k1] = xr; a[k1 + 1] = xi; j1 += m2; k1 += 2 * m2; xr = a[j1]; xi = a[j1 + 1]; yr = a[k1]; yi = a[k1 + 1]; a[j1] = yr; a[j1 + 1] = yi; a[k1] = xr; a[k1 + 1] = xi; } j1 = 2 * k + m2 + ip[2 + k]; k1 = j1 + m2; xr = a[j1]; xi = a[j1 + 1]; yr = a[k1]; yi = a[k1 + 1]; a[j1] = yr; a[j1 + 1] = yi; a[k1] = xr; a[k1 + 1] = xi; } } else { for (k = 1; k < m; k++) { for (j = 0; j < k; j++) { j1 = 2 * j + ip[2 + k]; k1 = 2 * k + ip[2 + j]; xr = a[j1]; xi = a[j1 + 1]; yr = a[k1]; yi = a[k1 + 1]; a[j1] = yr; a[j1 + 1] = yi; a[k1] = xr; a[k1 + 1] = xi; j1 += m2; k1 += m2; xr = a[j1]; xi = a[j1 + 1]; yr = a[k1]; yi = a[k1 + 1]; a[j1] = yr; a[j1 + 1] = yi; a[k1] = xr; a[k1 + 1] = xi; } } } } private void bitrv2conj(int n, double[] a) { int j, j1, k, k1, l, m, m2; double xr, xi, yr, yi; ip[2 + 0] = 0; l = n; m = 1; while ((m << 3) < l) { l >>= 1; for (j = 0; j < m; j++) { ip[2 + m + j] = ip[2 + j] + l; } m <<= 1; } m2 = 2 * m; if ((m << 3) == l) { for (k = 0; k < m; k++) { for (j = 0; j < k; j++) { j1 = 2 * j + ip[2 + k]; k1 = 2 * k + ip[2 + j]; xr = a[j1]; xi = -a[j1 + 1]; yr = a[k1]; yi = -a[k1 + 1]; a[j1] = yr; a[j1 + 1] = yi; a[k1] = xr; a[k1 + 1] = xi; j1 += m2; k1 += 2 * m2; xr = a[j1]; xi = -a[j1 + 1]; yr = a[k1]; yi = -a[k1 + 1]; a[j1] = yr; a[j1 + 1] = yi; a[k1] = xr; a[k1 + 1] = xi; j1 += m2; k1 -= m2; xr = a[j1]; xi = -a[j1 + 1]; yr = a[k1]; yi = -a[k1 + 1]; a[j1] = yr; a[j1 + 1] = yi; a[k1] = xr; a[k1 + 1] = xi; j1 += m2; k1 += 2 * m2; xr = a[j1]; xi = -a[j1 + 1]; yr = a[k1]; yi = -a[k1 + 1]; a[j1] = yr; a[j1 + 1] = yi; a[k1] = xr; a[k1 + 1] = xi; } k1 = 2 * k + ip[2 + k]; a[k1 + 1] = -a[k1 + 1]; j1 = k1 + m2; k1 = j1 + m2; xr = a[j1]; xi = -a[j1 + 1]; yr = a[k1]; yi = -a[k1 + 1]; a[j1] = yr; a[j1 + 1] = yi; a[k1] = xr; a[k1 + 1] = xi; k1 += m2; a[k1 + 1] = -a[k1 + 1]; } } else { a[1] = -a[1]; a[m2 + 1] = -a[m2 + 1]; for (k = 1; k < m; k++) { for (j = 0; j < k; j++) { j1 = 2 * j + ip[2 + k]; k1 = 2 * k + ip[2 + j]; xr = a[j1]; xi = -a[j1 + 1]; yr = a[k1]; yi = -a[k1 + 1]; a[j1] = yr; a[j1 + 1] = yi; a[k1] = xr; a[k1 + 1] = xi; j1 += m2; k1 += m2; xr = a[j1]; xi = -a[j1 + 1]; yr = a[k1]; yi = -a[k1 + 1]; a[j1] = yr; a[j1 + 1] = yi; a[k1] = xr; a[k1 + 1] = xi; } k1 = 2 * k + ip[2 + k]; a[k1 + 1] = -a[k1 + 1]; a[k1 + m2 + 1] = -a[k1 + m2 + 1]; } } } private void cftfsub(int n, double[] a) { int j, j1, j2, j3, l; double x0r, x0i, x1r, x1i, x2r, x2i, x3r, x3i; l = 2; if (n > 8) { cft1st(a); l = 8; while ((l << 2) < n) { cftmdl(l, a); l <<= 2; } } if ((l << 2) == n) { for (j = 0; j < l; j += 2) { j1 = j + l; j2 = j1 + l; j3 = j2 + l; x0r = a[j] + a[j1]; x0i = a[j + 1] + a[j1 + 1]; x1r = a[j] - a[j1]; x1i = a[j + 1] - a[j1 + 1]; x2r = a[j2] + a[j3]; x2i = a[j2 + 1] + a[j3 + 1]; x3r = a[j2] - a[j3]; x3i = a[j2 + 1] - a[j3 + 1]; a[j] = x0r + x2r; a[j + 1] = x0i + x2i; a[j2] = x0r - x2r; a[j2 + 1] = x0i - x2i; a[j1] = x1r - x3i; a[j1 + 1] = x1i + x3r; a[j3] = x1r + x3i; a[j3 + 1] = x1i - x3r; } } else { for (j = 0; j < l; j += 2) { j1 = j + l; x0r = a[j] - a[j1]; x0i = a[j + 1] - a[j1 + 1]; a[j] += a[j1]; a[j + 1] += a[j1 + 1]; a[j1] = x0r; a[j1 + 1] = x0i; } } } private void cftbsub(int n, double[] a) { int j, j1, j2, j3, l; double x0r, x0i, x1r, x1i, x2r, x2i, x3r, x3i; l = 2; if (n > 8) { cft1st(a); l = 8; while ((l << 2) < n) { cftmdl(l, a); l <<= 2; } } if ((l << 2) == n) { for (j = 0; j < l; j += 2) { j1 = j + l; j2 = j1 + l; j3 = j2 + l; x0r = a[j] + a[j1]; x0i = -a[j + 1] - a[j1 + 1]; x1r = a[j] - a[j1]; x1i = -a[j + 1] + a[j1 + 1]; x2r = a[j2] + a[j3]; x2i = a[j2 + 1] + a[j3 + 1]; x3r = a[j2] - a[j3]; x3i = a[j2 + 1] - a[j3 + 1]; a[j] = x0r + x2r; a[j + 1] = x0i - x2i; a[j2] = x0r - x2r; a[j2 + 1] = x0i + x2i; a[j1] = x1r - x3i; a[j1 + 1] = x1i - x3r; a[j3] = x1r + x3i; a[j3 + 1] = x1i + x3r; } } else { for (j = 0; j < l; j += 2) { j1 = j + l; x0r = a[j] - a[j1]; x0i = -a[j + 1] + a[j1 + 1]; a[j] += a[j1]; a[j + 1] = -a[j + 1] - a[j1 + 1]; a[j1] = x0r; a[j1 + 1] = x0i; } } } private void cft1st(double[] a) { int j, k1, k2; double wk1r, wk1i, wk2r, wk2i, wk3r, wk3i; double x0r, x0i, x1r, x1i, x2r, x2i, x3r, x3i; x0r = a[0] + a[2]; x0i = a[1] + a[3]; x1r = a[0] - a[2]; x1i = a[1] - a[3]; x2r = a[4] + a[6]; x2i = a[5] + a[7]; x3r = a[4] - a[6]; x3i = a[5] - a[7]; a[0] = x0r + x2r; a[1] = x0i + x2i; a[4] = x0r - x2r; a[5] = x0i - x2i; a[2] = x1r - x3i; a[3] = x1i + x3r; a[6] = x1r + x3i; a[7] = x1i - x3r; wk1r = w[2]; x0r = a[8] + a[10]; x0i = a[9] + a[11]; x1r = a[8] - a[10]; x1i = a[9] - a[11]; x2r = a[12] + a[14]; x2i = a[13] + a[15]; x3r = a[12] - a[14]; x3i = a[13] - a[15]; a[8] = x0r + x2r; a[9] = x0i + x2i; a[12] = x2i - x0i; a[13] = x0r - x2r; x0r = x1r - x3i; x0i = x1i + x3r; a[10] = wk1r * (x0r - x0i); a[11] = wk1r * (x0r + x0i); x0r = x3i + x1r; x0i = x3r - x1i; a[14] = wk1r * (x0i - x0r); a[15] = wk1r * (x0i + x0r); k1 = 0; for (j = 16; j < n; j += 16) { k1 += 2; k2 = 2 * k1; wk2r = w[k1]; wk2i = w[k1 + 1]; wk1r = w[k2]; wk1i = w[k2 + 1]; wk3r = wk1r - 2 * wk2i * wk1i; wk3i = 2 * wk2i * wk1r - wk1i; x0r = a[j] + a[j + 2]; x0i = a[j + 1] + a[j + 3]; x1r = a[j] - a[j + 2]; x1i = a[j + 1] - a[j + 3]; x2r = a[j + 4] + a[j + 6]; x2i = a[j + 5] + a[j + 7]; x3r = a[j + 4] - a[j + 6]; x3i = a[j + 5] - a[j + 7]; a[j] = x0r + x2r; a[j + 1] = x0i + x2i; x0r -= x2r; x0i -= x2i; a[j + 4] = wk2r * x0r - wk2i * x0i; a[j + 5] = wk2r * x0i + wk2i * x0r; x0r = x1r - x3i; x0i = x1i + x3r; a[j + 2] = wk1r * x0r - wk1i * x0i; a[j + 3] = wk1r * x0i + wk1i * x0r; x0r = x1r + x3i; x0i = x1i - x3r; a[j + 6] = wk3r * x0r - wk3i * x0i; a[j + 7] = wk3r * x0i + wk3i * x0r; wk1r = w[k2 + 2]; wk1i = w[k2 + 3]; wk3r = wk1r - 2 * wk2r * wk1i; wk3i = 2 * wk2r * wk1r - wk1i; x0r = a[j + 8] + a[j + 10]; x0i = a[j + 9] + a[j + 11]; x1r = a[j + 8] - a[j + 10]; x1i = a[j + 9] - a[j + 11]; x2r = a[j + 12] + a[j + 14]; x2i = a[j + 13] + a[j + 15]; x3r = a[j + 12] - a[j + 14]; x3i = a[j + 13] - a[j + 15]; a[j + 8] = x0r + x2r; a[j + 9] = x0i + x2i; x0r -= x2r; x0i -= x2i; a[j + 12] = -wk2i * x0r - wk2r * x0i; a[j + 13] = -wk2i * x0i + wk2r * x0r; x0r = x1r - x3i; x0i = x1i + x3r; a[j + 10] = wk1r * x0r - wk1i * x0i; a[j + 11] = wk1r * x0i + wk1i * x0r; x0r = x1r + x3i; x0i = x1i - x3r; a[j + 14] = wk3r * x0r - wk3i * x0i; a[j + 15] = wk3r * x0i + wk3i * x0r; } } private void cftmdl(int l, double[] a) { int j, j1, j2, j3, k, k1, k2, m, m2; double wk1r, wk1i, wk2r, wk2i, wk3r, wk3i; double x0r, x0i, x1r, x1i, x2r, x2i, x3r, x3i; m = l << 2; for (j = 0; j < l; j += 2) { j1 = j + l; j2 = j1 + l; j3 = j2 + l; x0r = a[j] + a[j1]; x0i = a[j + 1] + a[j1 + 1]; x1r = a[j] - a[j1]; x1i = a[j + 1] - a[j1 + 1]; x2r = a[j2] + a[j3]; x2i = a[j2 + 1] + a[j3 + 1]; x3r = a[j2] - a[j3]; x3i = a[j2 + 1] - a[j3 + 1]; a[j] = x0r + x2r; a[j + 1] = x0i + x2i; a[j2] = x0r - x2r; a[j2 + 1] = x0i - x2i; a[j1] = x1r - x3i; a[j1 + 1] = x1i + x3r; a[j3] = x1r + x3i; a[j3 + 1] = x1i - x3r; } wk1r = w[2]; for (j = m; j < l + m; j += 2) { j1 = j + l; j2 = j1 + l; j3 = j2 + l; x0r = a[j] + a[j1]; x0i = a[j + 1] + a[j1 + 1]; x1r = a[j] - a[j1]; x1i = a[j + 1] - a[j1 + 1]; x2r = a[j2] + a[j3]; x2i = a[j2 + 1] + a[j3 + 1]; x3r = a[j2] - a[j3]; x3i = a[j2 + 1] - a[j3 + 1]; a[j] = x0r + x2r; a[j + 1] = x0i + x2i; a[j2] = x2i - x0i; a[j2 + 1] = x0r - x2r; x0r = x1r - x3i; x0i = x1i + x3r; a[j1] = wk1r * (x0r - x0i); a[j1 + 1] = wk1r * (x0r + x0i); x0r = x3i + x1r; x0i = x3r - x1i; a[j3] = wk1r * (x0i - x0r); a[j3 + 1] = wk1r * (x0i + x0r); } k1 = 0; m2 = 2 * m; for (k = m2; k < n; k += m2) { k1 += 2; k2 = 2 * k1; wk2r = w[k1]; wk2i = w[k1 + 1]; wk1r = w[k2]; wk1i = w[k2 + 1]; wk3r = wk1r - 2 * wk2i * wk1i; wk3i = 2 * wk2i * wk1r - wk1i; for (j = k; j < l + k; j += 2) { j1 = j + l; j2 = j1 + l; j3 = j2 + l; x0r = a[j] + a[j1]; x0i = a[j + 1] + a[j1 + 1]; x1r = a[j] - a[j1]; x1i = a[j + 1] - a[j1 + 1]; x2r = a[j2] + a[j3]; x2i = a[j2 + 1] + a[j3 + 1]; x3r = a[j2] - a[j3]; x3i = a[j2 + 1] - a[j3 + 1]; a[j] = x0r + x2r; a[j + 1] = x0i + x2i; x0r -= x2r; x0i -= x2i; a[j2] = wk2r * x0r - wk2i * x0i; a[j2 + 1] = wk2r * x0i + wk2i * x0r; x0r = x1r - x3i; x0i = x1i + x3r; a[j1] = wk1r * x0r - wk1i * x0i; a[j1 + 1] = wk1r * x0i + wk1i * x0r; x0r = x1r + x3i; x0i = x1i - x3r; a[j3] = wk3r * x0r - wk3i * x0i; a[j3 + 1] = wk3r * x0i + wk3i * x0r; } wk1r = w[k2 + 2]; wk1i = w[k2 + 3]; wk3r = wk1r - 2 * wk2r * wk1i; wk3i = 2 * wk2r * wk1r - wk1i; for (j = k + m; j < l + (k + m); j += 2) { j1 = j + l; j2 = j1 + l; j3 = j2 + l; x0r = a[j] + a[j1]; x0i = a[j + 1] + a[j1 + 1]; x1r = a[j] - a[j1]; x1i = a[j + 1] - a[j1 + 1]; x2r = a[j2] + a[j3]; x2i = a[j2 + 1] + a[j3 + 1]; x3r = a[j2] - a[j3]; x3i = a[j2 + 1] - a[j3 + 1]; a[j] = x0r + x2r; a[j + 1] = x0i + x2i; x0r -= x2r; x0i -= x2i; a[j2] = -wk2i * x0r - wk2r * x0i; a[j2 + 1] = -wk2i * x0i + wk2r * x0r; x0r = x1r - x3i; x0i = x1i + x3r; a[j1] = wk1r * x0r - wk1i * x0i; a[j1 + 1] = wk1r * x0i + wk1i * x0r; x0r = x1r + x3i; x0i = x1i - x3r; a[j3] = wk3r * x0r - wk3i * x0i; a[j3 + 1] = wk3r * x0i + wk3i * x0r; } } } private void rftfsub(int n, double[] a, int nc, double[] c, int nw) { int j, k, kk, ks, m; double wkr, wki, xr, xi, yr, yi; m = n >> 1; ks = 2 * nc / m; kk = 0; for (j = 2; j < m; j += 2) { k = n - j; kk += ks; wkr = 0.5 - c[nw + nc - kk]; wki = c[nw + kk]; xr = a[j] - a[k]; xi = a[j + 1] + a[k + 1]; yr = wkr * xr - wki * xi; yi = wkr * xi + wki * xr; a[j] -= yr; a[j + 1] -= yi; a[k] += yr; a[k + 1] -= yi; } } private void rftbsub(int n, double[] a, int nc, double[] c, int nw) { int j, k, kk, ks, m; double wkr, wki, xr, xi, yr, yi; a[1] = -a[1]; m = n >> 1; ks = 2 * nc / m; kk = 0; for (j = 2; j < m; j += 2) { k = n - j; kk += ks; wkr = 0.5 - c[nw + nc - kk]; wki = c[nw + kk]; xr = a[j] - a[k]; xi = a[j + 1] + a[k + 1]; yr = wkr * xr + wki * xi; yi = wkr * xi - wki * xr; a[j] -= yr; a[j + 1] = yi - a[j + 1]; a[k] += yr; a[k + 1] = yi - a[k + 1]; } a[m + 1] = -a[m + 1]; } private void dctsub(int n, double[] a, int nc, double[] c, int nw) { int j, k, kk, ks, m; double wkr, wki, xr; m = n >> 1; ks = nc / n; kk = 0; for (j = 1; j < m; j++) { k = n - j; kk += ks; wkr = c[nw + kk] - c[nw + nc - kk]; wki = c[nw + kk] + c[nw + nc - kk]; xr = wki * a[j] - wkr * a[k]; a[j] = wkr * a[j] + wki * a[k]; a[k] = xr; } a[m] *= c[nw + 0]; } private void dstsub(int n, double[] a, int nc, double[] c, int nw) { int j, k, kk, ks, m; double wkr, wki, xr; m = n >> 1; ks = nc / n; kk = 0; for (j = 1; j < m; j++) { k = n - j; kk += ks; wkr = c[nw + kk] - c[nw + nc - kk]; wki = c[nw + kk] + c[nw + nc - kk]; xr = wki * a[k] - wkr * a[j]; a[k] = wkr * a[k] + wki * a[j]; a[j] = xr; } a[m] *= c[nw + 0]; } }
ご自由にお使いください。他人のソースコードを書き換えただけですから。
ではまた。
英単語アプリ「Memoria」を公開しました!
こんにちは。今日は私が開発したアプリを紹介させていただきます。
中学生に最適!英単語アプリ「Memoria」
今回公開したアプリは、「Memoria」です。
Memoriaは中学生で学習する英単語を収録した英単語アプリです。
英単語は大阪府教育委員会が公表している「大阪版中学校で学ぶ英単語集」を元に選定いたしました。
これは大阪府の公立高校入試にて、出題される英単語を集めたものです。
これを勉強すれば、高校入試に必要な英単語をしっかり暗記することができます。
現代では英語力が必須となってまいりました。
中学校で学ぶ英単語は社会人として覚えていて当然のものばかり!
みなさんも英単語の復習として、ぜひご活用ください。
技術的な話
ということで、宣伝はここまでです。
ここではこのアプリの技術的な話をしたいと思います。
使用したライブラリ
このアプリには、暗記した単語数や学習した時間をグラフで表示する機能があります。
ここでは、「MPAndroidChart」というライブラリを導入しました!
github.com
こちらで使い方が紹介されています。
moneyforward.com
このライブラリは様々なアプリで活用されて、非常に使い勝手のよいライブラリです。
データを設定すれば、アニメーション付きで表示され、とても見た目が美しいグラフが表示されます。
自分で実装するととても大変だと思いますので、非常に便利なライブラリです。
また、ライセンス表示には「AboutLibraries」というライブラリを使用しました。
github.com
こちらで使い方が紹介されています。
cutmail.hatenablog.com
有名なライブラリであれば、ライセンスが自動的に表示されます。
自分で追加することもできます。
日付処理には「ThreeTenABP」を使用しています。
github.com
こちらで使い方が紹介されています。
techblog.timers-inc.com
日付処理は、標準機能では大変扱いにくいものです。
それを簡単にしてくれるのがこのライブラリです。
日付処理はJodaTimeが有名ですが、JodaTimeはヘビーなライブラリという噂をきき、今回はこれを選びました。
これのお陰で1ヶ月前というような処理が簡単に行えるようになりました。
ThreeTenABPはJava8の日付処理APIのバックポートですので、もしAndroidがJava8に対応したとしても、大幅に書き換える必要はありません。
苦労した点
このアプリは私の人生で初めて開発したAndroidアプリです。
知らないことだらけで、すべてに苦戦しました。
単語のリスト表示には大いに悩まされました。
タブとして単語を分けるため、Fragmentを導入したり、リストに画像と文字を並べて表示できるようにしたり。
非常に大変でした。ほんとに苦労しました(笑)
アプリ作りに手慣れた方には非常に簡単なことだと思いますが、私にはチョモランマ級に高い壁でした。
リスト作成時にヒープが足らなくなったり、散々エラーに見舞われました…。
まあ、なにはともあれ完成してよかった!(笑)
おしゃれなギャラリーが作れるCMS「Lychee」を使ってみた
みなさん、CMSってご存じですか?
Contents Management Systemの略で、htmlを直接編集することなく、ブログやホームページが作れるシステムのことです。
世界中で広く利用されている「WordPress」は、このCMSの一種です。
WordPressなら、ブログを書くときにいちいちhtmlをまるごと制作する、なんてことはないですよね?
これがCMSの特徴です。
CMSは、WordPress以外にもDrupalやConcrete5など様々な種類があります。
余談ですが、個人的にはConcrete5が使いやすくて好きだったりします。
今日は、このCMSの中から「Lychee」をご紹介します。
Lycheeとは
タイトルに合ったようにLycheeはギャラリーを作るためのものです。
ギャラリーとは、写真を一覧で表示して、画像をクリックすると拡大される、といった機能を持つものです。
スマホの中にも写真を見るためのギャラリーアプリが入っていますよね。
写真をメインで扱うようなサイトでは、なるべく使いやすく、きれいに見せたいところですよね。
そんな望みを叶えてくれるのが、Lycheeです。
WordPressにプラグインをいれてギャラリーを作ってもいいのですが、そうすると大掛かりなシステムになりますよね。
ちょっと写真が見れるサイトを作りたいんだ、なんて時には面倒だったりします。
ということで、Lycheeが活用されるのです。
Lycheeは、主にPHPとjavascriptで構成されており、写真の管理にMySQLが必要です。
WordPressがインストールできるサーバーであれば、Lycheeもインストール可能です。
インストール手順はとても簡単です。
まず、Lycheeのgithubにアクセスし、ファイルをダウンロードします。
git cloneしても構いません。
github.com
このファイルを解凍し、インストールしたいディレクトリに配置して、その場所にアクセスします。
あとは画面に従って、MySQLのユーザー名やパスワードなど必要な情報を入力し、管理者用パスワードを設定すれば完了です。
詳しい利用方法は、READMEの中に書かれています。
直感的に使い方がわかるので、迷うこともないと思います。
Lycheeの良い点・悪い点
Lycheeの良いところは、シンプルさと美しさです。
ギャラリーを作るCMSは、レイアウトが残念で全近代的なビジュアルだったりしますが、Lycheeは黒を基調としたシンプルなデザインです。
また、それでいて必要な機能はしっかり備わっています。
写真を一括でzipにしてダウンロードしたり、写真1枚1枚を全画面表示したり、名前を変更したり共有したり。
さらにはパスワードによって写真を保護する、なんてこともできます。
基本的な機能は揃っていて、便利です。
今まで3日ほど私が使ってきて困ったことがいくつかありました。
まず、パスワード保護に関するものです。
パスワードによって写真を保護している場合、フォルダ内の写真を見るたびにパスワードを求められることがあります。
写真を1枚見るごとにパスワードを求められ、非常に面倒でした。
この原因はchromeのパスワード保存機能が原因でした。
管理者用のアカウント情報をchromeに記憶させていたことで、このような面倒な現象が起こるようです。
また、写真の一括ダウンロードですが、写真の数が多くなるとうまく動かなくなりました。
原因は、zipファイルの送信にありました。
一括ダウンロードでは、毎回phpにてzipファイルの作成が行われ、そのまま送信されるのですが、これがファイルサイズを考慮しない作りとなっているようです。
サーバーのメモリ不足によって転送ができなくなるようです。
php/Module/Album.php内のgetArchive()を以下のように編集すると直りました。
<?php public function getArchive() { // Check dependencies Validator::required(isset($this->albumIDs), __METHOD__); // Call plugins Plugins::get()->activate(__METHOD__, 0, func_get_args()); // Illicit chars $badChars = array_merge( array_map('chr', range(0,31)), array("<", ">", ":", '"', "/", "\\", "|", "?", "*") ); // Photos query switch($this->albumIDs) { case 's': $photos = Database::prepare(Database::get(), 'SELECT title, url FROM ? WHERE public = 1', array(LYCHEE_TABLE_PHOTOS)); $zipTitle = 'Public'; break; case 'f': $photos = Database::prepare(Database::get(), 'SELECT title, url FROM ? WHERE star = 1', array(LYCHEE_TABLE_PHOTOS)); $zipTitle = 'Starred'; break; case 'r': $photos = Database::prepare(Database::get(), 'SELECT title, url FROM ? WHERE LEFT(id, 10) >= unix_timestamp(DATE_SUB(NOW(), INTERVAL 1 DAY)) GROUP BY checksum', array(LYCHEE_TABLE_PHOTOS)); $zipTitle = 'Recent'; break; default: $photos = Database::prepare(Database::get(), "SELECT title, url FROM ? WHERE album = '?'", array(LYCHEE_TABLE_PHOTOS, $this->albumIDs)); $zipTitle = 'Unsorted'; } // Get title from database when album is not a SmartAlbum if ($this->albumIDs!=0&&is_numeric($this->albumIDs)) { $query = Database::prepare(Database::get(), "SELECT title FROM ? WHERE id = '?' LIMIT 1", array(LYCHEE_TABLE_ALBUMS, $this->albumIDs)); $album = Database::execute(Database::get(), $query, __METHOD__, __LINE__); if ($album===false) return false; // Get album object $album = $album->fetch_object(); // Album not found? if ($album===null) { Log::error(Database::get(), __METHOD__, __LINE__, 'Could not find specified album'); return false; } // Set title $zipTitle = $album->title; } // Escape title $zipTitle = str_replace($badChars, '', $zipTitle); $filename = LYCHEE_DATA . $zipTitle . '.zip'; // Create zip $zip = new ZipArchive(); if ($zip->open($filename, ZIPARCHIVE::CREATE)!==TRUE) { Log::error(Database::get(), __METHOD__, __LINE__, 'Could not create ZipArchive'); return false; } // Execute query $photos = Database::execute(Database::get(), $photos, __METHOD__, __LINE__); // Check if album empty if ($photos->num_rows==0) { Log::error(Database::get(), __METHOD__, __LINE__, 'Could not create ZipArchive without images'); return false; } // Parse each path $files = array(); while ($photo = $photos->fetch_object()) { // Parse url $photo->url = LYCHEE_UPLOADS_BIG . $photo->url; // Parse title $photo->title = str_replace($badChars, '', $photo->title); if (!isset($photo->title)||$photo->title==='') $photo->title = 'Untitled'; // Check if readable if (!@is_readable($photo->url)) continue; // Get extension of image $extension = getExtension($photo->url, false); // Set title for photo $zipFileName = $zipTitle . '/' . $photo->title . $extension; // Check for duplicates if (!empty($files)) { $i = 1; while (in_array($zipFileName, $files)) { // Set new title for photo $zipFileName = $zipTitle . '/' . $photo->title . '-' . $i . $extension; $i++; } } // Add to array $files[] = $zipFileName; // Add photo to zip $zip->addFile($photo->url, $zipFileName); } // Finish zip $zip->close(); // Send zip header("Content-Type: application/zip"); header("Content-Disposition: attachment; filename=\"$zipTitle.zip\""); header("Content-Length: " . filesize($filename)); //変更ここから while (ob_get_level() > 0) { ob_end_clean(); } ob_start(); $file = fopen($filename, 'rb'); if(!$file) { exit; } while(!feof($file) and (connection_status() == 0)) { echo fread($file, '1048576'); ob_flush(); } ob_flush(); ob_end_clean(); //変更ここまで // Delete zip unlink($filename); // Call plugins Plugins::get()->activate(__METHOD__, 1, func_get_args()); return true; }
こちらの記事を参考にさせていただきました。ありがとうございます。
every-rating.com
最後に
いかがでしょうか。不具合が多々あるので、実用可能ではないかもしれませんが、おしゃれで使い勝手がいいので、一度使ってみてはいかがでしょうか。
ではまた。
Androidでデータベースの作成・更新中にProgressDialogを表示する
こんにちは。
今日は久しぶりにAndroidネタです。
開発中のアプリでSQLiteを使ったデータベースを使用しているのですが、今回そのデータベースの構造自体を大きく変更する必要が出てきました。
データベースの変更には少し時間がかかる上に、その間にどこか別の場所をタップされたりなんかしたら非常に面倒です。
ということで、この更新処理中にProgressDialogを表示させることにしました。
意外と手間取りましたので、メモしておきます。
ProgressDialogとは
ProgressDialogというのは、これです。
上の画像では少しわかりづらいですが、左にはスピナー、右にはメッセージが表示されるダイアログです。
みなさんも、どこかでご覧になったことがあると思います。
よくデータを取得するときなんかに表示されたりしますよね。
今回は、これをデータベースの準備中に表示させようと思います。
実装
実装方法は非常にシンプル。
DatabaseHelperの中に、AsyncTaskを盛り込むだけです。
DatabaseHelper.java
public class DatabaseHelper extends SQLiteOpenHelper { private Context appContext; private int oldVersion,newVersion; public DatabaseHelper(Context context){ super(context,"database.db",null,2); this.appContext = context; } @Override public void onCreate(SQLiteDatabase db){ db.execSQL("テーブルの作成処理をここへ"); db.close(); } @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion){ this.oldVersion = oldVersion; this.newVersion = newVersion; new updateDB().execute(db); } public class updateDB extends AsyncTask<SQLiteDatabase,Void,Integer>{ ProgressDialog progressDialog; @Override protected void onPreExecute(){ super.onPreExecute(); progressDialog = ProgressDialog.show(appContext,"データを更新中","この処理には少し時間がかかる場合があります"); } @Override protected Integer doInBackground(SQLiteDatabase... params) { SQLiteDatabase db = params[0]; //データベースの更新処理 if(oldVersion == 1 && newVersion == 2) { db.execSQL("更新処理をここへ"); } db.close(); return 1; } @Override protected void onPostExecute(Integer result){ progressDialog.dismiss(); super.onPostExecute(result); } } }
非同期処理の中にデータベースの更新処理を入れることで、ダイアログを表示しながら処理ができるようになります。
非同期処理を呼び出す際には、updateDB().execute()を使用します。
ポイントは、doInBackgroundの引数です。
execute()の引数がdoInBackgroundのparamsに配列として渡されます。
そのため、今回は変数dbをparams[0]に格納して受け渡ししています。
最後に
まとめてみると、とても簡単ですね。
ではまた。
会員制の動画共有サイトの構築した
こんにちは。
今日は、会員制Webページの構築についてです。
まずは、構築したきっかけから。
お急ぎの方は読み飛ばして下さい。
- とある日の会話にて
- 会員向けサイトの仕様
- ①サーバーは有料のサーバーで、十分なスペック
- ②ログイン機能にて、会員以外は見れないようにする
- ③ユーザー名とパスワードは事前に個々に送信し、全ユーザー共通
- ④ユーザーの新規登録は必要ない
- ⑤ホームビデオを共有
- ⑥SSLによるセキュリティ対策
- 参考にしたサイト
- テンプレート
- ログイン画面
- メインページ
- ログイン機能の実装
- 動画のプレイヤー
- テンプレート
- 最後に
とある日の会話にて
ある日、こんなことを言われました。
依頼主『君はプログラミングできるから、ホームページ作れるよね。』
自分『はい?簡単そうに言いますけどそれは…』
依頼主『ホームページは会員だけが見られるようなもので、セキュリティは万全にしといてね。』
自分(もう断る雰囲気じゃないじゃん…)
みなさん、こんな無茶ぶりされたことありませんか!?笑
私はありましたよ、つい最近。
プログラミングできるから、ホームページも作れるって…
その理論でいくとプログラマー全員がホームページ作れて、Webデザイナーが仕事を失うことになるんですよ!
というツッコミを入れたいところですが、いつもお世話になってる方なので断れません。
ということで、はじめて会員サイトを作ってみました。