どうすればclojureでべき乗を行うことができますか?今のところ、必要なのは整数のべき乗だけですが、問題は分数にも当てはまります。
14 に答える
古典的な再帰(これを見て、スタックを吹き飛ばします)
(defn exp [x n]
(if (zero? n) 1
(* x (exp x (dec n)))))
末尾再帰
(defn exp [x n]
(loop [acc 1 n n]
(if (zero? n) acc
(recur (* x acc) (dec n)))))
機能的
(defn exp [x n]
(reduce * (repeat n x)))
卑劣な(スタックも吹き飛ばしますが、それほど簡単ではありません)
(defn exp-s [x n]
(let [square (fn[x] (* x x))]
(cond (zero? n) 1
(even? n) (square (exp-s x (/ n 2)))
:else (* x (exp-s x (dec n))))))
図書館
(require 'clojure.contrib.math)
Clojureには、うまく機能するべき関数があります。Clojureの任意精度の数値タイプをすべて正しく処理するため、Java相互運用機能を経由するのではなく、これを使用することをお勧めします。名前空間clojure.math.numeric-towerにあります。
べき乗ではなくexpt
、べき乗が求められます。これは、見つけるのが少し難しい理由を説明している可能性があります...とにかくここに小さな例があります(機能しますが、より適切に使用することに注意してpower
ください):pow
use
require
(require '[clojure.math.numeric-tower :as math :refer [expt]]) ; as of Clojure 1.3
;; (use 'clojure.contrib.math) ; before Clojure 1.3
(expt 2 200)
=> 1606938044258990275541962092341162602522202993782792835301376
パッケージのインストールに関するリマインダー
最初にJavaパッケージorg.clojure.math.numeric-tower
をインストールして、Clojure名前空間にclojure.math.numeric-tower
アクセスできるようにする必要があります。
コマンドライン:
$ lein new my-example-project
$ cd lein new my-example-project
次に、依存関係ベクトルを編集project.clj
して追加します。[org.clojure/math.numeric-tower "0.0.4"]
lein REPLを開始します(clojure REPLではありません)
$ lein repl
今:
(require '[clojure.math.numeric-tower :as math])
(math/expt 4 2)
;=> 16
また
(require '[clojure.math.numeric-tower :as math :refer [expt]])
(expt 4 2)
;=> 16
JavaMath.pow
またはBigInteger.pow
メソッドを使用できます。
(Math/pow base exponent)
(.pow (bigdec base) exponent)
この質問が最初に行われたとき、clojure.contrib.math/exptはこれを行うための公式のライブラリ関数でした。それ以来、clojure.math.numeric-towerに移動しました
user=> (.pow (BigInteger. "2") 10)
1024
user=> (.pow (BigInteger. "2") 100)
1267650600228229401496703205376
メソッドではなく関数が本当に必要な場合は、単純にラップできます。
(defn pow [b e] (Math/pow b e))
そして、この関数では、それをキャストすることができますint
。関数はメソッドよりも便利なことがよくあります。これは、関数をパラメーターとして別の関数に渡すことができるためです。この場合map
、私の頭に浮かびます。
Javaの相互運用を本当に回避する必要がある場合は、独自のべき関数を作成できます。たとえば、これは単純な関数です。
(defn pow [n p] (let [result (apply * (take (abs p) (cycle [n])))]
(if (neg? p) (/ 1 result) result)))
これは、整数指数(つまり、根がない)のべき乗を計算します。
また、多数を処理する場合は、BigInteger
の代わりにを使用することをお勧めしますint
。
また、非常に大きな数を扱う場合は、それらを数字のリストとして表現し、結果を計算して他のストリームに出力するときに、それらを介してストリーミングする独自の算術関数を作成することをお勧めします。
これもうまくいくと思います:
(defn expt [x pow] (apply * (repeat pow x)))
SICPは、上記の「卑劣な」実装の完全な反復高速バージョンに影響を与えました。
(defn fast-expt-iter [b n]
(let [inner (fn [a b n]
(cond
(= n 0) a
(even? n) (recur a (* b b) (/ n 2))
:else (recur (* a b) b (- n 1))))
]
(inner 1 b n)))
clojure.math.numeric-tower
以前はを使用しclojure.contrib.math
ます。
(ns user
(:require [clojure.math.numeric-tower :as m]))
(defn- sqr
"Uses the numeric tower expt to square a number"
[x]
(m/expt x 2))
末尾再帰を使用し、負の指数をサポートする「卑劣な」メソッドの実装:
(defn exp
"exponent of x^n (int n only), with tail recursion and O(logn)"
[x n]
(if (< n 0)
(/ 1 (exp x (- n)))
(loop [acc 1
base x
pow n]
(if (= pow 0)
acc
(if (even? pow)
(recur acc (* base base) (/ pow 2))
(recur (* acc base) base (dec pow)))))))
reduceを使用した単純なワンライナー:
(defn pow [a b] (reduce * 1 (repeat b a)))
試す
(defn pow [x n]
(loop [x x n n r 1]
(cond
(= n 0) r
(even? n) (recur (* x x) (/ n 2) r)
:else (recur x (dec n) (* r x)))))
末尾再帰のO(log n)ソリューションの場合、自分で実装する場合(正の整数のみをサポート)。明らかに、より良い解決策は、他の人が指摘しているライブラリ関数を使用することです。
私は個人的に使用します:
(defn pow [x n] (reduce *' (repeat n x)))
アスタリスクの後のアポストロフィ(')に注意してください。
すべてのサイズの整数に適しています。
注:これは、一部の実装では少し遅い場合があります。(時間(pow 2 200000))システムで解決するのに1.2秒かかりました。
clojure.contrib.genric.math-functionsはどうですか
clojure.contrib.generic.math-functionsライブラリにpow関数があります。これはMath.powの単なるマクロであり、Java数学関数を呼び出すための「clojureish」な方法です。