Closed7

paizaでClojureの勉強 - 【シミュレーション 1】反復横跳び (paizaランク B 相当)

むらむーむらむー

反復横跳びの回数(k)から左を何回通過したかを計算する関数

2回までは0、それ以降は4回やるごとにカウントアップするので、以下のようなコードになる
/ だけだとratioになるので、intにキャストする必要がある

(defn count-left-jump [k]
  (int (/ (+ k 1) 4)))

テストコード

(deftest count-left-jump-test
  (is (= (count-left-jump 0) 0))
  (is (= (count-left-jump 1) 0))
  (is (= (count-left-jump 2) 0))
  (is (= (count-left-jump 3) 1))
  (is (= (count-left-jump 4) 1))
  (is (= (count-left-jump 5) 1))
  (is (= (count-left-jump 6) 1))
  (is (= (count-left-jump 7) 2))
  (is (= (count-left-jump 8) 2))
  (is (= (count-left-jump 9) 2))
  (is (= (count-left-jump 10) 2))
  (is (= (count-left-jump 11) 3))
  (is (= (count-left-jump 12) 3))
  (is (= (count-left-jump 13) 3))
  (is (= (count-left-jump 14) 3))
  (is (= (count-left-jump 15) 4))
  (is (= (count-left-jump 16) 4)))
むらむーむらむー

左と中央の間を通過した回数

  • ちょうど左の地点にいるとき以外は、左を通過した回数 * 2回通過している
  • ちょうど左の地点にいるときは、まだ片道しか通ってないので、左を通過した回数 * 2回 - 1

※ Clojureにおいて、booleanを返す関数名には、慣習的に関数名の末尾に?をつけることが推奨されている

(defn is-just-left-point? [k]
  (zero? (mod (+ k 1) 4)))

(defn count-pass-between-left-and-center [k]
  (let [count (* (count-left-jump k) 2)]
    (if (is-just-left-point? k) (- count 1) count)))

テストコード

(deftest count-pass-between-left-and-center-test
  (is (= (count-pass-between-left-and-center 3) 1))
  (is (= (count-pass-between-left-and-center 4) 2))
  (is (= (count-pass-between-left-and-center 6) 2))
  (is (= (count-pass-between-left-and-center 7) 3))
  (is (= (count-pass-between-left-and-center 8) 4))
  (is (= (count-pass-between-left-and-center 10) 4))
  (is (= (count-pass-between-left-and-center 11) 5))
  (is (= (count-pass-between-left-and-center 12) 6)))
むらむーむらむー

左と中央の間を通過した回数の合計の移動距離を求める

まず、左と中央の間を通過した、その回数での移動距離を求める

  • Nが1の場合
    • [0-2] : 0
    • [3-4] : 1 * x
    • [5-6] : 2 * x
  • Nが2の場合
    • [0-4] : 0
    • [5-8] : 1 * x
    • [9-12] : 2 * x

なので以下のようなコード

(defn calc-extra-moved-at [pass-count x n]
  (let [divider (* 2 n)
        times (- (int (Math/ceil (/ pass-count divider))) 1)]
    (* x times)))

テストコード

(deftest calc-extra-moved-at-test
  (testing "Given [x n] = [1 1]"
    (is (= (calc-extra-moved-at 2 1 1) 0))
    (is (= (calc-extra-moved-at 3 1 1) 1))
    (is (= (calc-extra-moved-at 4 1 1) 1))
    (is (= (calc-extra-moved-at 5 1 1) 2))
    (is (= (calc-extra-moved-at 6 1 1) 2))
    (is (= (calc-extra-moved-at 7 1 1) 3))
    (is (= (calc-extra-moved-at 8 1 1) 3))
    (is (= (calc-extra-moved-at 9 1 1) 4)))
  (testing "Given [x n] = [3 2]"
    (is (= (calc-extra-moved-at 2 3 2) 0))
    (is (= (calc-extra-moved-at 4 3 2) 0))
    (is (= (calc-extra-moved-at 5 3 2) 3))
    (is (= (calc-extra-moved-at 8 3 2) 3))
    (is (= (calc-extra-moved-at 9 3 2) 6)))
  (testing "Given [x n] = [2 3]"
    (is (= (calc-extra-moved-at 6 2 3) 0))
    (is (= (calc-extra-moved-at 7 2 3) 2))
    (is (= (calc-extra-moved-at 12 2 3) 2))
    (is (= (calc-extra-moved-at 13 2 3) 4))))
むらむーむらむー

次に、合計の移動距離を求める
各通過回数時点での、移動距離のリストを作って、そのリストの要素の合計を出す

(defn calc-total-extra-moved [pass-count x n]
  (let [extra-moved-list (map #(calc-extra-moved-at (inc %) x n) (range pass-count))]
    (reduce + extra-moved-list)))

テストコード

(deftest calc-total-extra-moved-test
  (testing "Given [x n] = [3 1]"
    (is (= (calc-total-extra-moved 2 3 1) 0))
    (is (= (calc-total-extra-moved 3 3 1) 3))
    (is (= (calc-total-extra-moved 4 3 1) 6))
    (is (= (calc-total-extra-moved 5 3 1) 12)))
  (testing "Given [x n] = [3 2]"
    (is (= (calc-total-extra-moved 4 5 2) 0))
    (is (= (calc-total-extra-moved 5 5 2) 5))
    (is (= (calc-total-extra-moved 8 5 2) 20))
    (is (= (calc-total-extra-moved 9 5 2) 30))))
むらむーむらむー

問題を勘違いしていた。。4Nの倍数で伸びていくと思ったけど、4Nのときにだけ伸びるだけだった。。

なので、余計に移動した距離を求めるには、以下の計算式

("合計の左と中央の間を通過した回数" - "4N回までに左と中央の間を通過した回数") * 伸ばした距離

コードにすると以下

(defn calc-extra-moved [n x k]
  (let [pass-count (count-pass-between-left-and-center k)
        pass-count-before-extend (count-pass-between-left-and-center (* n 4))]
    (* x (- pass-count pass-count-before-extend))))

テストコード

(deftest calc-extra-moved-test
  (is (= (calc-extra-moved 1 3 10) 6))
  (is (= (calc-extra-moved 2 5 20) 30)))
むらむーむらむー

コードの全体像

https://github.com/eno314/clojure-practice/pull/5

提出コード

(ns clojure-practice.paiza.libs
  (:require [clojure.string :as string]))

(defn split-line-by-space [line]
  (string/split line #" "))

(defn read-int-values-line []
  (vec (map #(Integer/parseInt %) (split-line-by-space (read-line)))))

(defn count-left-jump [k]
  (int (/ (+ k 1) 4)))

(defn is-just-left-point? [k]
  (zero? (mod (+ k 1) 4)))

(defn count-pass-between-left-and-center [k]
  (let [count (* (count-left-jump k) 2)]
    (if (is-just-left-point? k) (- count 1) count)))

(defn calc-extra-moved [n x k]
  (let [pass-count (count-pass-between-left-and-center k)
        pass-count-before-extend (count-pass-between-left-and-center (* n 4))]
    (* x (- pass-count pass-count-before-extend))))

(defn main []
  (let [[n x k] (read-int-values-line)]
    (println (calc-extra-moved n x k))))

(main)
このスクラップは2023/07/06にクローズされました