2

4clojure Problem 150の回文数パズルが解決策を投稿したときにタイムアウトになる理由がわかりません。私のローカル REPL では、すべてがかなり高速です。このソリューションを見つけて動作しますが、パフォーマンスは私のソリューションと同様です。コードが多いことはわかっていますが、問題は簡単ではありません。コメントがアイデアを得るのに役立つことを願っています。

(defn palindrome [n] 
  (let [ ;; given a number return a string
        digits (fn [^String x]  (String/valueOf x))              
    ;; given a string return the length
        digits-count  (fn [^String x] ( .length x) ) 
    ;; given an input sequence of digits, return a number. e.g. (seqstr (seq "1234")) => 1234
        seqstr  #(Long/parseLong (apply str %)) 
    ;; true when the given string has an even number of digits:
    even-digits? #(even? (digits-count %)) 
    ;; return the left middle of a string. For even-digit strings returns the 'real' left part, for odd-digit strings returns one digit more. 
    ;; e.g. (left-middle "12345678") => "1234"
    ;; e.g. (left-middle "1234567")  => "1234"
        left-middle #(if (even-digits? %) 
              (subs % 0 (quot (digits-count % ) 2) ) 
          (subs % 0 (inc (quot (digits-count % ) 2)))) 
    ;; mirror a given number. 
    ;; (mirror [1234 :even] ) => 12344321
    ;; (mirror [1234 :odd] )  => 1234321
        mirror (fn [[num dig]]
           (let [ d (seq (digits num))]
            (if (= :even dig) (seqstr (concat d (reverse d)))    
                        (seqstr (concat d (drop 1 (reverse d)))))))

    ;; initialize loop given a string. Returns a vector given a starting point.
    ;; (init "12345678") => [1234 :even 10000]
    ;; (init "1234567" ) => [1234 :odd  10000]
    ;; the first item in the vector contains the number we start the iteration with.
    ;; the flag indicates whether we should mirror it to an even or odd-digit number
    ;; the goal (power of 10) indicates when the next switch between odd/even-digit numbers will occur
    init #(let [s (left-middle %)] 
           (vector (Long/parseLong s) 
                (if (even-digits? %) :even :odd) 
            (long (Math/pow 10 (digits-count s)))))

    ;; given a vector (see structure above), determine the next step
      ;;(next  [999 :even 1000] ) => [1000 :odd 10000] . When finished mirroring even-digit numbers of length 3, continue with mirroring 4-digit odd-digit numbers
      ;;(next [9999 :odd 10000] ) => [1000 :even 10000]. When finished mirroring odd-digit numbers of length 4, continue with mirroring 4-digit even-digit numbers
    ;;(next [123 :odd 1000])    => [124 :odd 1000]. The normal case.
    next (fn [[num even goal]] 
         (let [m (inc num)] 
          (if (= m goal)
            (if (= even :even)
              [goal :odd (* 10 goal)]
              [(/ goal 10) :even goal])
            [m even goal] )))
   i  (init (digits n)) 
   palindromes (iterate next i) ] 
  (filter (partial <= n ) (map mirror palindromes))))

上記のコードを REPL に貼り付けると、次の単体テストを評価できます。

(= (take 26 (palindrome 0))
   [0 1 2 3 4 5 6 7 8 9 
    11 22 33 44 55 66 77 88 99 
    101 111 121 131 141 151 161])

(= (take 16 (palindrome 162))
   [171 181 191 202 
    212 222 232 242 
    252 262 272 282 
    292 303 313 323])

(= (take 6 (palindrome 1234550000))
   [1234554321 1234664321 1234774321 
    1234884321 1234994321 1235005321])  

(= (first (palindrome (* 111111111 111111111)))
   (* 111111111 111111111))

