Project

General

Profile

Actions

Bug #3676

closed

CMath.cbrt(-8)の結果が複素数にならない

Added by tadf (tadayoshi funaba) almost 11 years ago. Updated about 10 years ago.

Status:
Closed
Priority:
Normal
Target version:
-
ruby -v:
ruby 1.9.3dev (2010-08-10 trunk 28952) [i686-linux]
Backport:
[ruby-dev:41972]

Description

=begin
CMath.cbrt(-8)の結果が複素数にならない。
=end

Actions #1

Updated by matz (Yukihiro Matsumoto) almost 11 years ago

=begin
まつもと ゆきひろです

In message "Re: [ruby-dev:41972] [Bug #3676] CMath.cbrt(-8)の結果が複素数にならない"
on Tue, 10 Aug 2010 21:28:22 +0900, tadayoshi funaba redmine@ruby-lang.org writes:

|CMath.cbrt(-8)の結果が複素数にならない。

現在のCMath.cbrt(-8)の結果は-2で、これは3乗すると-8になるとい
う意味で正しい値に思えるのですが。Cmath.cbrt(-8)が返すべき
「正しい」値はなんだとお考えですか?

あるいは(-2+0i)を返すべきという主張なのかもしれませんが、現在
のCMathの関数群は、全般に「Mathの関数群が実数の範囲で定義され
ずエラーになる場合、CMathの対応する関数は複素数を返す」という
設計になっており、ことさらcbrtについてだけ変える必要があるよ
うには思えません。

=end

Actions #2

Updated by tadf (tadayoshi funaba) almost 11 years ago

=begin

現在のCMath.cbrt(-8)の結果は-2で、これは3乗すると-8になるとい
う意味で正しい値に思えるのですが。Cmath.cbrt(-8)が返すべき
「正しい」値はなんだとお考えですか?

絶対的に正しい値はないと思いますが、おおよそ、Complex(-8) ** (1.0/3) で
ある (1.0+1.73205080756888i) ではないでしょうか。

あるいは(-2+0i)を返すべきという主張なのかもしれませんが、現在
のCMathの関数群は、全般に「Mathの関数群が実数の範囲で定義され
ずエラーになる場合、CMathの対応する関数は複素数を返す」という
設計になっており、ことさらcbrtについてだけ変える必要があるよ
うには思えません。

複素数を値域、定義域にする関数なら、複素数の範囲内でもっとも適切な値を
返すべきだと思います。逆に言えば、Math.cbrt は実数の範囲内では、-2.0 を
返すのが妥当だからそうなっているのではないかと思います。CMath は実数で
表現できない場合に複素数をつかう関数ではなく、複素数を値域、定義域に持
つ関数群なのではないでしょうか。

=end

Actions #3

Updated by matz (Yukihiro Matsumoto) almost 11 years ago

=begin
まつもと ゆきひろです

In message "Re: [ruby-dev:41980] Re: [Bug #3676] CMath.cbrt(-8)の結果が複素数にならない"
on Tue, 10 Aug 2010 23:22:12 +0900, Tadayoshi Funaba tadf@dotrb.org writes:

|> 現在のCMath.cbrt(-8)の結果は-2で、これは3乗すると-8になるとい
|> う意味で正しい値に思えるのですが。Cmath.cbrt(-8)が返すべき
|> 「正しい」値はなんだとお考えですか?
|
|絶対的に正しい値はないと思いますが、おおよそ、Complex(-8) ** (1.0/3) で
|ある (1.0+1.73205080756888i) ではないでしょうか。

そうなんですか、複素数の範囲では一意に決まらないということな
んですかね。無知ですいません。

|複素数を値域、定義域にする関数なら、複素数の範囲内でもっとも適切な値を
|返すべきだと思います。逆に言えば、Math.cbrt は実数の範囲内では、-2.0 を
|返すのが妥当だからそうなっているのではないかと思います。CMath は実数で
|表現できない場合に複素数をつかう関数ではなく、複素数を値域、定義域に持
|つ関数群なのではないでしょうか。

繰り返しになりますが、現状のCMathは「複素数の範囲内でもっと
も適切な値を返す」という設計にはなっていませんね。そうすべき
であるかどうか、私は判断できる知識がありませんが、そうだとす
るとCMathそのもののあり方を変更するような大きな判断だと思い
ます。

=end

Actions #4

Updated by tadf (tadayoshi funaba) almost 11 years ago

=begin

繰り返しになりますが、現状のCMathは「複素数の範囲内でもっと
も適切な値を返す」という設計にはなっていませんね。そうすべき
であるかどうか、私は判断できる知識がありませんが、そうだとす
るとCMathそのもののあり方を変更するような大きな判断だと思い
ます。

まつもとさんが言う意味と僕のそれは違う感じがしますが、「適切な値」とい
うところでは課題があるのは間違いなさそうです。

