Project

General

Profile

Wiki » History » Version 4

hori, 04/24/2023 11:39 PM

1 1 hori
# 公開の記録
2 2 hori
3
{{toc}}
4
5 4 hori
## <2023-04-24 Mon 23:31> Anthy の日本語入力
6
7
手元の作業マシンは Ubuntu 20.04 のデスクトップ版を利用していて、日本語入力には Mozc を使っていた。
8
ところが、どうも誤変換が多くて使い物にならない [^1] ということで Mozc は諦め、Anthy に戻した。
9
いつ頃からそうなったのか不明だが、キーバインドを柔軟に割り当てられるようになったため、問題なく戻せた。
10
11
個人的に一番重要と思うのは IME のオン・オフ (日本語入力と直接入力) のために指を大きく移動させたくないというのと、オンとオフを異なるキーバインドにしたいという点だ。
12
いくつか試してみたが今一番上手く言っている気がするのは `Ctrl + j` でひらがなモード、`Ctrl + ;` で半角英数モード、とすることだ。
13
これが指の位置的には最小の動きで済むというのと、意外に他のキーバインドと干渉しない点が気に入っている。
14
15
[^1]: 変換の精度も問題だが、変換候補の文節の切り方が異常で、どう考えてそうは切らないだろうというところで切られた単位で変換候補が表示されるのが辛いところ。
16
17
## <2023-01-03 Tue 19:49> ABC 274 E の TLE
18 3 hori
19
独力で解けなかったので https://atcoder.jp/contests/abc274/submissions/35636696 の回答を参考に実装してみたら、TLE になってしまったので、原因を調べてみた。
20
21
ほぼ同じ実装である https://atcoder.jp/contests/abc274/submissions/35887265 が AC なのに、なぜ TLE したのかと思って細かく見ていったところ、DP の漸化式に相当する「訪問済みノードのビットセット」の更新処理に問題があることが分かった。
22
23
公式回答の Python では以下のように、ビットセット `s` に `(1<<j)` というビットを追加するために XOR を使用している。
24
25
```
26
     if dp[j][s^(1<<j)]>new_dist: dp[j][s^(1<<j)]=new_dist
27
```
28
29
Ruby に書き換える際に、この XOR を深く考えず安直にコピーしてしまったために、余計な遅延が乗って時間制限をオーバーしてしまったということだった。
30
31
実際簡単なベンチマークでビット XOR 演算子とビット OR 演算子の速度の違いを比較してみる。
32
33
```
34
$ cat xor_or.rb
35
S = (1<<17)*17*17
36
37
t1 = Time.now
38
S.times do |i|
39
  x = i | (S - 1)
40
end
41
t2 = Time.now
42
p t2 - t1
43
44
t1 = Time.now
45
S.times do |i|
46
  x = i ^ (S - 1)
47
end
48
t2 = Time.now
49
p t2 - t1
50
```
51
52
ABC274E の最大規模に相当するループ回数で実行してみると、以下のように 1 秒程度の差が生じた。
53
XOR は OR よりも遅いことを認識しておかないといけない。
54
55
```
56
$ ruby xor_or.rb
57
2.797993445
58
3.898028078
59
```
60
61
少し関係ないが、本問題を検証している上で、ビットカウントのルーチンの効率について調べてみたので、一応書いておく。
62
63
```
64
$ cat popcount_test.rb
65
# https://stackoverflow.com/questions/1639723/ruby-count-the-number-of-1s-in-a-binary-number に基づく最適なルーチン
66
def popcount(x)
67
  m1 = 0x55555555
68
  m2 = 0x33333333
69
  m4 = 0x0f0f0f0f
70
  x -= (x >> 1) & m1
71
  x = (x & m2) + ((x >> 2) & m2)
72
  x = (x + (x >> 4)) & m4
73
  x += x >> 8
74
  return (x + (x >> 16)) & 0x3f
75
end
76
77
M = 20
78
79
t1 = Time.now
80
(1 << M).times do |i|
81
  popcount i
82
end
83
t2 = Time.now
84
85
# 整数型に [] でアクセスすると bit がセットされているかどうかで 0, 1 を返す
86
t3 = Time.now
87
(1 << M).times do |i|
88
  M.times.count {|j| i[j] > 0}
89
end
90
t4 = Time.now
91
92
t5 = Time.now
93
(1 << M).times do |i|
94
  i.digits(2).size
95
end
96
t6 = Time.now
97
p t2 - t1
98
p t4 - t3
99
p t6 - t5
100
101
$ ruby popcount_test.rb
102
0.355551277
103
3.333421961
104
0.839962182
105
```
106
107
ということで、独自ルーチン `popcount` を用いるのが最速のようだ。
108
ライブラリに登録しておこう。
109
110 2 hori
## AtCoder ABC 276 E について <2022-11-06 Sun 00:07>
111
112
https://atcoder.jp/contests/abc276/editorial/5162
113
公式解法のとおり、BFS や DSU を用いたにも関わらず TLE を解決できなかったので振り返る。
114
アルゴリズムの問題ではなく、Ruby の基本的なデータ構造の問題で遅くなっていた。
115
116
グリッド問題の入力は以下のような文字列で与えられるが、これをどういうデータ構造に落とすのがよいかという問題。
117
118
```
119
5 7
120
.#...#.
121
..#.#..
122
...S...
123
..#.#..
124
.#...#.
125
```
126
127
安直なものでは以下のような格納方式が考えられる。
128
129
1. 1 つの文字列として格納する方式
130
   ```
131
   ".#...#...#.#.....S.....#.#...#...#."
132
   ```
133
134
2. 1 次元の文字の配列として格納する方式
135
   ```
136
   [".", "#", ".", ".", ".", "#", ".", ".", ".", ...]
137
   ```
138
139
3. 各行ごとの文字列の配列として格納する方式
140
   ```
141
   [".#...#.",
142
    "..#.#..",
143
    "...S...",
144
    "..#.#..",
145
    ".#...#."]
146
   ```
147
148
4. 文字の 2 次元配列として格納する方式
149
   ```
150
   [[".", "#", ".", ".", ".", "#", "."],
151
    [".", ".", "#", ".", "#", ".", "."],
152
    [".", ".", ".", "S", ".", ".", "."],
153
    ...]
154
   ```
155
156
私はこれまで割と雰囲気で 1. や 2. を選んでしまっていた。
157
また、過去に別問題を変に学習してしまったようで 2D 配列は 1D 配列にした方が速い (添字を毎回変換していた) と思い込んでいたのもある。
158
159
一回自前で計測しておくと安心ということで測ってみた。
160
1000x1000 の適当なグリッドを作って各セルを走る処理を 5 回実行した時間を比較する (単位は秒)。
161
162
|   |                    | データロード | 全処理時間 |
163
|---|--------------------|--------------|------------|
164
| 1 | 文字列             | 0.22         | 4.35       |
165
| 2 | 一次元文字配列     | 4.96         | 6.49       |
166
| 3 | 行ごとの文字列配列 | 0.006        | 5.12       |
167
| 4 | 二次元文字配列     | 0.21         | 2.84       |
168
169
データロードにおいて一次元文字配列が遅いのは `array1 + array2` のような連結処理をしているからで、これは以前遅いことを学習済み。
170
文字列の添字でのアクセスよりも `string.chars` で文字れ配列に分割した方が、データロードには多少時間がかかるが、アクセスは高速になるようだ。
171
172
ということで、今後はグリッド問題が出た時は二次元文字配列で処理するようにしてみる。
Go to top