(= (set (take 199 (palindrome 0)))
   (set (map #(first (palindrome %)) (range 0 10000))))

(= true 
  (apply < (take 6666 (palindrome 9999999))))

(= (nth (palindrome 0) 10101)
   9102019)

「最も遅い」テスト (番号 5) では、次のパフォーマンスが得られます。

user=> (time (= (set (take 199 (palindrome 0)))
   (set (map #(first (palindrome %)) (range 0 10000)))))
  "Elapsed time: 66.509472 msecs"

4clojure のホームページにある「タイムアウト」の基準がよくわかりません。私の意見では、1秒未満のものはすべて機能するはずです。JRE 7 では Clojure 1.5.1 を、JRE 6 では Clojure 1.2.1 を使用しました。

このパズルを解くのは私の 4 回目の試みであり、私の解決策のほとんどはテスト 5 に苦労していますが、遅いと言う意味がはっきりとわかりません。RAM の使用量が多すぎますか? 再帰?私はこの言語に慣れていないので、クールなヒントをいただければ幸いです。

私の最初の解決策は少し遅いですが、私のマシンでは約 600ms で終了します。

(defn palindrome [n] 
  (let [
    digits         #(seq (str %))      ;; given a number return a seq of its digits
    digits-count   #(count (digits %)) ;; given a number return how many digits it has 
    seqstr         #(Long/parseLong (apply str %))  ;; seq to number
    start          (seqstr (take (/ (digits-count n) 2) (digits n)))
    digits-range   #(map long (range (max (Math/pow 10 (dec %)) start) (Math/pow 10 %)))  ;; return all numbers of given digits length
    mirror         #(let [d (digits %1)]
                       (if (true? %2) (seqstr (concat d (reverse d)))     ;; mirror 1234 true  => 12344321 even number of digits 
                                      (seqstr (concat d (drop 1 (reverse d)))))) ;; mirror 1234 false => 1234321  odd number of digits

    digits-seq     #(drop (/ (digits-count %) 2) (range)) ;; e.g. when input number has 10 digits, result is a seq: 5, 6, 7, 8, ....
                                                          ;;      when input number has  9 digits, result is a seq: 5, 6, 7, 8, ....
    input-even     (even? (digits-count n))                                                   
    ]
(filter #(<= n %) (cons 0 (mapcat #(concat (map (fn [x]  (mirror x false))  (if (and input-even (* 2 %)) [] (digits-range %))) 
                                  (map (fn [x]   (mirror x true ))  (digits-range %)) ) (digits-seq n) ))))) 
4

1 に答える 1

2

各 4clojure パズルには、1 つ以上の教育目的があります。あなたは正しいアルゴリズムを持っていますが、タイムアウトテストは、文字列との間の変換が多すぎるためにあなたを罰しています.

犯人は、数値を文字列に変換し、それを操作してから数値に戻すミラー関数です。

文字列の変換と操作を必要としないこの代替手段をプラグインしてみてください。

    mirror (fn [[num dig]]
             (loop [a num, r (if (= dig :even) num (quot num 10))]
               (if (= 0 r)
                 a
                 (recur (+ (* a 10) (mod r 10)), (quot r 10)))))

これは、元の投稿に基づいた完全な合格ソリューションであり、その他の小さな変更がいくつかあります。

(fn palindrome [n] 
  (let [even-digits? #(even? (count %)) 
        left-middle #(if (even-digits? %) 
                       (subs % 0 (quot (count % ) 2) ) 
                       (subs % 0 (inc (quot (count % ) 2)))) 
        mirror (fn [[num dig]]
                 (loop [a num r (if (= dig :even) num (quot num 10))]
                   (if (= 0 r)
                     a
                     (recur (+ (* a 10) (mod r 10)) (quot r 10)))))
        init #(let [s (left-middle %)] 
                (vector (Long/parseLong s) 
                        (if (even-digits? %) :even :odd) 
                        (long (Math/pow 10 (count s)))))
        nextp (fn [[num even goal]] 
               (let [m (inc num)] 
                 (if (= m goal)
                   (if (= even :even)
                     [goal :odd (* 10 goal)]
                     [(/ goal 10) :even goal])
                   [m even goal] )))
        i  (init (str n)) 
        palindromes (iterate nextp i) ] 
    (filter (partial <= n ) (map mirror palindromes))))
于 2013-10-08T15:59:29.527 に答える