本当は、1.9 の議論で、Math が複素数をあつかうようにして、場合によっては
C のライブラリに投げてしまうようにしたいなと思っていたのです。実は、こ
の部分に限っては後でむらけんさんが C に任せろという意見にほぼ賛成だった
のですが、あっさり却下され、こっちもこの件で延々議論する余裕もないので、
そこに到る議論はなくなりました。

いまの実装は、1.8 のもの殆どそのままですが、十数年くらい前の段階で議論
が進んで、業界での標準的な考えが出来ていた筈なのですが、ruby の複素数関
数はそこに追い付いていないと思います。そういう意味で適切な値でないに違
いないです。

まつもとさんが言っているのはそういう意味じゃないと思いますが、定義域と
値域があって関数がある、Math は実数に対して定義がある。CMath は複素数に
対して定義される、というのは常識的な事で、議論の余地はないくらいに思い
ます。CMath は制限の撤廃、あるいは定義域値域の拡大だと思います。

=end

Actions #5

Updated by mame (Yusuke Endoh) almost 11 years ago

=begin
遠藤です。

まず、これを変えたのは私です。[ruby-core:31234] でバグ報告が
来たためでした。相談せずに変えてしまってすみません。

2010年8月10日23:22 Tadayoshi Funaba tadf@dotrb.org:

現在のCMath.cbrt(-8)の結果は-2で、これは3乗すると-8になるとい
う意味で正しい値に思えるのですが。Cmath.cbrt(-8)が返すべき
「正しい」値はなんだとお考えですか?

絶対的に正しい値はないと思いますが、おおよそ、Complex(-8) ** (1.0/3) で
ある (1.0+1.73205080756888i) ではないでしょうか。

高校レベルの複素数の知識しかないので、Complex#**(Rational) が
n 乗根のどれを返すかは不定だと思っていました。
仰角が正で最小のものが「おおよそ正しい」んですかね。

少なくとも Complex#** の仕様として決まっているなら、できれば
rdoc に書いておいて欲しいです。

2010年8月11日0:35 Tadayoshi Funaba tadf@dotrb.org:

まつもとさんが言っているのはそういう意味じゃないと思いますが、定義域と
値域があって関数がある、Math は実数に対して定義がある。CMath は複素数に
対して定義される、というのは常識的な事で、議論の余地はないくらいに思い
ます。CMath は制限の撤廃、あるいは定義域値域の拡大だと思います。

私はまつもとさんと同じ感覚でした (CMath は Math の挙動を拡張する
だけで変えはしない) 。[ruby-core:31234] の人も同じ感覚だと思います。
ちなみに後で登場するに違いないと思いますが、むらけんさんはふなば
さんと同じ感覚のようです。
この機会にコンセンサス (や将来的な方針) を決めとくとよさそうですね。

--
Yusuke Endoh mame@tsg.ne.jp

=end

Actions #6

Updated by mrkn (Kenta Murata) almost 11 years ago

=begin
むらたです。

On 2010/08/11, at 1:08, Yusuke ENDOH wrote:

2010年8月10日23:22 Tadayoshi Funaba tadf@dotrb.org:

現在のCMath.cbrt(-8)の結果は-2で、これは3乗すると-8になるとい
う意味で正しい値に思えるのですが。Cmath.cbrt(-8)が返すべき
「正しい」値はなんだとお考えですか?

絶対的に正しい値はないと思いますが、おおよそ、Complex(-8) ** (1.0/3) で
ある (1.0+1.73205080756888i) ではないでしょうか。

高校レベルの複素数の知識しかないので、Complex#**(Rational) が
n 乗根のどれを返すかは不定だと思っていました。
仰角が正で最小のものが「おおよそ正しい」んですかね。

仰角が正で最小のものを主値 (principal value) と言い、
多価関数を一価関数とするために使用されます。
http://ja.wikipedia.org/wiki/%E4%B8%BB%E5%80%A4

n乗根の主値は1の原始n乗根の実数倍になっています。
http://ja.wikipedia.org/wiki/1%E3%81%AE%E5%86%AA%E6%A0%B9

ご存知の通り、この主値さえあればすべての解を順番に生成できます:

a = Complex(-8)(1.0/3) # 主値
w = a / a.abs # 1の原始3乗根
a * w #=> (-0.9999999999999996+1.7320508075688776i)
a * w
2 #=> (-2.0+7.771561172376096e-16i)

しかし、負数の奇数乗根はかならず実解を持ちますから、
主値ではなく実解をダイレクトに手に入れる方法があっても良いと思います。
なぜなら、上の方法では浮動小数点演算による誤差を含んでしまうことと、
ダイレクトに求められれば必要なかった計算を必要としているからです。

(引用の順序を入れ替えます)

