30代SEの自由帳

最初のタイトルは頓挫した

ヒューマン・リソース・マシーン 攻略 入社40年目 素因数に分解せよ

ここで解説している内容なりヒントなりはあくまで筆者の解法に基づいたものなので、別の考え方ももちろんあるよ。ってのは念頭に置いてください。

課題

左側のパネルの数値に対し、素因数分解を行い、答えを右側に運んで下さい。
答えは昇順、すなわち小さい順に運んで下さい。

使用可能な命令

  • inbox
  • outbox
  • copyfrom
  • copyto
  • add
  • sub
  • bump+
  • bump-
  • jump
  • jump_if_zero
  • jump_if_neg

効率目標

  • サイズ:28行
  • スピード:399ステップ

ヒント

その1

試しに割ってみて、割り切れたら因数
2から順番にやることで必然的に素因数になる
※これは試し割り法というアルゴリズム(手法)

その2(スピード目標)

2以外の偶数を試し割するのは無駄

その3(スピード目標)

試し割る数字を2(初項)の次だけ+1、それ以降は+2していけば偶数は飛ばせる

その4(スピード目標)

今回は最初だけ、+1したくない状況。 このゲームでは条件ジャンプが0とマイナスなので、最初だけ0かマイナスになるような判定値を用意すれば良い

回答例 + 解説

サイズ

回答例 + 解説

ラベル1// 1セットの開始位置
1. copyfrom 24
2. copyto B
3. bump+ B
4. bump+ B
5. inbox
ラベル3// Aを更新
6. copyto A
ラベル4// 試し割り開始
7. copyfrom 24
8. copyto cnt
9. copyfrom A
ラベル5// 割り算
10. sub B
11. jump_if_zero:ラべル212. jump_if_neg:ラベル6// 商を更新して割り算を継続
13. copyto temp
14. bump+ cnt
15. copyfrom temp
16. jump:ラベル5へ
ラベル6// Bを更新して試し割りを継続
17. bump+ B
18. jump:ラベル4へ
ラベル2// 因数を運ぶ
19. copyfrom B
20. outbox
21. copyfrom cnt
22. jump_if_zero:ラベル1// Aを更新して試し割りを継続
23. bump+ cnt // 余りに対する判定を行ってからcntを更新するため、この時点ではcnt=商-1なので+1してからAを更新する
24. jump:ラベル3

2から順番に試しに割ってみて、割り切れたら素因数として運ぶ。これを割れなくなるまで繰り返す。 ※試し割り法というアルゴリズム(手法)

初期化用に0を取得(copyfrom 24)し、一旦割る数(=B)として設定(copyto)。
割る数の初項は2のため2になるまで加算(bump+)

左のパネルを拾って(inbox)、割られる数(=A)として設定(copyto)
0を拾って(copyfrom)、商用のカウンタ(=cnt)を初期化(copyto)
割れなくなる(jump_if_zero|jump_if_neg)まで割り算を繰り返す(sub B ~ jump)
この時、割り切れなかった場合は割る数だけ更新する必要があるため、Aとは別に計算過程用のカーペット(temp)を用意して、 計算過程についてはそちらに保持(copyto)する

  • 割り切れた(jump_if_zero)場合 Bを因数として運ぶ(copyfrom -> outbox)。cntが0なら(jump_if_zero)割り算を終了し次のパネルへ
    0じゃないならば、cntを+1(bump+)してAを更新(copyto)し、同じBで再度試し割りを行う(jump)

  • 割り切れなかった(jump_if_neg)場合 Bを更新(bump+)し、再度試し割りを行う(jump)

サイズ + スピード

回答例 + 解説

ラベル1// 1セットの開始位置
1. copyfrom 24
2. copyto B
3. bump+ B
4. copyto flg // 手元に1がある状態なのでflgの初期設定に利用する
5. bump+ B
6. inbox
ラベル3// Aを更新
7. copyto A
ラベル4// 試し割り開始
8. copyfrom 24
0. copyto cnt
10. copyfrom A
ラベル5// 割り算
11. sub B
12. jump_if_zero:ラべル213. jump_if_neg:ラベル6// 商を更新して割り算を継続
14. copyto temp
15. bump+ cnt
16. copyfrom temp
17. jump:ラベル5へ
ラベル6// Bを更新して試し割りを継続
18. bump- flg
19. jump_if_zero:ラベル7// B+1
20. bump+ B
ラベル7:B+1
21. bump+ B
22. jump:ラベル4へ
ラベル2// 因数を運ぶ
23. copyfrom B
24. outbox
25. copyfrom cnt
26. jump_if_zero:ラベル1// Aを更新して試し割りを継続
27. bump+ cnt // 余りに対する判定を行ってからcntを更新するため、この時点ではcnt=商-1なので+1してからAを更新する
28. jump:ラベル3

サイズ目標を満たしている前提で、そこからの変更点を

素因数で考えると、2以外の偶数を試し割するのは無駄
試し割る数字を2(初項)の次だけ+1、それ以降は+2することで偶数の試し割りを飛ばす

そのため、Bを更新する際は基本的には+2になるようにbump+を2回実施するように変更する。 ただし、初回だけは+1としたいため、bump+を1回分飛ばせるようにフラグ(flag)を用意し、初回分についてカバーする。
※今回のように特定の状況を示すための概念をフラグ(flag)と言う。特に覚える必要はない

フラグの考え方としては、「初期値=0として、jump_if_zeroで飛んだ先でbump+ flg」もしくは「初期値=1として、jump_if_zeroで飛ばなかったケースでbump- flg」と
ON/OFFで管理するのが正しい考え方だが、
サイズ/スピードの節約のため、値の取得/更新をまとめて実施出来る「初期値=1として、bump- flgしてからjump_if_zero」を採用
※なので厳密にはフラグとは違うけど、細かいことは気にしない。

  

<前:39年目> <目次> <次:41年目>