Twitter APIのScalaラッパーを作っている話
はじめに
夏休み、特にすることがなかったのとTwitter4Jに憧れてScalaでTwitter4SというTwitter APIのラッパーを作り始めた。
初めは標準ライブラリだけでごり押すつもりが色々ありいくつかライブラリを使用しているがある程度出来た為、概要をまとめる。
ただまだまだ作りこまないといけないため、今後の更新によってはこのブログの記事は当てにならなくなる。
Github上で管理してるものが全てであり、どのように動作するかはソースコードが物語っている。
Github
このライブラリは次のレポジトリで公開されている。
また、github pagesでこれらのjarファイルを公開しているため次をbuild.sbtに追加すると自分のプロジェクトで使用できるようになる。
resolvers += "Maven Repo on github" at "https://lrf141.github.io/Twitter4S/" libraryDependencies ++= Seq( "Twitter4S" % "twitter4s_2.12" % "1.0.0" )
主要なクラスやオブジェクトに関するドキュメントを次のリンクで公開しているので何かあればそちらも参照して欲しい。
ソースコード概要
ドキュメントで公開してるソースファイルとは別にnet,utilパッケージが存在する
ディレクトリ構成は次の通り。
├── src │ ├── main │ │ └── scala │ │ └── twitter4s │ │ ├── APIKeys.scala │ │ ├── HomeTimeLine.scala │ │ ├── SearchBase.scala │ │ ├── Status.scala │ │ ├── StatusBase.scala │ │ ├── TimeLineBase.scala │ │ ├── TimeLineUser.scala │ │ ├── Tweet.scala │ │ ├── Tweets.scala │ │ ├── Twitter.scala │ │ ├── TwitterFactory.scala │ │ ├── TwitterImpl.scala │ │ ├── UserArray.scala │ │ ├── UserStatus.scala │ │ ├── UserTimeLine.scala │ │ ├── net │ │ │ ├── HttpRequest.scala │ │ │ ├── HttpResponse.scala │ │ │ └── oauth │ │ │ └── OAuthRequest.scala │ │ └── util │ │ └── JsonDecoder.scala
net package
このパッケージではhttp requestやOAuthに関するクラスを管理している。
OAuthRequest.scalaでOAuth認証に必要なパラメータや署名などを生成できる。
Twitter APIを利用するにあたってOAuthはver 1.0に準拠している。
それらを利用してHttpRequest.scalaでPOST/GETに関する機能を実装している。
util package
このパッケージではこのプロジェクトで使用するネットワークや本体に関係ないクラスを管理している。
この記事公開時点ではJsonDecoder.scalaという中身が非常に行儀悪いクラスのみである。
このクラスではStringの値として受け取ったjsonを解析しcase classのメンバに代入することを主としている。
jsonの解析にcirceというscala製のライブラリを使用している。
src/main/scala直下 twitter4s package
ここではtwitter apiを利用するための色々なクラスやオブジェクト、トレイトが存在する。
主にjsonを解析した際、データの受け皿となるcase classだが、リクエストに必要なメソッドを含むトレイトやそれを実際に実装しているTwitterImpl.scalaなどがメインになっている。
必要な機能は***Base.scalaという名前でトレイトとして存在しそれらはTwitter.scalaのトレイトにミックスインされている。
このライブラリを使用する際にはそれらを実装したTwitterImplのメソッドを呼び出している。
またそのクラスを継承したTwitterFactoryクラスがありライブラリユーザはこれからインスタンスを受け取り各種メソッドを呼び出す仕組みになっている。
このクラスはシングルトンである。
加えてcase classをjson解析で多用しているため解析によって受け取った値は好きなように使用することが出来る。
使用方法
最初に書いたようにbuild.sbtもしくはBuild.scala内にresolversとlibraryDependenciesを追加してもらえるとすぐに使用できる。
具体的な使用方法については公開しているレポジトリのREADME.mdを参照して欲しい。
プルリクについて
プルリクについては、たまに「英語でないとダメ」という意見も見受けられるがこのレポジトリに関しては日本語でのプルリクも受け付けている。
公開されているソースコードをみて、「このコードクソだな」などある場合は積極的にプルリクを送って欲しい。
形態素解析とngram、マルコフ連鎖を用いてもののけ姫風の文章を生成する。
形態素解析とngram,マルコフ連鎖を組み合わせる
前回紹介した記事では
ngramをいくつかの文字で分割するタイプにしたが今回は形態素解析を用いていくつかの形態素で分割し、マルコフ連鎖を使って文章を生成する。
今回の概要
形態素解析は今回はライブラリを用いて行う。
言語はScalaで書くので「kuromoji」を用いる。
ビルドツールにsbtを用いているのでbuild.sbtに次を追加。
resolvers += "Atilika Open Source repository" at "http://www.atilika.org/nexus/content/repositories/atilika" libraryDependencies ++= Seq( "org.atilika.kuromoji" % "kuromoji" % "0.7.7" )
前回は夏目漱石の「こころ」を対象データにしたが今回はもののけ姫のサンでいこうと思う。
このサイトから借りた。
lovegundam.dtiblog.com
形態素解析を行う
今回はkuromojiという日本語の形態素解析器を使うので上記の依存関係などを記述しておく必要がある。
形態素解析が何かはウィキペディア等を参照してほしい。
ライブラリを使うと簡単なので今回はコードのみ紹介する。
import org.atilika.kuromoji.{Tokenizer,Token} import collection.JavaConversions._ import scala.collection.mutable.{Map,ArrayBuffer} import scala.io.{Source,BufferedSource} object NgramMorph{ private [this] val tokenizer = Tokenizer.builder.mode(Tokenizer.Mode.NORMAL).build private [this] val n:Int = 1 def main(args:Array[String]):Unit = { val sourceLine:Iterator[String] = Source.fromResource("san_tmp.dat").getLines for(line <- sourceLine){ val tokens = tokenizer.tokenize(line) tokens.foreach{ t => val token:Token = t.asInstanceOf[Token] println(s"${token.getSurfaceForm}") } } } }
マルコフ連鎖を用いて会話文を自動生成する
ここではngramとマルコフ連鎖を用いる。
今回はn = 1とする。
import org.atilika.kuromoji.{Tokenizer,Token} import collection.JavaConversions._ import scala.collection.mutable.{Map,ArrayBuffer} import scala.io.{Source,BufferedSource} import java.util.Random object NgramMorph{ private [this] val tokenizer = Tokenizer.builder.mode(Tokenizer.Mode.NORMAL).build private [this] val n:Int = 1 def main(args:Array[String]):Unit = { val sourceLine:Iterator[String] = Source.fromResource("san_tmp.dat").getLines val ngramMap:Map[String,ArrayBuffer[String]] = Map[String,ArrayBuffer[String]]() //一行ずつ形態素解析をしてngramで分割 for(line <- sourceLine){ val tokens = tokenizer.tokenize(line) var nowSurface:String = tokens.head.asInstanceOf[Token].getSurfaceForm if( !ngramMap.contains(nowSurface) ) ngramMap += (nowSurface -> ArrayBuffer.empty[String]) tokens.tail.foreach{ t => val token:Token = t.asInstanceOf[Token] var tmpSurface:String = token.getSurfaceForm var ngramArray:ArrayBuffer[String] = ngramMap(nowSurface) ngramArray += tmpSurface ngramMap.update(nowSurface,ngramArray) if(!ngramMap.contains(tmpSurface)) ngramMap += (tmpSurface -> ArrayBuffer.empty[String]) nowSurface = tmpSurface } } //マルコフ連鎖を使って会話文生成 val rnd:Random = new Random var result_str:String = ngramMap.keys.toList(rnd.nextInt(ngramMap.size)) var tmp_str:String = result_str while(!result_str.contains("。")){ var next_wordArray:ArrayBuffer[String] = ngramMap(tmp_str) tmp_str = next_wordArray(rnd.nextInt(next_wordArray.size)) result_str += tmp_str } println(result_str) } }
このプログラムを10回動かして得た出力結果は次の通り
きかないもの…。 動きはじめた。 知らせてもここはシシ神さまがおかしいの礼を言いな。 だろう。 においで木をシシ神にこのことも人間のか。 味方だよ。 アシタカ、人間くさい。 味方だ。 けがれるだけだから…。 がんばって。
今回はもののけ姫のサンのセリフを対象に会話文生成を行ったが、前回使用した夏目漱石のこころに比べると圧倒的に量が少ないので微妙な結果になってしまったが形態素解析を用いたので意味不明な文は格段に減少した。
ちなみに前回使った夏目漱石のこころを同様に試すと次のようになった
姿を引いたの驚いたけれども横文字のさせる勇気に、私のです。 捌けた。 邯鄲という意味を読み出しました。 盛り潰そう。 邪魔をできるだけ切り詰めたの様子が一面に、気の外れた室で、因循らしく見える動作はよほど経っていなん。 済まなくなります。 使用さが少しもそれが帰った軽薄に書く事まで書いたと私に、お母さん一週間のようにはすぐ引き受けたので私はいよいよまた、私に向ってもらった。 会いにしてなお目立ちます。 鈍って見に吹き払った。 曇りが、こっそりこの世にもう熱心に気が付くはずが、彼の中に裹まれて植え付けられてくれさえ頼りに取った。
n = 3あたりでやるとうまくいくかもしれない。
n-gramとマルコフ連鎖の理論と実装、もちろんScalaを使って。
n-gramとは
n-gramとは形態素解析のように辞書を使って文章を品詞分解していくのではなく「文字単位で文章を分割していく」手法のこと。
なので、形態素解析のように辞書を必要とするわけではない。
またn-gramのnが何を指すかというとn文字で分割するという意味である。
1-gramは1文字ずつ分割する。
2-gramは2文字ずつ分割する。
3-gramは3文字ずつ分割する。
…
みたいに。
実際には次のように分割していく。
文章:今日は雨のようだ。 条件:n = 2とする 分割: 今日 日は は雨 雨の のよ よう うだ だ。
ただここで問題なのがnが小さすぎるとその自然言語にもよるが不適切な結果を生むことに成る。
文章:東京都庁 条件:n = 2 分割: 東京 京都 都庁
n-gramを使って文字列分割を行うと本来想定している最適解とは異なる場合がある。
今回で言えば、「東京都庁」という意味を持つのに本来の意味とかけ離れた「京都」が出てしまうなど。
そこで日本語の文章を分割するときはn=3を想定するっぽい。
簡単故にこれを実際の会話で使う文章生成に使うと割と精度が下がる。
n-gramのサンプル
今回は、「青空文庫」さんから夏目漱石の「こころ」をお借りしました。
事前にutf-8にしてあれこれ邪魔な記述を抜かして使用します。
これに対してn = 3の時のn-gram、つまりtri-gramで分割しました。
ソースコードは次の通り。
import scala.io.Source import scala.io.BufferedSource import scala.collection.mutable.Map object Ngram{ //3文字ずつ分割していく private [this] val n:Int = 3 def main(args:Array[String]):Unit = { //文字列とその出現頻度を格納 var ngramMap:Map[String,Int] = Map[String,Int]() //ファイルを読み込む val source = Source.fromFile("kokoro.dat","utf-8") //1行ずつ読み込む val lines:Iterator[String] = source.getLines //ngramで分割し格納 for(line <- lines){ var tmp_line:String = line while(tmp_line.length >= this.n){ var subString:String = tmp_line.take(this.n) //切り出した文字列が今までに出てないなら追加 //出現しているなら出現回数の更新 if(!ngramMap.contains(subString)){ ngramMap += (subString -> 1) }else{ var count:Int = ngramMap(subString) + 1 ngramMap.update(subString, count) } //先頭文字の除去 tmp_line = tmp_line.tail } } val result_key:Iterable[String] = ngramMap.keys for(key <- result_key){ println(s"${key} ${ngramMap(key)}") } } }
これをコンパイルして次のように実行する。
$ scala Ngram | sort -r -nk2 | head
そうすると、よく使われる部分文字列ベスト10が切り出せた。
部分文字列 | 出現回数 |
した。 | 1464 |
ました | 1026 |
った。 | 856 |
です。 | 789 |
のです | 776 |
かった | 593 |
たので | 562 |
なかっ | 470 |
ってい | 397 |
奥さん | 386 |
語尾とか接続詞的なあれこれは頻度高いのわかるが「奥さん」…。
マルコフ連鎖とは
マルコフ連鎖とは、ある事象がその直近の事象のみに影響を受けて確率的に変化するという考えかた。
これにはマルコフ性という、次に起こる事象が過去に起きた事象の影響を受けないという性質がなければならない。
もっと言えば、マルコフ性を備えた確率過程のことをマルコフ過程といい、その中でも状態空間が連続ではなく離散的である場合を特にマルコフ連鎖という。
身近な例を上げるなら、すごろくや人生ゲームといったものが一種のマルコフ連鎖の例と言える。
n-gramとマルコフ連鎖を使った文章生成
本来は確率を使ってどうこうするけど、Scalaでそれをやってしまうと処理速度的に心配なので次に出てくる文字列をひたすら追加しそこからランダムで選択することにした。
それが次のコード
import scala.io.{Source,BufferedSource} import scala.collection.mutable.{Map,ArrayBuffer} import scala.util.Random object Markov{ //tri-gramを想定 private [this] val n:Int = 3 def main(args:Array[String]):Unit = { //ngramの分割結果と次に続く文字列を格納する var ngramMap:Map[String,ArrayBuffer[String]] = Map[String,ArrayBuffer[String]]() //ファイルから一行ずつ読み込む val source:BufferedSource = Source.fromFile("kokoro.dat","utf-8") val lines:Iterator[String] = source.getLines //ngramで分割 for(line <- lines){ var tmp_line:String = line var split_str:String = tmp_line.take(this.n) //先頭文字列だけ独立して追加 if(!ngramMap.contains(tmp_line.take(this.n))) ngramMap += ( split_str -> ArrayBuffer.empty[String] ) tmp_line = tmp_line.tail while(tmp_line.length >= this.n){ var ngramArray:ArrayBuffer[String] = ngramMap(split_str) ngramArray += tmp_line.take(this.n) ngramMap.update(split_str,ngramArray) if( !ngramMap.contains(tmp_line.take(this.n)) ) ngramMap += (tmp_line.take(this.n) -> ArrayBuffer.empty[String]) split_str = tmp_line.take(this.n) tmp_line = tmp_line.tail } } //先頭文字列選択を行う val rnd:Random = new Random var result_str:String = ngramMap.keys.toList(rnd.nextInt(ngramMap.size)) var tmp_str:String = result_str while(!result_str.contains("。")){ var next_wordArray:ArrayBuffer[String] = ngramMap(tmp_str) tmp_str = next_wordArray(rnd.nextInt(next_wordArray.size)) result_str += tmp_str.last } println(result_str) } }
これを実行することによって得たいくつかの文章は次の通り
ひどい文もできていたので比較的ましなものを選択した。
叔父だの叔母だの、その性質としてよかった。 耳に異様な調子をもっと底の方に沈んだ心を大事そうに聞いた。 端書を受けない事はできないかと疑ってみました。 かも彼らは、退屈のために箒で背中を向けた。 今東京へ出て来たといって褒めるのですから、私には分りませんでした。 生と異なる点を明白に述べなければ純白でなくっちゃなるまですでに妻を迎えたらしい。 父に万事をやっ付けて離さない悠長な田舎にありませんでした。 思うかと、奥さんをどうしてもいいました 酸にも聞こえる試しはまるで頼りにするのは定めていた私が、それは迷惑そうな顔をちょっと顔だけ向け直して来て、いつの間にか暮れて春になりました。 まだ起きているのであった。
ngramだと単語が意味を持たないので、計算コストは低いけど出来上がる文が時折変な文になってしまう。
マルコフ連鎖を採用しているのも原因の一つであるけど。
Oracle Code Japan Tour in Sapporoに行ってきた
はじめに
Oracle Code Japan TourがSapporoにくるとのことで初めて情報系のイベントに参加してみた。
というわけで実際に得た情報をあれこれまとめてみた。
すごいメモのような感じになっているので間違ってたらコメントなどくれると嬉しい。
Feed Back from Java Day Tokyo 2017
Java + Dockerが現在利用できるようになっている。
javaだけでなくoracleの製品も利用できるようになった。
java ee8修正案としてhttp2の実装がありになったりリアクティブプログラミングができるようになったりしていくとのこと
非同期と同期を組み合わせて高速化を図る。
javaの最新情報は以下にある
builder.japan.zdnet.com
Raspberry Pi with Java 9
jlinkというコマンドの話
必要なモジュールなどのみでjreを作成できるコマンド
ラズパイみたいにリソースが限られている時に有効
java se9ではhttp2のあれこれが乗っかる。
se9ではarm v5とarm v6(raspberry pi a,b,b+,zero)には未対応
そこでjavaを使いたいなら8以下のものを使うひつようが出てくる。
javafxをiphone,androidで使いたくなったらgluonが開発してるヤツを使える。
gluonhq.com
javaでラズパイのあれこれを動かすためのプロジェクトがあるためハードウェア制御などをjavaでも行うことができる。
ここではそのデモをみて大半が終わってしまった。
ただラズパイ上でjavaを動かすのも楽しそうだと思った。
Cloud Native Java EE
ここはjava ee使わんしネットワークとかあれこれやってないから普通に話聞いて面白いなぐらいしか感想がない。
ただdockerと組み合わせるのが面白かった。
あと
JOnsen – Java Unconference at a Japanese Onsen
っていうアンカンファレンスの話聞けてよかった。
Java SE 9のススメ
ここではjava9での互換性、新機能、改善点などについての話だった
互換性
java9で互換性がなくなったものがいくつかある
言語仕様とライブラリ
java se8ではアンダースコアを1文字変数名として使用できたが警告がでる状況だったが、se9からはアンダースコア1文字だけではコンパイルエラーとなる。
@deprecatedの付いているメソッドについて、今まではずっと残ってきたものでも本当になくなる可能性があるとのこと。
特にGUI関連のものや、OSに比較的依存したものがJigsawプロジェクトによりブラックオックス化される可能性が高いそう。
保守、運用
virtual VM,hprof,jhatなどなどのツールがno moreとなってしまう。
デフォルトのGCが現行のものだとParallel GCなところがG1GCに変更になる。
またそれ以前のCMSはdeprecatedになり今後のアップデートで消える可能性がある。
CMSとG1GCでは使えないのでゴリゴリチューニングしている人は注意が必要。
ただしメモリサイズが小さいマシンなどではparallel GCを使うのがよい。
新機能
今回は大きく分けて3つ
- Project Jigsaw
- JShell
- Reactive Streams
が紹介された。
Project Jigsaw
背景:大規模システムになるとクラスパスが300個近くになることがあるが、実際バージョン違いだけだったりするのにこれはどうなのという問題
俗に言うjar hellに陥る問題。
ライブラリ更新で動作不良を起こしたり、publicがpublicすぎる問題などなど。
これらを辿って行くと依存性の欠如であったり、公開範囲を適切に設定できないなどなど。
これをモジュールにして解決しようというのがProject Jigsaw
モジュールに関して
現行のjarはほぼそのまま使える。
jarファイル中にメタ情報を付加する。
それらを管理するmodule-info.javaの有無が現行とse9のおおきな違い。
クラスパスが消えてモジュールパスに統一される可能性がある。
その他としてjava.baseにjava.langやjava.utilなどが統一されScalaっぽくimportしなくても使えるみたい
依存性の調査は
jdeps -s ファイル名.jar
でそのファイルが使ってるモジュールを表示してくれる。
それをもとにモジュールのあれこれを作成できるようになる。
JShell
javaのreplツール
jshellコマンドを打つと使用できるようになる。
ちなみに行末の;を省略してもいい
同じ変数名で別のデータ型の変数を宣言すると一番最後に変更したものに置換される。
Reactive Streams
非同期処理のためのインターフェース
http://www.reactice-streams.org
back pressureがあるタイプのPublish-Subscribeモデル。
subscriberがpublisherに要求するとsubscriptionが独自に生成されデータがどれだけ通過するか制限できるだか把握できるだか
ココらへんはあまり実感がわかなかった
改善
project coin
stream,collection
Stream
ファクトリメソッドとして
- ofNullable
- iterate
が追加される。
そのほかのメソッドでは
- take/dropWhile
^ Collectors.flatMapping
- Collectors.filtering
が追加され関数型のあれこれが割と使えるようになる。
Optionalはstream,flatMapと併用を前提にした設計になる。
ifPresentOrElseが追加され、値がなかった時(Else)にどうするか記述できるようになる。
orの中にoptionalを書けるようになる。
ofメソッドも追加され
List<Integer> list = List.of(1,2,3)
と実装できる。
これはイミュータブルとして生成される。
その他
javadocがhtml5に対応。
検索窓が追加されるがデフォルトで検索窓にフォーカスがあてられるためスペースでしたにいけなくなった。
リリースは2017/09/21に変更になった。
JDK 9 Early-Access Builds
ここで最新情報をあされる。
日本語版NetBeans8.2をUbuntuにいれてみた
なぜかNetBeansをいれることになった
友達曰く、「NetBeansが英語なのとプロジェクトを開けない」とのことで実際に検証してみた。
普段はVSCodeかVimとsbtでScala,Javaしか書かない人間のUbuntuにはそんな便利なもの入ってるわけもなく
実際に導入しながらそれらを試してみた。
NetBeans日本語化できないのか?
最初から日本語化されてるのあったっぽい
インストールした
次のコマンド打ったらこんな出力でた
$ sh netbeans-8.2-javase-linux.sh インストーラを構成しています... システムでJVMを検索しています... インストール・データを抽出しています... インストーラ・ウィザードを実行中...
これでインストーラが起動したので
どんどん次へなり、進むなりを押していく
すると最高に嫌な予感がする。
こんなのが出てきた。
これ無視し続けて、なんとかインストールすると
起動はするが、プロジェクト開くときにこんなのがでた
まぁ開けないわな。
開き方を変えた
$ cd netbeans-8.2
そこでさらにbinディレクトリに移動する。
~/netbeans-8.2$ cd bin ~/netbeans-8.2/bin$
ここまできたら、次のコマンドを実行してjdkとやらの場所を確認する
~/netbeans-8.2/bin$/usr/sbin/update-java-alternatives -l java-1.8.0-openjdk-amd64 1081 /usr/lib/jvm/java-1.8.0-openjdk-amd64 java-8-oracle 1077 /usr/lib/jvm/java-8-oracle
みたいなことになるから、jdkのほうをコピペして次のコマンドで実行する
~/netbeans-8.2/bin$./netbeans --jdkhome /usr/lib/jvm/java-1.8.0-openjdk-amd64
これで使えるようになった(はず)。
最後に
一回使ってみたけど、やっぱりNetBeans使いにくい。
【メモ】sbtで自作ライブラリを使えるようにした話
とりあえず公開したかった。
なんか公開したかった。
sbtで使えるようにしたかった。
ライブラリをビルドする。
とりあえず、sbtプロジェクトに作成してるであろうBuild.scala,build.sbt的なあれこれに次を追加する。
Build.scalaならsettingsの中に突っ込んでください。
それ以外のname,version,scalaVersionはもうすでに設定したものとする。
publishTo := Some(Resolver.file("名前",file("jar,pomなどを作成するディレクトリを指定"))(Patterns(true, Resolver.mavenStyleBasePattern)))
ここで一度プロジェクトをコンパイルする。
$ sbt compile
これでokなら次を実行する
$ sbt publish
するとさっき指定したディレクトリに「名前」のディレクトリとその直下に「名前_ScalaVersion」のディレクトリが生成されている
github pagesを作成しそこにビルドしたやつをぶん投げる
公開したいライブラリがgithubのレポジトリにあるとして、そのレポジトリにgh-pagesというブランチを作る。
とりあえずそこにindex.htmlを投げる。
そしていつもどおりgh-pagesにpushする。
その勢いでさっきビルドしたあれこれをぶん投げる。
手順はこれで終わり。
wgetでテストする
次のコマンドを走らせてdlできればok
wget https://[username].github.io/reponame/projectname/projectname_scalaVersion/version/projectname_scalaVersion-version.jar
これでダウンロードできないなら何かしら失敗しているか、gh-pagesの反映に時間がかかることがあるので最初にリンクを張ったサイトをみてどうにかしてみる。
sbtで使えるかテストする
適当にプロジェクト作るかどうにかして別のBuild.scalaで使うには次のようにする
//ライブラリを追加するところに "projectName" % "projectName_scalaVersion" % "Version" //resolverを作って次を記述 resolvers += "Maven Repo on github" at "https://[username].github.io/projectName/"
これでダウンロードできる(はず)
Newton-Raphson法と単位円内接正多角形を使った円周率近似をScalaでやってみた
こんなやつをコードにした
まずNewton-Raphson法は次の式におけるxの値を求める
このあと登場するけど、単位円内接正多角形を使う近似では漸化式をつかっていくわけだけども
その中で平方根を計算しないといけないのでCを任意実数定数として関数g(x)を次のようにする
すると次の式におけるxを近似的に求めることになる
Newton-Raphson法ではこれを次の式を満たす様に微分を使いながら近似していく
ここまでNewton-Raphson法
次に単位円を用いて円周率を求める。
しかし、円周がわからない。
これは円周率が不明であるためである。
そこで単位円内に内接正多角形を作って円周とみなす。
これは漸化式で表現でき正n角形のnが大きくなればなるほどその周の長さが円周の長さに近づく。
その内接正多角形の一辺の長さが次の式となる
というわけでコードにしてみた
実際に書いたコード
object calcPI{ val max = 20 def main(args:Array[String]):Unit = { var Ln = newtonRaphson(2)//L_1 var n = 0 var pow = 2 while( n < max ){ println( pow * Ln ) Ln = newtonRaphson(2 - newtonRaphson(4-Ln*Ln)) n += 1 pow *= 2 } } def newtonRaphson(c:BigDecimal):BigDecimal = { var x:BigDecimal = c var dx:BigDecimal = -func(x,c) / dfunc(x) while( 1.0e-16 < dx.abs ){ x += dx dx = -func(x,c) / dfunc(x) } return x } def func(x:BigDecimal, c:BigDecimal):BigDecimal = x*x - c def dfunc(x:BigDecimal):BigDecimal = 2.0*x }
これを実行して、出力結果をdatファイルにおとしてgnuplotでグラフにするとこんな感じになる
BigDecimal使ってはいるけど精度的にはまだまだかなと思うけどある程度できた。