2010年8月11日0:35 Tadayoshi Funaba tadf@dotrb.org:

まつもとさんが言っているのはそういう意味じゃないと思いますが、定義域と
値域があって関数がある、Math は実数に対して定義がある。CMath は複素数に
対して定義される、というのは常識的な事で、議論の余地はないくらいに思い
ます。CMath は制限の撤廃、あるいは定義域値域の拡大だと思います。

私はまつもとさんと同じ感覚でした (CMath は Math の挙動を拡張する
だけで変えはしない) 。[ruby-core:31234] の人も同じ感覚だと思います。
ちなみに後で登場するに違いないと思いますが、むらけんさんはふなば
さんと同じ感覚のようです。
この機会にコンセンサス (や将来的な方針) を決めとくとよさそうですね。

私は、どちらかといえばふなばさんと同じ立場です。
しかし上で書いたように実解をダイレクトに得る方法があっても良いと思います。

たとえば CMath.cbrt にオプション引数を追加して

CMath.cbrt(-8) #=> 主値
CMath.cbrt(-8, :real_only => true) #=> -2

のようにするとか。

もしくは逆に

CMath.cbrt(-8) #=> -2
CMath.cbrt(-8, :principal_value => true) #=> 主値

でも良いかもしれませんが、実解を得たいなら現在は Math.cbrt を使えるので
CMath.cbrt のデフォルトの返り値は主値のほうが良いと思います。

このような、オプション引数による挙動の変更が許されるのであれば、
いっそ CMath と Math を統合してしまうのも一つの方法だと思います。

少なくとも Complex#** の仕様として決まっているなら、できれば
rdoc に書いておいて欲しいです。

私は ** の結果は主値になっているほうが良いと考えます。
理由はいくつかあって、

(1) 1の原始3乗根の実数倍という数学的に特別である
(2) レシーバが Complex なんだから、一般に実解は期待できない
(3) Mathematica がそうなっている

などです。なお Mathematica の挙動は Wolfram|Alpha で検証できます。
http://www.wolframalpha.com/input/?i=(-27)^(1/3)

--
Kenta Murata
OpenPGP FP = 1D69 ADDE 081C 9CC2 2E54 98C1 CEFE 8AFB 6081 B062

本を書きました!!
『Ruby 逆引きレシピ』 http://www.amazon.co.jp/dp/4798119881/mrkn-22

E-mail: mrkn@mrkn.jp
twitter: http://twitter.com/mrkn/
blog: http://d.hatena.ne.jp/mrkn/

=end

Actions #7

Updated by matz (Yukihiro Matsumoto) almost 11 years ago

=begin
まつもと ゆきひろです

Topポスティングはあまり好きではないのですが、理解しないまま
にヘタに削るとまずそうなので、今回は残します。

要約すると

(1) Complex#** は主値を返すべきである(現状のまま)
(2) CMath.cbrt()は主値を返せた方がよい
(2-1) 主値を返す
(2-2) 主値を返すオプションを用意する
(2-3) オプションを用意した上、CMathはMathのaliasにする

これが誤読でないとして、私の意見を表明しておくと

(1) 賛成
(2) 条件つき賛成
以下を明確にする必要がある
* 変更範囲はどこまでか。cbrtだけ?
* 上記の3案(またはそれ以外)のいずれを採用するか
* だれが作業するか

です。

