teratail header banner
teratail header banner
質問するログイン新規登録

回答編集履歴

10

追記

2020/04/06 21:38

投稿

hayataka2049
hayataka2049

スコア30939

answer CHANGED
@@ -88,4 +88,39 @@
88
88
  (ベンチマーク取ってるページがありましたが、これを見るとnumbaでいいじゃんとなる・・・
89
89
  [Python を高速化する Numba, Cython 等を使って Julia Micro-Benchmarks してみた - Qiita](https://qiita.com/yniji/items/b7acffa02f03a94882e5))
90
90
 
91
- int16やint8にすれば多少速くなりますが、8bitだと128個1が続いただけでオーバーフローなので都合が悪いでしょう。
91
+ int16やint8にすれば多少速くなりますが、8bitだと128個1が続いただけでオーバーフローなので都合が悪いでしょう。
92
+
93
+ ---
94
+
95
+ 行方向走査で、配列のコピーをやめてインプレース処理にすると関数自体の速度は1秒を切ります(0.6くらい)。使いたいかどうかはわかりませんが、一応載せておきます。
96
+ ```python
97
+ @jit("i4[:,:](i4[:,:])", nopython=True)
98
+ def f2_i(B):
99
+ for i in range(B.shape[0]): # 行のループ
100
+ before = 0
101
+ start_pos = 0
102
+ cnt = 0
103
+ for j in range(B.shape[1]): # 列のループ
104
+ if B[i, j] == 1: # 1のとき
105
+ if before == 0: # 直前の状態が0なら1にして数え始める
106
+ start_pos = j
107
+ before = 1
108
+ cnt += 1 # 数える
109
+ else: # 0のとき
110
+ if before != 1: # 直前の状態が0なら無視して続ける
111
+ continue
112
+ else: # 直前の状態が1のとき
113
+ # 1が出た範囲をcntで埋める
114
+ B[i, start_pos:j] = cnt
115
+
116
+ # 状態をリセットする
117
+ before = 0
118
+ cnt = 0
119
+ # 行が終わって状態が1のとき
120
+ if before == 1:
121
+ B[i, start_pos:B.shape[1]] = cnt
122
+ return B
123
+
124
+ ```
125
+
126
+ 逆に言うとコピーに時間がかかると思うので、新しい配列を返すつもりであれば本体の処理の高速化で受けられる恩恵はそんなにないのかもしれません。

9

追記

2020/04/06 21:38

投稿

hayataka2049
hayataka2049

スコア30939

answer CHANGED
@@ -85,4 +85,7 @@
85
85
 
86
86
  どちらにしても、処理時間のかなりの割合は1.6GBもある配列の生成とコピーで食っています。ということで、これで実用上問題にはならないでしょう。Cythonで同じロジックをやるともう少し速い可能性はあるのですが、numbaのjitコンパイルだって優秀です。
87
87
 
88
+ (ベンチマーク取ってるページがありましたが、これを見るとnumbaでいいじゃんとなる・・・
89
+ [Python を高速化する Numba, Cython 等を使って Julia Micro-Benchmarks してみた - Qiita](https://qiita.com/yniji/items/b7acffa02f03a94882e5))
90
+
88
91
  int16やint8にすれば多少速くなりますが、8bitだと128個1が続いただけでオーバーフローなので都合が悪いでしょう。

8

追記

2020/04/06 18:38

投稿

hayataka2049
hayataka2049

スコア30939

answer CHANGED
@@ -83,6 +83,6 @@
83
83
 
84
84
  こちらは6秒でした。
85
85
 
86
- どちらにしても、処理時間のかなりの割合は1.6GBもある配列の生成とコピーで食っています。関数自体は一瞬で返るみたなので、これでいでしょう。
86
+ どちらにしても、処理時間のかなりの割合は1.6GBもある配列の生成とコピーで食っています。うことで、これで実用上問題にはならないでしょう。Cythonで同じロジックをやるともう少し速い可能性はあるのですが、numbaのjitコンパイルだって優秀です。
87
87
 
88
88
  int16やint8にすれば多少速くなりますが、8bitだと128個1が続いただけでオーバーフローなので都合が悪いでしょう。

7

追記

2020/04/06 18:35

投稿

hayataka2049
hayataka2049

スコア30939

answer CHANGED
@@ -83,6 +83,6 @@
83
83
 
84
84
  こちらは6秒でした。
85
85
 
86
- どちらにしても、処理時間のかなりの割合は1.6GBもある配列の生成で食っています。関数自体は一瞬で返るみたいなので、これでいいでしょう。
86
+ どちらにしても、処理時間のかなりの割合は1.6GBもある配列の生成とコピーで食っています。関数自体は一瞬で返るみたいなので、これでいいでしょう。
87
87
 
88
88
  int16やint8にすれば多少速くなりますが、8bitだと128個1が続いただけでオーバーフローなので都合が悪いでしょう。

6

追記

2020/04/06 18:32

投稿

hayataka2049
hayataka2049

スコア30939

answer CHANGED
@@ -1,7 +1,9 @@
1
1
  numbaで書いてみました。手元でいくつかのテストケースでは確認しましたが、絶対に正しいとは言い切れないので、ちゃんと動くかはご自身で確認してください。
2
2
 
3
- 方針としては、とにかくpython側で処理してしまうと遅いので、配列ごとnumbaに投げます。numba側のプログラムの書き方は見ての如くです。
3
+ 方針としては、とにかくpython側で処理してしまうと遅いので、配列ごとnumbaに投げます。
4
4
 
5
+ numba側のプログラムの書き方は見ての如くです。CとかFORTRANのノリで書いてください。numpyの関数を呼び出すよりそっちの方が速いのです(新たなnumpy配列を返す関数はメモリ上に新たな配列を作るのですべて本質的には遅いのです)。
6
+
5
7
  ```python
6
8
  import numpy as np
7
9
  from numba import jit

5

passよりcontinueの方が意味が明確だし、もしかしたら微妙に最適化されるかもと思って(測った限り効いてない)

2020/04/06 18:30

投稿

hayataka2049
hayataka2049

スコア30939

answer CHANGED
@@ -21,7 +21,7 @@
21
21
  cnt += 1 # 数える
22
22
  else: # 0のとき
23
23
  if before != 1: # 直前の状態が0なら無視して続ける
24
- pass
24
+ continue
25
25
  else: # 直前の状態が1のとき
26
26
  for k in range(start_pos, j): # 1が出た範囲をcntで埋める
27
27
  B[k, i] = cnt
@@ -63,7 +63,7 @@
63
63
  cnt += 1 # 数える
64
64
  else: # 0のとき
65
65
  if before != 1: # 直前の状態が0なら無視して続ける
66
- pass
66
+ continue
67
67
  else: # 直前の状態が1のとき
68
68
  for k in range(start_pos, j): # 1が出た範囲をcntで埋める
69
69
  B[i, k] = cnt

4

追記

2020/04/06 18:23

投稿

hayataka2049
hayataka2049

スコア30939

answer CHANGED
@@ -45,6 +45,7 @@
45
45
  列方向に見ていくのはキャッシュ効率の観点からするとあまり好ましくありません。ということで、同じロジックで列ベクトルではなく行ベクトルを扱うバージョンの関数も作ってみました。
46
46
 
47
47
  入力を転置して与えてください。結果も転置されたものが出てきます。
48
+ (転置がビューかコピーかで変わるかな? とも思ったのですが、これで速くなったのでたぶんいいのでしょう)
48
49
 
49
50
  ```python
50
51
  @jit("i4[:,:](i4[:,:])", nopython=True)

3

追記

2020/04/06 18:21

投稿

hayataka2049
hayataka2049

スコア30939

answer CHANGED
@@ -80,4 +80,6 @@
80
80
 
81
81
  こちらは6秒でした。
82
82
 
83
- どちらにしても、処理時間のかなりの割合は1.6GBもある配列の生成で食っています。関数自体は一瞬で返るみたいなので、これでいいでしょう。
83
+ どちらにしても、処理時間のかなりの割合は1.6GBもある配列の生成で食っています。関数自体は一瞬で返るみたいなので、これでいいでしょう。
84
+
85
+ int16やint8にすれば多少速くなりますが、8bitだと128個1が続いただけでオーバーフローなので都合が悪いでしょう。

2

送信エラー

2020/04/06 18:15

投稿

hayataka2049
hayataka2049

スコア30939

answer CHANGED
@@ -78,4 +78,6 @@
78
78
 
79
79
  ```
80
80
 
81
- こちらは6秒でした。
81
+ こちらは6秒でした。
82
+
83
+ どちらにしても、処理時間のかなりの割合は1.6GBもある配列の生成で食っています。関数自体は一瞬で返るみたいなので、これでいいでしょう。

1

追記

2020/04/06 18:12

投稿

hayataka2049
hayataka2049

スコア30939

answer CHANGED
@@ -38,4 +38,44 @@
38
38
  ```
39
39
 
40
40
 
41
- 2万の正方行列で10秒くらいでした。
41
+ 2万の正方行列で10秒くらいでした。
42
+
43
+ ---
44
+
45
+ 列方向に見ていくのはキャッシュ効率の観点からするとあまり好ましくありません。ということで、同じロジックで列ベクトルではなく行ベクトルを扱うバージョンの関数も作ってみました。
46
+
47
+ 入力を転置して与えてください。結果も転置されたものが出てきます。
48
+
49
+ ```python
50
+ @jit("i4[:,:](i4[:,:])", nopython=True)
51
+ def f_trans(A):
52
+ B = A.copy()
53
+ for i in range(B.shape[0]): # 行のループ
54
+ before = 0
55
+ start_pos = 0
56
+ cnt = 0
57
+ for j in range(B.shape[1]): # 列のループ
58
+ if B[i, j] == 1: # 1のとき
59
+ if before == 0: # 直前の状態が0なら1にして数え始める
60
+ start_pos = j
61
+ before = 1
62
+ cnt += 1 # 数える
63
+ else: # 0のとき
64
+ if before != 1: # 直前の状態が0なら無視して続ける
65
+ pass
66
+ else: # 直前の状態が1のとき
67
+ for k in range(start_pos, j): # 1が出た範囲をcntで埋める
68
+ B[i, k] = cnt
69
+ # 状態をリセットする
70
+ before = 0
71
+ cnt = 0
72
+ # 行が終わって状態が1のとき
73
+ if before == 1:
74
+ for k in range(start_pos, B.shape[1]):
75
+ B[i, k] = cnt
76
+
77
+ return B
78
+
79
+ ```
80
+
81
+ こちらは6秒でした。