In message "Re: [ruby-dev:42006] Re: [Bug #3676] CMath.cbrt(-8)の結果が複素数にならない"
on Thu, 12 Aug 2010 01:09:13 +0900, Kenta Murata muraken@gmail.com writes:
|
|むらたです。
|
|On 2010/08/11, at 1:08, Yusuke ENDOH wrote:
|
|> 2010年8月10日23:22 Tadayoshi Funaba tadf@dotrb.org:
|>>> 現在のCMath.cbrt(-8)の結果は-2で、これは3乗すると-8になるとい
|>>> う意味で正しい値に思えるのですが。Cmath.cbrt(-8)が返すべき
|>>> 「正しい」値はなんだとお考えですか?
|>>
|>> 絶対的に正しい値はないと思いますが、おおよそ、Complex(-8) ** (1.0/3) で
|>> ある (1.0+1.73205080756888i) ではないでしょうか。
|>
|> 高校レベルの複素数の知識しかないので、Complex#(Rational) が
|> n 乗根のどれを返すかは不定だと思っていました。
|> 仰角が正で最小のものが「おおよそ正しい」んですかね。
|
|仰角が正で最小のものを主値 (principal value) と言い、
|多価関数を一価関数とするために使用されます。
|http://ja.wikipedia.org/wiki/%E4%B8%BB%E5%80%A4
|
|n乗根の主値は1の原始n乗根の実数倍になっています。
|http://ja.wikipedia.org/wiki/1%E3%81%AE%E5%86%AA%E6%A0%B9
|
|ご存知の通り、この主値さえあればすべての解を順番に生成できます:
|
| a = Complex(-8)
(1.0/3) # 主値
| w = a / a.abs # 1の原始3乗根
| a * w #=> (-0.9999999999999996+1.7320508075688776i)
| a * w*2 #=> (-2.0+7.771561172376096e-16i)
|
|しかし、負数の奇数乗根はかならず実解を持ちますから、
|主値ではなく実解をダイレクトに手に入れる方法があっても良いと思います。
|なぜなら、上の方法では浮動小数点演算による誤差を含んでしまうことと、
|ダイレクトに求められれば必要なかった計算を必要としているからです。
|
|(引用の順序を入れ替えます)
|
|> 2010年8月11日0:35 Tadayoshi Funaba tadf@dotrb.org:
|>> まつもとさんが言っているのはそういう意味じゃないと思いますが、定義域と
|>> 値域があって関数がある、Math は実数に対して定義がある。CMath は複素数に
|>> 対して定義される、というのは常識的な事で、議論の余地はないくらいに思い
|>> ます。CMath は制限の撤廃、あるいは定義域値域の拡大だと思います。
|>
|> 私はまつもとさんと同じ感覚でした (CMath は Math の挙動を拡張する
|> だけで変えはしない) 。[ruby-core:31234] の人も同じ感覚だと思います。
|> ちなみに後で登場するに違いないと思いますが、むらけんさんはふなば
|> さんと同じ感覚のようです。
|> この機会にコンセンサス (や将来的な方針) を決めとくとよさそうですね。
|
|
|私は、どちらかといえばふなばさんと同じ立場です。
|しかし上で書いたように実解をダイレクトに得る方法があっても良いと思います。
|
|たとえば CMath.cbrt にオプション引数を追加して
|
| CMath.cbrt(-8) #=> 主値
| CMath.cbrt(-8, :real_only => true) #=> -2
|
|のようにするとか。
|
|もしくは逆に
|
| CMath.cbrt(-8) #=> -2
| CMath.cbrt(-8, :principal_value => true) #=> 主値
|
|でも良いかもしれませんが、実解を得たいなら現在は Math.cbrt を使えるので
|CMath.cbrt のデフォルトの返り値は主値のほうが良いと思います。
|
|このような、オプション引数による挙動の変更が許されるのであれば、
|いっそ CMath と Math を統合してしまうのも一つの方法だと思います。
|
|
|> 少なくとも Complex#
* の仕様として決まっているなら、できれば
|> rdoc に書いておいて欲しいです。
|
|私は ** の結果は主値になっているほうが良いと考えます。
|理由はいくつかあって、
|
|(1) 1の原始3乗根の実数倍という数学的に特別である
|(2) レシーバが Complex なんだから、一般に実解は期待できない
|(3) Mathematica がそうなっている
|
|などです。なお Mathematica の挙動は Wolfram|Alpha で検証できます。
|http://www.wolframalpha.com/input/?i=(-27)^(1/3)
|
|
|--
|Kenta Murata
|OpenPGP FP = 1D69 ADDE 081C 9CC2 2E54 98C1 CEFE 8AFB 6081 B062
|
|本を書きました!!
|『Ruby 逆引きレシピ』 http://www.amazon.co.jp/dp/4798119881/mrkn-22
|
|E-mail: mrkn@mrkn.jp
|twitter: http://twitter.com/mrkn/
|blog: http://d.hatena.ne.jp/mrkn/

=end

Actions #8

Updated by shyouhei (Shyouhei Urabe) almost 11 years ago

  • Status changed from Open to Assigned
  • Assignee set to mrkn (Kenta Murata)

=begin

=end

Updated by ko1 (Koichi Sasada) about 10 years ago

この件はどうなってますでしょうか.

Updated by mrkn (Kenta Murata) about 10 years ago

まず CMath.cbrt 単独で、主値を返却するように修正します。

CMath の他の関数についてはバグとして、オプション引数の導入についてはフィーチャーとちえ、個別にチケット化します。

Updated by mrkn (Kenta Murata) about 10 years ago

  • Status changed from Assigned to Closed

CMath についてですが、現状で複素数に対応できていないのは以下の3関数です。

gamma
lgamma
erf
erfc

これらのうち、gamma は exp(lgamma) なので良いとして、他の3種類はアルゴリズムを手に入れるところからのスタートなので、1.9.4で対応することを目標に取り組みます。

それから、frexp, ldexp, hypot の3関数は CMath の下に定義する必要は無いと思いますので、別チケットで削除の提案をしたいと思います。

そういう訳ですので、このチケットは CMath.cbrt の挙動が修正できたので閉じます。

Actions

Also available in: Atom PDF