裏 RjpWiki

Julia ときどき R, Python によるコンピュータプログラム,コンピュータ・サイエンス,統計学

Julia で出生性比をプロットする

2021年11月27日 | ブログラミング

奥村先生の Python のページで,出生性比のグラフの描き方が紹介されていた
https://oku.edu.mie-u.ac.jp/~okumura/python/sexratio.html

Julia でもやってみた。

CSV ファイルをダウンロードして保存した段階で,エディタで開いて,utf-8 ファイルにした(そうしないと missingstring="…" が効かなかった)。

その他,1944, 1945, 1946 年のデータは全項目欠測値だけど,エディタで開いた時点で3行挿入したほうが簡単だった気がする(プログラム3行も消費した)。
データの先頭11行は,まあ,削らないでそのまま使った。

using DataFrames, CSV, Dates, Plots
df = CSV.read("/Users/foo/Downloads/mb010000.csv", DataFrame,
     skipto=12, # データ開始行
    
header=["年","出生数_総数","出生数_男","出生数_女","出生率","出生性比","合計特殊出生率"], # 列名
    
missingstring="…"); # 欠損値定義

df_missing = DataFrame(年=["1944", "1945", "1946"]); # 欠損値のデータフレーム
df2 = join(df, df_missing, on=:年, kind=:outer); # R での merge  相当
df3 = sort(df2, :年); # ソートしないといけない
df3.出生性比 = df3.出生数_男 ./ df3.出生数_女; # もとの出生性比は100倍して小数点以下一桁で丸めてある
df3.年d = DateTime.(parse.(Int, df3.年)); # 年を DateTime 型に変換する
pyplot(grid=false, color=:black, label="")
tmtick = range(DateTime("1900-01-01"), df3.年d[end], step=Year(10)); # 横軸を描くために面倒なことを
tmticks = Dates.format.(tmtick, "yyyy"); # 横軸を描くために面倒なことを
plot(df3.年d, df3.出生性比, linewidth=0.5, title="年次別出生性比",
    xticks=(tmtick, tmticks), tickdirection=:out); # 折れ線グラフ
scatter!(df3.年d, df3.出生性比, marker=(3, 0.6, :blue, stroke(0, :blue))) # データ点は重ね描き

コメント
  • X
  • Facebookでシェアする
  • はてなブックマークに追加する
  • LINEでシェアする

折れ線グラフと棒グラフ--どっちが見やすい?

2021年11月26日 | ブログラミング

折れ線グラフと棒グラフ--どっちが見やすい?

この場合なら,私は 棒グラフのほうを選ぶ

# https://covid19.mhlw.go.jp/public/opendata/newly_confirmed_cases_daily.csv
# 以下では,ダウンロードしたデータを参照

using DataFrames, CSV, Dates
df = CSV.read("/Users/foo/Downloads/newly_confirmed_cases_daily.csv", DataFrame);
filter!(row -> row.Prefecture == "ALL", df); # 全国データのみ抽出
df.date = DateTime.(replace.(df[:, 1], "/" => "-")); # 日付フォーマットに変換
using Plots
tmtick = range(DateTime("2020-01-01"), df.date[end], step=Month(3))
tmticks = Dates.format.(tmtick, "yy-mm");
bar(df.date, df[:, 3], xticks=(tmtick, tmticks), label="") # 棒グラフと
plot(df.date, df[:, 3], xticks=(tmtick, tmticks), color=:black, label="") # 折れ線グラフを比較

それにしても,tidy data は無駄が多い

コメント
  • X
  • Facebookでシェアする
  • はてなブックマークに追加する
  • LINEでシェアする

SymPy だけでは解けない積分(?)

2021年11月26日 | ブログラミング

SymPy だけでは解けない積分

完全版は https://r-de-r.github.io/jupyterlab/pi.html

2019 の日付のある誰かのツイートで「Wi-fi パスワードを数式にするのが流行っている」とかあって,そこで示されているのが

  だ。

SymPy でやれば簡単じゃないかと思って,やってみるとおやまあという結果だったので記事にしてみる。

  using SymPy
  @syms x
  expression = (x^3 * cos(x/2) + 1/2) * sqrt(4-x^2)
  expression |> println
  sqrt(4 - x^2)*(x^3*cos(x/2) + 0.5)

なにはともあれ,やってみる。

  a = integrate(expression, (x, -2, 2))

随分時間がかかって,出てきた答えが以下のようなもの。2 つの定積分の和という答えを出してもらっても困る。ちなみに,Integral という関数はない。integrate だ。

  a |> println
  1.0*Integral(1.0*x^3*sqrt(4 - x^2)*cos(x/2), (x, -2, 2)) + 1.0*Integral(0.5*sqrt(4 - x^2), (x, -2, 2))

前半部分をもう一度やってみる。

  b = integrate(x^3*sqrt(4 - x^2)*cos(x/2), (x, -2, 2));

なんの進展もない結果が帰ってくる。後で対処する。

  b |> println
  Integral(x^3*sqrt(-(x - 2)*(x + 2))*cos(x/2), (x, -2, 2))

後半部分をやってみる。

  c = integrate(0.5*sqrt(4 - x^2), (x, -2, 2))

こちらはすぐに,(1.0* というのは気に入らないが)きれいな答えが得られた。π だ。

  c |> println
  1.0*pi

さて,b の integrate(x^3 sqrt(-(x - 2) (x + 2)) * cos(x/2), (x, -2, 2)) だが。

  b0 = integrate(x^3 * sqrt(-(x - 2) * (x + 2)) * cos(x/2), (x, 0, 2));

結局何も進展しない。

  b0 |> println
  Integral(x^3*sqrt(2 - x)*sqrt(x + 2)*cos(x/2), (x, 0, 2))

ここで,式をちゃんと見ると,う?奇関数(懐かしい用語)。

  expression2 = x^3 * sqrt(4 - x^2) * cos(x/2)
  expression2 |> println
  x^3*sqrt(4 - x^2)*cos(x/2)
  expression2(x => -x) |> println
  -x^3*sqrt(4 - x^2)*cos(x/2)

奇関数でした。なので,a の第 1 項は 0 なので,a の答えは a の第 2 項の π ということになる。

やれやれ。

#####

R では,数値微分しかできないのでやってみる。

> y = function(x) (x^3 * cos(x/2) + 1/2) * sqrt(4-x^2)
> options(digits=16)
> integrate(y, -2, 2)
3.141592653589796 with absolute error < 2e-09

#####

WolframAlpha でやってみた。数値積分になるが。

コメント
  • X
  • Facebookでシェアする
  • はてなブックマークに追加する
  • LINEでシェアする

2 直線の交点 SymPy/Julia

2021年11月24日 | ブログラミング

あいも変わらず,

2直線の交点座標を求めるプログラム
https://blog.goo.ne.jp/r-de-r/e/e81316c26c521b31e9d47526f9bd5861

がよく参照されているが,わけわからん。

そこで,Julia の SymPy で書いたものも掲載しておく。
これが「わけわからん」という人もいるだろうが。

using SymPy

@syms a b c d x

function func(a, b, c, d)
    if a != c
        eq1 = a*x + b
        eq2 = c*x + d
        x2 = solve(Eq(eq1, eq2), x)[1]
        y2 = simplify(eq1(x => x2))
        println("x = $x2,  y = $y2")
    elseif b == d
        println("2直線はまったく同じなので,交点は無数にあります")
    else
        println("2直線は平行なので,交点はありません")
    end
end

記号変数で呼び出すと,数式で答えが返ってくる。

func(a, b, c, d)
x = (-b + d)/(a - c),  y = (a*d - b*c)/(a - c)

数値で呼び出すと,然るべき答えが返ってくる。

func(2, 3, 1, -4)
x = -7,  y = -11


func(1, 2, 1, 2)
2直線はまったく同じなので,交点は無数にあります


func(1, 2, 1, 5)
2直線は平行なので,交点はありません

コメント
  • X
  • Facebookでシェアする
  • はてなブックマークに追加する
  • LINEでシェアする

SymPy/Julia(2)

2021年11月24日 | ブログラミング

SymPy/Julia

https://www.math.csi.cuny.edu/~verzani/tmp/julia/symbolic.html

5. 数式の図示

数式は,Julia へ戻ることなく,直接プロットすることができる。

f(x) = sin(x^4) - cos(x^4) を [0,1] の範囲で描くには以下のようにする。

  using Plots
  gr(label="")
  f(x) = sin(x^4) - cos(x^4)
  plot(f(x), 0, 1)

plot の第一引数は関数でなくて,数式でよい。

  g = sin(x^4) - cos(x^4)
  plot(g, 0, 1)

f(x) も g も,どちらも Sym オブジェクトである。

  println("f(x): $(f(x)),  typeof(f(x)): $(typeof(f(x)))")
  println("g:    $g,  typeof(g):    $(typeof(g))")
  f(x): sin(x^4) - cos(x^4),  typeof(f(x)): Sym
  g:    sin(x^4) - cos(x^4),  typeof(g):    Sym

複数の数式は plot(), plot!() で図示する(一回の plot() 呼び出しだけで図示する方法はできなくなった)。

  f(x) = sqrt(x)
  fp = diff(f(x)) # fp は関数ではなく数式
  c = 2
  m = fp(x => c)  # at c=2
  println("c = $c,  m = $m")
  c = 2,  m = sqrt(2)/4
  plot(f(x), 1, 3)
  plot!(f(c) + m * (x - c))

5.1. 課題

5.1.1. 問題 1.

x^4 - 3x^3 + 3x^2 - 3x +2 を [-1, 3] の範囲で描画せよ。実数解はいくつあるか。

  g = x^4 - 3x^3 + 3x^2 - 3x + 2
  plot(g, -1, 3)
  savefig("fig4.png")
  solve(g) |> println
  real_roots(g) |> println
  real_roots(g) |> length |> println
  Sym[1, 2, -I, I]
  Sym[1, 2]
  2

6. 極限

limit() 関数は,数式の極限を求める。

limit() 関数の第 1 引数に数式を指定する。第 2 引数にどの変数について計算するか,第 3 引数はその変数をどこへ近づけていくかを指定する。

  limit(sin(x)/x, x, 0) |> println
  1
  limit((1-cos(x))/x^2, x, 0) |> println
  1/2

無限大は小文字の o を 2 個続けて表す。

  limit(sin(x)/x, x, oo) |> println
  0

関数の導関数は limit() 関数を使って数式計算できる(数値計算もできるが)。

  @syms h
  f(x) = x^10
  limit((f(x + h) - f(x))/h, h, 0) |> println
  10*x^9

diff() での結果とあっている。

  diff(f(x)) |> println
  10*x^9

同じ結果を与えるがもっと複雑な計算式を使う方法もある。

  central_difference(f, x, h) = ( f(x + h) - f(x - h) ) / (2h)
  a = limit(central_difference(f, x, h), h, 0) |> println
  10*x^9

更にもっと複雑な計算式もある。

  central_4th_difference(f, x, h) = (-f(x + 2h) + 8f(x + h) - 8f(x - h) + f(x - 2h))/(12h)
  limit(central_4th_difference(f, x, h), h, 0) |> println
  10*x^9

limit() はチェイン規則にしたがう。

  func(x) = sin(x)
  limit((f(func(x + h)) - f(func(x))) / h, h, 0) |> println # 10*sin(1)^9*cos(1)
  10*sin(x)^9*cos(x)

6.1. 課題

6.1.1. 問題 1.

(3 - √(x + 5)) / (x - 4) の x を 4 に近づけていくときの極限を求めよ。

  f(x) = (3 - sqrt(x + 5)) / (x - 4)
  a = limit(f(x), x, 4)
  println("a = $a, float(a) = $(float(a))")
  a = -1/6, float(a) = -0.16666666666666666

6.1.2. 問題 2.

(x^(1/4) - 1) / (x^(1/3) - 1) の x を 1 に近づけていくときの極限を求めよ。

  f(x) = (x^(1/4) - 1) / (x^(1/3) - 1)
  limit(f(x), x, 1) |> float |> println
  0.75

6.1.3. 問題 3.

記号変数 h を定義し,f(x) = sin(x) とする。

  @syms h
  f(x) = sin(x);

(f(x+h) - 2f(x) + f(x-h)) / h^2 の h を 0 に近づけていくときの極限は,何になるか。

  gg = (f(x+h) - 2f(x) + f(x-h)) / h^2
  limit(gg, h, 0) |> println
  (diff(f(x), x), diff(f(x), x, 2), diff(f(x), x, 3)) |> println
  -sin(x)
  (cos(x), -sin(x), -cos(x))

この式は limit() 関数で 2 次の導関数を求めるための式である。

  f(x) = 3x^3 + 2x^2 +3x + 4
  limit(gg, h, 0) |> println
  expand(diff(f(x), x, 2)) |> println
  -sin(x)
  18*x + 4

6.1.4. 問題 4.

記号変数 a を定義し,

(cos(a * x) - 1) / (cos(x) - 1) において x を 0 に近づけていくときの極限は,何になるか。

  @syms a
  limit((cos(a * x) - 1) / (cos(x) - 1), x, 0) |> println
  a^2

6.1.5. 問題 5.

式 f(x) = 1/(x^(log(log(log(1/x)))-1)) の x を 0 に近づけていくときの極限を数値的に求めることができる。

  f(x) = 1/(x^(log(log(log(1/x)))-1))
  xs = 0.1 .^ (2:4:40)
  fxs = [f(x) for x in xs]
  [xs fxs]
  10×2 Matrix{Float64}:
   0.01        0.0702822
   1.0e-6      0.619862
   1.0e-10    27.0054
   1.0e-14  2695.41
   1.0e-18     4.65934e5
   1.0e-22     1.20915e8
   1.0e-26     4.32215e10
   1.0e-30     2.00968e13
   1.0e-34     1.16712e16
   1.0e-38     8.21329e18

数式的に極限を求めると,結果は ∞ になり,数値的な場合に予測されたものに一致する(途中に float() を挟まない場合は,Inf を表す "oo" が表示される)。

  limit(f(x), x, 0) |> float |> println
  Inf

6.1.6. 問題 6.

式 f(x) = log(log(xexp(xexp(x)) + 1)) - exp(exp(log(log(x)) + 1/x)) で,x を ∞ に近づけていくときの極限を求めよ。

極限は 0 であるが,数値的に確認することはできない。

例えば f(10) をもとめてみよ。

  f(x) = log(log(x*exp(x*exp(x)) + 1)) - exp(exp(log(log(x)) + 1/x))
  f(x) |> println
  -exp(exp(1/x)*log(x)) + log(log(x*exp(x*exp(x)) + 1))
  f(10) |> println
  Inf

確かに x = 10 の時点で Inf になってしまっている。

しかし,x として長精度実数を使うと実は結果は Inf ではないことがわかる。

  f(big(10))
  -0.4374481647059431342589507553638095674342208743587679099109922069614458809810611

喜びもつかの間,x が大きくなるとそのうち(いくら使用する精度を高くしても) Inf になってしまう(要するに想像を絶する大きな数値になるのだ)。

  f(big(39))
  Inf

limit() 関数を使えば,正しい答え 0 が導かれる。

  limit(f(x), x, oo) |> println
  0

7. 導関数

前述したように,limit() を使って導関数を求めることもできるが,SymPy には導関数を求めるための diff() 関数がある。

以下で,f(x) は 関数ではなく,数式 Sym である。

  f(x) = exp(exp(x))
  f (generic function with 1 method)

数式中に変数が 2 個以上ある場合は,どの変数について導関数を求めるかを指定する(デフォルトは x)。

  diff(f(x)) |> println
  diff(f(x), x)  |> println
  exp(x)*exp(exp(x))
  exp(x)*exp(exp(x))

第三引数で何階導関数かを指定する(デフォルトでは 1)

  diff(f(x), x, 2) |> println
  (exp(x) + 1)*exp(x)*exp(exp(x))

x^x と,その導関数のグラフを描いてみる。

  f(x) = x^x
  plot(f(x), 1/10, 2, label="f(x)")
  plot!(diff(f(x)), label="f'(x)")

f(x) と c = 1 での切線のグラフを描く。

  f(x) = x^x; c = 1
  m = diff(f(x))
  plot(f(x), .5, 1.5)
  plot!(f(c) + m * (x - c))

7.1. 番外

diff() と solve() を組み合わせて,閉区間内の極値 f'(x) = 0 となる x を求めることができる。

3 辺の長さの和 L が一定の長方形の面積 A を最大にする問題を考えよう。

L = 2x + y
A = x * y

すなわち,A(x) = x * (L - 2x)

L を記号変数として,A'(x) = 0 となる x を求め,A(x) を求める。

  @syms L, x, y
  A = x*y
  A = A(y => L - 2x)
  out = solve(diff(A, x), x)[1]
  out |> println # x
  L/4
  (L - 2x)(x => out) |> println # y
  L/2

7.2. 課題

7.2.1. 問題 1.

tan(x)^(-1) の導関数を求めよ。

落とし穴(?)。tan(x)^(-1) は 1/tan(x) ではない。tan(x) の逆関数なので,atan(x) だ。

  diff(atan(x)) |> println
  1/(x^2 + 1)

微分は積分の逆なのだから,結果として提示されている 3 つの選択肢を integrate() してみればよい。atan(x) が解になる。

  integrate(1/(x^2 + 1)) |> println
  atan(x)

7.2.2. 問題 2.

f(x) = exp(-cos(x)) のとき,f'(3) を求めよ。

  diff(exp(-cos(x)))(x => 3) |> println 
  diff(exp(-cos(x)))(x => 3) |> float   |> println
  exp(-cos(3))*sin(3)
  0.3797841807457766324319619290395706689950768584935908050136628093384225996266999

7.2.3. 問題 3.

f(x) = 1/x^2 + x^3 の極値を求めよ。nsolve(diff(f(x)), 1) も試してみよ。

注: Julia では,? nsolve としても No documentation found. となるばかりだ。
Python の sympy で help(nsolve) でやっと,
Solve a nonlinear equation system numerically.
nsolve(f, [args,] x0, modules=['mpmath'])
'f' is a vector function of symbolic expressions representing the system.
args are the variables. If there is only one variable, this argument can
be omitted. 'x0' is a starting vector close to a solution.

  @syms x::real
  f(x) = 1/x^2 + x^3
  solve(diff(f(x)))              |> println
  solve(diff(f(x)))     |> float |> println
  nsolve(diff(f(x)), 1) |> float |> println # 1 は初期値
  Sym[2^(1/5)*3^(4/5)/3]
  [0.9221079114817278]
  0.9221079114817277656701705309291930146705850859540336029625316749125970624956978

7.2.4. 問題 4.

f(x) = tan(x) として, ニュートン法により 切線の方程式が f(c) + f'(c)(x-c) = 0 になるときの x を求めよ。

  f(x) = tan(x)
  @syms c
  solve(f(c) + diff(f(c)) * (x - c), x) |> println
  Sym[c - sin(2*c)/2]

c = pi/4 とすると何が得られるか?

  solve(f(c) + diff(f(c)) * (x - c), x)[1](c => PI/4) |> float |> println
  0.2853981633974483096156608458198757210492923498437764552437361480769541015715538

8. 積分(不定積分)

SymPy でも数式積分を行うことができるが,Mathematica に比べると劣る。

数値積分を行うのは integrate() である。

  @syms x, a
  f(x) = cos(x) - sin(x)
  integrate(f(x), x) |> println
  sin(x) + cos(x)

定数を含むこともできる。

  integrate(1/cos(x + a), x) |> println
  -log(tan(a/2 + x/2) - 1) + log(tan(a/2 + x/2) + 1)

その他の例

  integrate(x^3 * (1-x)^4, x) |> println
  x^8/8 - 4*x^7/7 + x^6 - 4*x^5/5 + x^4/4
  integrate(x/(x^2+1), x) |> println
  log(x^2 + 1)/2

9. 定積分

積分範囲を指定することで,定積分を計算することができる。

  integrate(x^2, (x, 0, 1)) |> println
  1/3

定積分の結果から,(分布関数の)中央値を計算することができる。

  using SymPy
  @vars x b real=true positive=true # 「b は正の実数」という条件を付けないと虚数解を含めて 4 個の解が求まる
  f(x) = 4x^3
  eq = integrate(f(x), (x,  0, b)) - 1/2 * integrate(f(x), (x, 0, 1));
  println(eq)
  solve(eq, b) |> float |> println
  b^4 - 0.5
  [0.8408964152537145]

9.1. 課題   

9.1.1. 問題 1.

f(x) = 2 / (x * log(x)) の不定積分を求めよ。

  integrate(2/(x * log(x))) |> println
  2*log(log(x))

9.1.2. 問題 2.

f(x) = (2x + 5)(x^2 + 5x)^7 を [0, 1] で定積分せよ。

  integrate((2x + 5) * (x^2 + 5x)^7, (x, 0, 1)) |> float |> println
  209952.0

9.1.3. 問題 3.

f(x) = sqrt(4 - sqrt(x)) を [0,9] で定積分せよ。

  integrate(sqrt(4 - sqrt(x)), (x, 0, 9)).evalf() |> println
  12.5333333333333

integrate(sqrt(4 - sqrt(x)), (x, 0, 9)) |> float |> println ではエラーになる

コメント
  • X
  • Facebookでシェアする
  • はてなブックマークに追加する
  • LINEでシェアする

SymPy/Julia(1)

2021年11月24日 | ブログラミング

SymPy/Julia

https://www.math.csi.cuny.edu/~verzani/tmp/julia/symbolic.html

1. Julia で数式処理

コンピュータによる計算処理で,3 通りくらいのアプローチがある。

  • Mathenatica,Maple, Sage に代表される数式処理
  • MATLAB, R による数値処理
  • Python, Haskel による一般化された汎用処理

Julia は 数値計算処理のために,MATLAB や R の代替として存在する。Julia は PyCall を使うことによって,Python およびその SymPy パッケージへの連携を提供した。

Julia で SymPy パッケージを使えるようにする方法については,ここでは述べない。単に,Python を動くようにすることと,SymPy パッケージを使えるようにした後,Pkg.add("SymPy") するだけである。

2. 概略

数式処理をするために必用なのは,SymPy を使うことを宣言する(using SymPy)と,そこで使う変数を宣言すること(@syms x など)である。

  using SymPy
  @syms x
  (x,)

これによって宣言(定義)される x は,Julia における変数名ではなく,以下に引き続く処理の記述で使用される記号変数(Sym オブジェクト)である。

x の型(typeof) は Sym である。

  x |> typeof |> println
  Sym

Sym オブジェクト(以下では単に変数と呼ぶ)は,何通りかの方法で宣言(定義)することができるが,@syms マクロ(または @vars マクロ)を使うのがもっと便利であろう。h, y の 2 変数を定義するには以下のようにする。

変数名は空白あるいはカンマで区切って複数記述できる。

  @syms h y
  (h, y)

なお,実際の使用例を後で示すが,特定の条件をもつ変数として宣言することもできる。

  @syms a::positive b::real c::integer
  (a, b, c)

複数の条件を持つ場合は @vars を使い,条件をタプルで指定する。

  @vars x y z real=true w nonnegative=true v (positive=true, real=true)
  (x, y, z, w, v)

このようにして定義した変数を使用して,任意の数式を記述することができる。

たとえば x^2 -2x +2 のような数式はそのまま指定できる。

この記事を通じて,数式のあとに "|> typeof" あるいは "|> println" としているのは,左側の数式に対して typeof(数式),さらに printiln(処理された数式) を実行し,最終的な結果を表示する(REPL の場合)ことを示しているだけである。

ユーザが実際に SymPy を使用する環境によってはそれらは不要,あるいはないほうがよい,更にはあっては困るということもあるであろうから,適宜省略・削除するようお願いしたい。(最後のいくつかの |> を省略したらどうなるか,事前に把握しておいて欲しい)。

以下では,定義した数式の型が何であるかを表示している。なれてくれば,このような確認は不用であろう。

  x^2 - 2x + 2 |> typeof |> println
  Sym

以下では,関数名 f で普通の Julia 関数を定義している。

  f(x) = exp(-x^2/2)
  f (generic function with 1 method)

普通通り,x = 3 のときの関数値は f(3) で得られる。

  f(3) |> println
  0.011108996538242306

しかし,f(x) は Sym 型を持つ数式である(typeof(f(x)) = Sym)。

  f(x) |> println
  exp(-x^2/2)
  typeof(f(x)) |> println
  Sym

f(x) が Sym オブジェクトであるということは,そのオブジェクト中の変数を特定の値として評価できるということである。

以下は,f(x) 中の x を 1 にするとどのような数式になるかを示すものである。

  subs(f(x), x, 1) |> println
  exp(-1/2)
  f(x)(x => 1) |> println
  exp(-1/2)

f(x) が表す式にある x を 1 で置き換えると exp(-1/2) になるということである。

subs() 関数によって式の一部が置き換えられた(代入された)ものも,前と同じ Sym オブジェクトなので,その式に別のものを代入することもできる。

以下の示すのは,f(x) において, x = 1 を代入 (subs) した結果の数値 (float) を表示する (println) という一連の処理を示している。

  f(x) |> subs(x => 1) |> float |> println
  0.6065306597126334

同じ処理をするのには何通りかの方法があることがあるが,今の場合は以下のほうがより簡潔かもしれなお。

  f(x)(x => 1) |> float |> println
  0.6065306597126334

多くの場合,最終的な(人間が見てわかる)表示形式にするのは一番最後になるであろう。

3. 方程式

SymPy は代数演算として,簡約化,因数分解,方程式の解など多くのタスクをこなすことができる。

しかし,数式処理をするために必用とされるテクニックはそんなに多くはない。

例えば,以下のように記述すれば,数式は自動的に簡約化される(最後の 「|> println」 は,処理された結果を画面表示するとそのようになるということであるが,環境によってはそれがないほうがわかりやすい数式表現がされることもあろう)。

  (x+1) + (x+2) + (x+3) |> println
  3*x + 6

書かれた数式をもう少しわかりやすい(人と場合によるのだが)形式で表現してほしいこともある。

例えば,以下の式は人によっては別の形式で表現してほしいかもしれない。現役数学者(中高生?)なら,我慢できないかもしれない。

  sin(x)^2 + cos(x)^2 |> println
  sin(x)^2 + cos(x)^2

このよう場合,simplyfi() 関数により有名すぎる公式「x が何であろうと(有効範囲はあるが),sin(x) と cos(x) の二乗の和和は 1」簡約化された解を得られる。

  simplify(sin(x)^2 + cos(x)^2) |> println
  1
  sin(x)^2 + cos(x)^2 |> simplify |> println
  1

factor() 関数により,数式の因数分解もできる。

  factor(x^2 + 2x + 1) |> println
  (x + 1)^2

因数分解された形式で表示された数式は,expand() 関数によって展開された数式にすることができる。

  expand( (x+1)*(x+2)*(x+3) ) |> println
  x^3 + 6*x^2 + 11*x + 6

4. 方程式を解く

多項式の実数解を数式解として求めるには real_roots() 関数を使う。

  real_roots(x^2 + x - 1) |> println
  Sym[-sqrt(5)/2 - 1/2, -1/2 + sqrt(5)/2]

もう少し一般的にいえば,多項式に限らず方程式の数式解を得るには solve() 関数を使う。

  solve(x^2 + x -1, x) |> println
  Sym[-1/2 + sqrt(5)/2]

得られるのは数式解なので,数値解を得るには float() 関数を適用すればよい。

  solve(x^2 + x -1, x) |> float |> println
  [0.6180339887498949]

三角関数方程式は無数の解を持つが,SymPy の sin では -90度 〜 90度の範囲の解を求める。

  solve(sin(x)^2 - (1/2)^2) * (180 /pi) |> println
  Sym[30.0000000000000, 150.000000000000, 210.000000000000]

式中に変数が 2 つ以上ある場合に,どの変数についての解を求めるかは第 2 引数で指定する。

  fx = x^2 + y^2 - 1
  out = solve(fx, y)
  println(out)
  Sym[-sqrt(1 - x^2), sqrt(1 - x^2)]

方程式は二次式なので,2 つの数式解を含むベクトルが返される。x = 1/2 のときの値は,各要素について x = 1/2 を代入して,float にする。

  out[1](x => 1/2) |> float |> println
  out[2](x => 1/2) |> float |> println
  -0.8660254037844386
  0.8660254037844386

以下のようにすればベクトルとしてまとめて表示できる。

  println([o(x => 1/2) |> float for o in out])
  [-0.8660254037844386, 0.8660254037844386]
  println(map(arg -> arg(x => 1/2) |> float, out))
  [-0.8660254037844386, 0.8660254037844386]

連立方程式も solve で解くことができる。 x + y - 2a = 0 x - y + b = 0 を x, y について解く。 連立方程式と,どの変数について解くかの指定をベクトルで指定する。

  solve([x + y - 2a, x - y + b], [x, y]) |> println
  Dict{Any, Any}(y => a + b/2, x => a - b/2)

別法として,Julia で数値解を得るときと同じように記述することもできる。

x + y = 2a x - y = -b

係数行列 [1 1; 1 -1] と定数ベクトル [2a, -b] と \ 演算子を使う。

  Sym[1 1; 1 -1] \ Sym[2a, -b] |> println
  Sym[a - b/2, a + b/2]

4.1. 課題

4.1.1. 問題 1.

(x-1)(x-2)(x-3)(x-4)(x-5) において,x^3 の係数はいくつか。

  eq = expand((x-1)*(x-2)*(x-3)*(x-4)*(x-5))
  eq |> println
  x^5 - 15*x^4 + 85*x^3 - 225*x^2 + 274*x - 120

目で見るだけで解は分かるが,係数を取り出す object.coeff(...) メソッドを使えばよい。

  eq.coeff(x^3) |> println
  85

4.1.2. 問題 2. Horner 法

((1x + 2)x + 3)x + 4 を展開せよ。

  ((2x + 3)x + 4)x + 5 |> expand |> println
  2*x^3 + 3*x^2 + 4*x + 5

Horner 法は,多項式 2 x^3 + 3 x^2 + 4 x + 5 を記述する方法の一つで,((2 x + 3)x + 4)x + 5 のように ( ) を使って記述する。

((2 x + 3) x + 4) x + 5 では,加算が 3 回,乗算が 3 回であるのに対し
2
x^3 + 3 x^2 + 4 x + 5 では,加算は同じく 3 回であるが,乗算は(^ も加え) 6 回(3, 2, 1 回)である。 もっとも,最近のコンピュータの性能から見ればたいした違いではないだろうし,Julia のようなコンパイラ言語では数式の解釈時に Hoener 法を使って最適化するようなこともあるのかもしれない。

4.1.3. 問題 3.

x^3 - 6x^2 + 11x - 6 = 0 を解き,最も大きい解を求めよ。

以下で,N() 関数は,適切な数値表現への変換関数である。float() 関数は引数が整数でも実数化してしまう(この場合には,実害はないのだが)。

  solve(x^3 - 6x^2 + 11x - 6) |> N |> println
  [1, 2, 3]

目で見ても分かるが,最大値を求める関数 maximum() を使えばよい。

  solve(x^3 - 6x^2 + 11x - 6) |> maximum |> println
  3

4.1.4. 問題 4.

直角三角形において,直角を挟む二辺が 3cm と 4cm の場合に,斜辺の長さ x を求めよ。

この場合,x は非負の実数なので,@syms で変数を定義するときに「変数::変数の特性」の書き方を使い,nonnnegative であるという条件を付けておくと不要な解が含まれなくなる。

  @syms x::nonnegative
  solve(x^2 - (3^2 + 4^2)) |> N |> println
  [5]

なお,Julia ではベクトルは要素が 1 個の場合でもベクトルなので,スカラーとして取り出すときは 「ベクトル[1]」 のように添え字を指定しないと「[要素]」となってしまうので注意が必要である。

要素数が 1 個の場合と,2 個以上の場合の両方に対応するには以下のようにする。

  a = solve(x^2 - (3^2 + 4^2))
  length(a) == 1 ? println(N(a[1])) : println(a)
  5
  @syms y
  b = solve((y + 3) * (y - 5))
  length(b) == 1 ? println(N(b[1])) : println(N(b))
  [-3, 5]

4.1.5. 問題 5.

x^4 - 10x^3 + 32x^2 - 38x + 15 = 0 を解き,最も大きい解を求めよ。

問題 5 と同じである。

  @syms x
  solve(x^4 - 10x^3 + 32x^2 - 38x + 15) |> N |> println
  [1, 3, 5]
  solve(x^4 - 10x^3 + 32x^2 - 38x + 15) |> maximum |> println
  5

いまの場合,4 次式なので解は 4 個あるはずだが,solve() では 3 個しか表示されなかった。

solve では重解は複数として数えないのである。factor を使えば詳細が分かる。

  factor(x^4 - 10x^3 + 32x^2 - 38x + 15) |> println
  (x - 5)*(x - 3)*(x - 1)^2

この場合は,x = 1 が二重解であることが分かる。

このような場合には,roots() 関数を使えば重解も複数として数える。解は辞書形式で返される。

  roots(x^4 - 10x^3 + 32x^2 - 38x + 15) |> println
  Dict{Any, Any}(3 => 1, 1 => 2, 5 => 1)

この場合は,x=3, x=5 は 1つずつ,x=1 は 2 個であることが分かる。

実数解だけを求める必要がある場合は,real_roots() 関数を使うことができる。

以下の p は 8 次式なので,実数解,虚数解を合わせて 8 個ある。

  p = (x - 3)^2 * (x - 2) * (x-1) * x * (x + 1) * (x^2 + x + 1)
  expand(p) |> println
  x^8 - 7*x^7 + 13*x^6 + 2*x^5 - 11*x^4 - 13*x^3 - 3*x^2 + 18*x

real_roots() によれば,そのうち 6 個は実数解であることがわかる。

  real_roots(p) |> N |> println
  [-1, 0, 1, 2, 3, 3]

p のすべての解は roots() 関数でわかる。

虚数解は共役複素数 -1/2 ± sqrt(3)*I/2 (2 個)である。

  roots(p) |> println
  Dict{Any, Any}(-1 => 1, 3 => 2, 1 => 1, -1/2 - sqrt(3)*I/2 => 1, -1/2 + sqrt(3)*I/2 => 1, 0 => 1, 2 => 1)

4.1.6. 問題 6.

e^x = x^4 を解け。

Julia で,ℯ は REPL で \euler[tab] ,⩵ は \Equal[tab] として入力する。(ここからコピーペーストしても良い)

  solve(ℯ^x ⩵ x^4) |> println
  Sym[-4*LambertW(-1/4), -4*LambertW(1/4), -4*LambertW(-1/4, -1)]

⩵ が入力しづらければ Eq() 関数を使って以下のように書くこともできる。

  solve(Eq(ℯ^x, x^4)) |> println
  Sym[-4*LambertW(-1/4), -4*LambertW(1/4), -4*LambertW(-1/4, -1)]

LambertW() Lambert の W 関数というものだそうだ。float() を通せばその値がわかる。

  solve(Eq(ℯ^x, x^4)) |> float |> println
  [1.4296118247255556, -0.8155534188089607, 8.6131694564414]

N() では BigFloat として多くの桁数が表示される

  solve(Eq(ℯ^x, x^4)) |> N |> println
  BigFloat[1.42961182472555561227524441622361901326636222030404817451048179435868561038458, -0.8155534188089606577727273253085594805974099088406385398936250403272774562683745, 8.613169456441398596676396600371925502144825941277910783538046030885450338602536]

方程式の解として正しいか,evalf() を使って検算してみる。

3 番目の解では,左辺と右辺の数値は表示されている範囲では等しいが,全体としては誤差の範囲で一致しないということであろう。

  for res in solve(Eq(ℯ^x, x^4))
      left, right = ℯ^res.evalf(), (res^4).evalf()
      println("left = $left  right = $right  right == left = $(right == left)")
  end
  left = 4.17707743900016  right = 4.17707743900016  right == left = true
  left = 0.442394430203601  right = 0.442394430203601  right == left = true
  left = 5503.66468907673  right = 5503.66468907672  right == left = false

Lambert の W 関数は Julia の LambertW パッケージに用意されている。

  # import Pkg; Pkg.add("LambertW")
  using LambertW
  solve(Eq(ℯ^x, x^4)) |> println
  Sym[-4*LambertW(-1/4), -4*LambertW(1/4), -4*LambertW(-1/4, -1)]
  typeof(sympy.LambertW)
  PyObject

PyCall.PyObject の LambertW() 関数を LambertW パッケージの lambertw() 関数で置き換える。

  res = [-4*lambertw(-1/4), -4*lambertw(1/4), -4*lambertw(-1/4, -1)]
  res |> float |> println
  [1.4296118247255556, -0.8155534188089607, 8.6131694564414]

検算してみる。

  res .^ 4 |> float |> println
  [4.177077439000158, 0.44239443020360086, 5503.664689076726]
  ℯ .^ res |> float |> println
  [4.177077439000158, 0.44239443020360075, 5503.664689076728]
コメント
  • X
  • Facebookでシェアする
  • はてなブックマークに追加する
  • LINEでシェアする

続・ベクトルの内積関連の問題を Julia の SymPy で解く

2021年11月21日 | ブログラミング

ベクトルの内積関連の問題を Julia の SymPy で解く
https://blog.goo.ne.jp/r-de-r/e/cd7d0c0701c2541aa17d5943fe6e482b

の問題 2 の (2) で,「a = [1, 1], b = [1-√3, 1+√3]b の定義で直接 sqrt(3) を使うと,数値解を求めようとするので,それを避けるために変数 x を用いて sqrt(x) とする。」などと書いてしまったが,誤ったことを書いてしまった。

ちゃんとやればちゃんとできる。SymPy 公式サイトの文書をよく読んだら(というか,最初の方に)解決法は書いてあった。

sqrt(3) では,Base の sqrt が呼ばれてしまうので,数値解になってゆく。

ついでに,関数を内包していく書き方ではなく,パイプ |> を使ってみる。

  using SymPy
  u = sqrt(3)
  "u = $u, typeof(u) = $(typeof(u))" |> println
  u = 1.7320508075688772, typeof(u) = Float64

ここは,sympy.sqrt(3) として,Sym オブジェクトの √3 にしなければならない。

  v = sympy.sqrt(3)
  "v = $v, typeof(v) = $(typeof(v))" |> println
  v = sqrt(3), typeof(v) = Sym

ということで,正解は以下のとおりである。

  a = Sym[1, 1]
  b = Sym[1-sympy.sqrt(3), 1+sympy.sqrt(3)]
  "a = $a\nb = $b" |> println
  a = Sym[1, 1]
  b = Sym[1 - sqrt(3), 1 + sqrt(3)]
  acos((a'*b)./(sqrt(a'*a)*sqrt(b'*b))) |> println
  acos(sqrt(2)/sqrt((1 - sqrt(3))^2 + (1 + sqrt(3))^2))

もう少し簡約化するために simplify を使う。

  acos((a'*b)./(sqrt(a'*a)*sqrt(b'*b))) |> simplify |> println
  pi/3

閉じカッコに気をつけなくてもよいので,統一感も必用だが,使えるときにはパイプもよいかな。

なお,b の定義のとき,

b = Sym[1-sqrt(Sym(3)), 1+sqrt(Sym(3))]

でもよい。

コメント
  • X
  • Facebookでシェアする
  • はてなブックマークに追加する
  • LINEでシェアする

微分関連の問題を Julia の SymPy で解く

2021年11月21日 | ブログラミング

高校数学の「微分」関連の問題をPythonで解く

https://qiita.com/code0327/items/97fcacc42ab44402eb35

Julia の SymPy と Plotでやってみる。

  using SymPy
  @syms x
  diff((x^2 - 1) * (2x^2 + x - 3)) |> string
  "2*x*(2*x^2 + x - 3) + (4*x + 1)*(x^2 - 1)"

結果を因数分解した場合

  factor(diff((x^2 - 1) * (2x^2 + x - 3))) |> string
  "(x - 1)*(8*x^2 + 11*x + 1)"

結果を展開した場合

  expand(diff((x^2 - 1) * (2x^2 + x - 3))) |> string
  "8*x^3 + 3*x^2 - 10*x - 1"

  using SymPy
  @syms x
  diff((x + 1)^2 * (2x - 3)^4) |>string
  "8*(x + 1)^2*(2*x - 3)^3 + (2*x - 3)^4*(2*x + 2)"

結果を因数分解した場合

  factor(diff((x + 1)^2 * (2x - 3)^4)) |>string
  "2*(x + 1)*(2*x - 3)^3*(6*x + 1)"

結果を式を展開した場合

  expand(diff((x + 1)^2 * (2x - 3)^4)) |>string
  "96*x^5 - 320*x^4 + 160*x^3 + 360*x^2 - 270*x - 54"
  
コメント
  • X
  • Facebookでシェアする
  • はてなブックマークに追加する
  • LINEでシェアする

極限値関連の問題を Julia の SymPy で解く

2021年11月21日 | ブログラミング

高校数学の「極限値」関連の問題をPythonで解く

https://qiita.com/code0327/items/1fd0df574c8c8357af5a

Julia の SymPy と Plotでやってみる。

  using SymPy
  @syms x
  limit((5x^2 - x) / (4 -2x^2), x, -oo) |> string
  "-5/2"

  using SymPy
  @syms x
  limit(sqrt(x^2 + 1) - x, x, oo) |>string
  "0"
コメント
  • X
  • Facebookでシェアする
  • はてなブックマークに追加する
  • LINEでシェアする

2次関数関連の問題を Julia の SymPy で解く

2021年11月21日 | ブログラミング

高校数学の「2次関数」関連の問題をPythonで解く

https://qiita.com/code0327/items/66efb7ac56cdea5b0b0d

Julia の SymPy と Plotでやってみる。

  using SymPy
  @syms x
  y = -2x^2 + 4x + 1
  y |> string
  "-2*x^2 + 4*x + 1"

y の導関数 y' を求め,y1 と置く。

  y1 = diff(y)
  y1 |> string
  "4 - 4*x"

y1 = 0 を解き,切線の傾きが 0 になるときの x 座標を求める。

  x0 = solve(y1)[1];

そのときの y の値を求める。

  y0 = y(x => x0);
  println("頂点の座標は ($x0, $y0)")
  頂点の座標は (1, 3)

関数の定義

  f(x) = -2x^2 + 4x + 1
  f (generic function with 1 method)

必要最小限の描画

  using Plots
  plot(f, xlims=(-1, 3), label="")
  scatter!([x0], [y0], label="")

  using Plots
  f(x) = x^2 +x - 6
  plot(f, label="")

x 軸との交点を求めて...などしなくて,x^2 + x - 6 < 0 を解くだけでよい。

  using SymPy
  @syms x
  answer = solve(Lt(x^2 + x - 6, 0))
  answer |> string
  "(-3 < x) & (x < 2)"

  f(x) = x^2 + x + 1
  plot(f, label="")

問 2 に同じだが,解がないときは False が返される。

  solve(Lt(x^2 + x + 1, 0)) |> string
  "False"
コメント
  • X
  • Facebookでシェアする
  • はてなブックマークに追加する
  • LINEでシェアする

高次方程式・恒等式関連の問題を Julia の SymPy で解く

2021年11月21日 | ブログラミング

1. 高校数学の「高次方程式・恒等式」関連の問題をPythonで解く

https://qiita.com/code0327/items/66efb7ac56cdea5b0b0d

Julia の SymPy でやってみる。


  
  using SymPy
  @syms x
  f = x^3 - 1;
  # (1)
  f.(x => 2) |> string
  "7"
  # (2)
  f.(x => 1) |> string
  "0"
  # (3)
  factor(f) |> string
  "(x - 1)*(x^2 + x + 1)"
  # (4)
  solve(f) |> string
  "Sym[1, -1/2 - sqrt(3)*I/2, -1/2 + sqrt(3)*I/2]"
  # (1)
  solve(x^4 - 10x^3 +35x^2 - 50x + 24) |> string
  "Sym[1, 2, 3, 4]"
  # なお,
  factor(x^4 - 10x^3 +35x^2 - 50x + 24) |> string
  "(x - 4)*(x - 3)*(x - 2)*(x - 1)"
  # (2)
  solve(x^4 - 10x^2 +9) |> string
  "Sym[-3, -1, 1, 3]"
  # なお,
  factor(x^4 - 10x^2 +9) |> string
  "(x - 3)*(x - 1)*(x + 1)*(x + 3)"
  # (1)
  @syms a, b, c, x
  solve(Eq(x^2, a*(x - 2)^2 + b*(x - 2) + c), [a, b, c]) |> string
  "Dict{Any, Any}(c => 4, b => 4, a => 1)"
  # (2)
  @syms a, b, x
  solve(Eq((3x- 4) / ((x + 2) * (x - 3)), a / (x + 2) + b / (x - 3)), [a, b])
  Dict{Any, Any} with 2 entries:
    b => 1
    a => 2
  # (1)
  solve(Eq(3 / (x + 2) + 3 / (x - 2), 2)) |> string
  "Sym[-1, 4]"
  # (2)
  solve(Eq(3 / (x + 2) + 3 / (x - 2), 0)) |> string
  "Sym[0]"
  # (3)
  solve(Eq(sqrt(x + 3), x + 1)) |> string
  "Sym[1]"
  # (4)
  solve(Eq(-sqrt(x + 3), x + 1)) |> string
  "Sym[-2]"
コメント
  • X
  • Facebookでシェアする
  • はてなブックマークに追加する
  • LINEでシェアする

ベクトルの内積関連の問題を Julia の SymPy で解く

2021年11月21日 | ブログラミング

続編書きました
https://blog.goo.ne.jp/r-de-r/e/69e78b604d5a75003c716cd20c47e9de

 

高校数学の「ベクトルの内積」関連の問題をPythonで解く

https://qiita.com/code0327/items/b8614649757110c654b6

「 高校数学の「ベクトルの内積」関連の問題をPythonで解く 」を参考に、値を最後に代入してみた。
https://qiita.com/mrrclb48z/items/2d67795a90778b1ea2d3

Julia の SymPy でやってみる。

問題 1. 次のベクトル a, b の内積と,それらがなす 角θ (0 ≦ θ ≦ π) を求めよ。

(1) a = [2, -3], b = [-4, 6]

まずは,ベクトルの設定からであるが,Python のように sympyMatrix は使わない。公式マニュアルにも,"Avoid sympy.Matrix" と書いてある。

Sym[a, b, c ...] で,Sym 型の配列(今の場合はベクトル)が定義される。

注意すべきは,Sym は関数ではないということ。例えば,Julia では,Int[2, 3] と書くと,2 と 3 を要素とする整数ベクトルが定義できるが,それと同じである。Sym は [a, b, c, ...] の型を示しているのである。

なお,SymPy では(Python の場合も同じであるが),変数の宣言が必要なのは右辺に初めてで来る場合であり,左辺で初めてでてくの変数は宣言する必要はない(しても不都合はないが)。つまり,右辺の変数は左辺の変数の型を持つ変数として宣言されると同時に定義もされるわけである。

  using SymPy
  a = Sym[2, -3];

左辺に初めて出てくる a は,Sym 型の要素を持つベクトル変数として宣言されると同時に定義される。

REPL では自動的に LaTeX 形式で表示されるが,文字型で表示するために "オブジェクト |> string" と言う形式で指示する。

  a |> string
  "Sym[2, -3]"

a は Sym 型のベクトル(1 次元配列)である。

  typeof(a)
  Vector{Sym} (alias for Array{Sym, 1})
  b = Sym[-4, 6]
  b |> string
  "Sym[-4, 6]"

関数などを使用する場合,Python の sympy では,場合によって,どのパッケージ中の関数であるかを明示する必要があるが,Julia の SymPy では,式の評価は対象とするオブジェクトの型によって自動的に適切に行われる。

a, b の内積は a'b,a のノルムは sqrt(a'a) で求めることができる。a'ba' * b ≡ transpose(a) * b と同じである。

  a'a |> string # a' * a と同じ
  "25"
  sqrt(a'a) |> string # ノルム
  "5"
  a'b |> string # 内積
  "sqrt(x) + 7"

(1) の場合は直接以下によって解 π が求められる。

  acos((a'b)./(sqrt(a'a)*sqrt(b'b))) |> string
  "pi"

(2) a = [1, 1], b = [1-√3, 1+√3]

  using SymPy
  a = Sym[1, 1]
  a |> string
  "Sym[1, 1]"

b の定義で直接 sqrt(3) を使うと,数値解を求めようとするので,それを避けるために変数 x を用いて sqrt(x) とする。x は右辺で初めて出てくるので @syms で宣言する。

  @syms x
  b = Sym[1-sqrt(x), 1+sqrt(x)]
  b |> string
  "Sym[1 - sqrt(x), sqrt(x) + 1]"

説明のために,段階的に数式を展開していく。

  acos((a'*b)./(sqrt(a'*a)*sqrt(b'*b))) |> string
  "acos(sqrt(2)/sqrt((1 - sqrt(x))*(1 - conjugate(sqrt(x))) + (sqrt(x) + 1)*(conjugate(sqrt(x)) + 1)))"

この段階ではまだ sqrt(x) が実数なのか虚数なのかわかっていないので式が複雑になっている。 そこで,x に 3 を代入する。

  acos((a'*b)./(sqrt(a'*a)*sqrt(b'*b)).(x => 3)) |> string
  "acos(sqrt(2)/sqrt((1 - sqrt(3))^2 + (1 + sqrt(3))^2))"

まだ,簡約化されていないので,更に simplyfi() すると,きれいな結果になる。

  simplify(acos((a'*b)./(sqrt(a'*a)*sqrt(b'*b)).(x => 3))) |> string # "pi/3"
  "pi/3"

もし最初から sqrt(3) でやってしまうと,数値解を求めようとするし,中途で評価が止まってしまう。

  a = Sym[1, 1]
  b = Sym[1-sqrt(3), 1+sqrt(3)]
  acos((a'*b)./(sqrt(a'*a)*sqrt(b'*b))) |> string
  "acos(0.353553390593274*sqrt(2))"

acos の中は

  0.353553390593274*sqrt(2)
  0.5000000000000003

直接 0.5 を与えると,数値解が標示されてしまう。

  acos(0.5)
  1.0471975511965979

この数値が π/3 であることは,知っている人は知っている。

  pi/3
  1.0471975511965976

問題 2. 次の 2 つのベクトルが張る平行四辺形の面積を求めよ。

a = [1, 1], b = [1 - sqrt(3), 1 + sqrt(3)]

平行四辺形の面積は,sqrt(a'a * b'b - (a'b)^2) で求められるが,問題 1. の場合と同じく,いきなり sqrt(3) としてしまうと数値解の方へ流れてしまう。

  using SymPy
  @syms x
  a = Sym[1, 1]
  b = Sym[1-sqrt(x), 1+sqrt(x)]
  b |> string
  "Sym[1 - sqrt(x), sqrt(x) + 1]"
  sqrt(a'a * b'b - (a'b)^2) |> string
  "sqrt(2*(1 - sqrt(x))*(1 - conjugate(sqrt(x))) + 2*(sqrt(x) + 1)*(conjugate(sqrt(x)) + 1) - 4)"
  sqrt(a'a * b'b - (a'b)^2).(x => 3) |> string
  "sqrt(-4 + 2*(1 - sqrt(3))^2 + 2*(1 + sqrt(3))^2)"
  simplify(sqrt(a'a * b'b - (a'b)^2).(x => 3)) |> string
  "2*sqrt(3)"

問題 3. 次のベクトル a に垂直な単位ベクトル u を求めよ。

a = [3, 4]

u = [x, y] とし,
a ⊥ y なので,a'u = 0  …… (1)
|u| = 1 なので,sqrt(x^2 + y^2) = 1  …… (2)
の連立方程式を解く。

  using SymPy
  @syms x, y
  a = Sym[3, 4]
  u = Sym[x, y];
  unorm = sqrt(x^2 + y^2)
  unorm |> string
  "sqrt(x^2 + y^2)"
  answer = solve([a'u, unorm - 1], [x, y])
  answer |> string
  "Tuple{Sym, Sym}[(-4/5, 3/5), (4/5, -3/5)]"

問題 4. 次のベクトル a を法線ベクトルとして,点 (3,2) を通る直線の方程式を求めよ。

問題 3. に引き続き,answer を引用して,

  c1 = answer[1][1]
  c2 = answer[1][2]
  @syms x, y
  solve((x - 3) / c1 - (y - 2) / c2, y) |> string
  "Sym[17/4 - 3*x/4]"

まとめ

Julia の SymPy のほうが簡潔に書けるようだ。

コメント
  • X
  • Facebookでシェアする
  • はてなブックマークに追加する
  • LINEでシェアする

クレジットカードのチェックディジット

2021年11月20日 | ブログラミング

クレジットカードのチェックディジット

クレジットカード番号には規則性がある。

一見して区別はできないが,ある計算処理をした結果を見れば,それが真正な番号であるかそうでないかはすぐにわかる。

数字の各桁が特定の規則に従う場合,それぞれの桁について計算された数(チェックディジットと呼ばれる)を新たに数字の桁に付加することもあるし,計算された数字が特定の値になることを利用することもある。

クレジットカードの場合は後者である。クレジットカード番号の奇数桁と偶数桁について計算した最終結果の剰余が0になれば(つまり末尾が 0 になれば)正しいクレジットカード番号であるとする,ルーンアルゴリズムというのを採用している。

詳しい計算方法はプログラムを読めばわかることなので,ここでは述べない(ちゃんと知りたい人は,解説ページがいろいろあるので検索するとよい)。

以下が「クレジットカードのチェックディジット ルーンアルゴリズム」のプログラムである。

与えられた16桁の数字から計算される数値とそれが真正なものであるかどうか(true/false)を返す。

  function isvalidcard(n)
      magicnumber = 0
      for (i, digit) in enumerate(Char[string(n)...])
          digit = parse(Int, digit)
          if i % 2 == 1
              digit *= 2
              digit > 9 && (digit -= 9)
          end
          magicnumber += digit
      end
      magicnumber, magicnumber % 10 == 0
  end
  isvalidcard (generic function with 1 method)

6922184350331903 は正しいカード番号であろうか?

  isvalidcard(6922184350331903) # (63, false)
  (63, false)

計算された数は 63 となり,末尾が 0 でないので,真正なカード番号ではないことがわかる。

3566002020360505 は正しいカード番号であろうか?

  isvalidcard(3566002020360505) # (50, true)
  (50, true)

計算された数は 50 となり,末尾が 0 なので,真正なカード番号であることがわかる。

では,出鱈目に作成された 16 桁の数字が真正なカード番号である確率はどのくらいであろうか?

計算しないで,瞬時に正解が思い浮かんだ人は鋭い!

例えば 15 桁の数,123456789012345 の末尾に,例えば 0 を付して,1234567890123450 という番号を作って,それが真正なカード番号かどうか調べてみる。(最も,そのうちの何桁かはカード発行会社の識別番号だったりするので,必ずしもそれが実在しうるかどうかは別であるが)

  isvalidcard(1234567890123450) # (58, false)
  (58, false)

計算結果が 58 で末尾が 0 になるには 2 足りない。

つまり,1234567890123452 なら真正なカード番号だ。

  isvalidcard(1234567890123452) # (60, true)
  (60, true)

最初の 15 桁の数が 123456789012345 の場合,16 桁目の数字は 0〜9 通りあるが,真正な番号になるのはそのうちのひとつだけである

他のどの 1 桁についても,同じことがいえる。

つまり,ランダムに生成された 16 桁の数字がクレジットカード番号として真正なものである確率は 1/10 であるということだ。

人によっては,これは由々しき問題だと思うかもしれないが,実際には(最近というかかなり前から)セキュリティーコードと呼ばれる3,4桁の付加数字が要求されるようになっているし,名義人の名前も必要なのであまり心配する必要はないだろう。

1000000000000000〜9999999999999999 のランダムな番号を生成し,それが申請なクレジット番号である割合を求めるプログラムは以下の通り。

  n = 10^6
  validcard = 0
  for i in rand(1000000000000000:9999999999999999, n)
      isvalidcard(i)[2] && (validcard += 1)
  end
  println("valid card rate = $(validcard/n)")
  valid card rate = 0.10012

理論どおり,ほぼ 1/10 となっていることが確認できる。

コメント
  • X
  • Facebookでシェアする
  • はてなブックマークに追加する
  • LINEでシェアする

Julia v1.6.4, v1.7.0-rc3 が出ています

2021年11月20日 | ブログラミング

Nov 19, 2021 Julia v1.6.4
 M1 チップの場合,私がインストールしているパッケージでは PNGFiles だけがプリコンパイルに失敗しています。

Nov 15, 2021 Julia v1.7.0-rc3 (Intel or Rosetta / M-series Processor)
 M1 チップへの対応は,まだまだ問題が残っているものが多いようです。

コメント
  • X
  • Facebookでシェアする
  • はてなブックマークに追加する
  • LINEでシェアする

中国の剰余定理

2021年11月15日 | ブログラミング

今年 2021 年の次の 3 個の素数年は, 2027, 2029, 2039 年である。

2027 で割ると余りが 101
2029 で割ると余りが 103
2039 で割ると余りが 107

となるような最小の正整数を求めよ

答えは,ずっと下へスクロール

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

function chinese_remainder_theorem(a1, r1, a2, r2, a3, r3)
    #=
    a1, r1 最初の除数と剰余
    a2 r2 二番目の除数と剰余
    a3, r3 最後の除数と剰余
    =#
    gcd1, m1, n1 = xgcd(a1, a2*a3)
    gcd2, m2, n2 = xgcd(a2, a1*a3)
    gcd3, m3, n3 = xgcd(a3, a1*a2)
    answer = r1*a2*a3*n1 + a1*r2*a3*n2 +a1*a2*r3*n3
    while answer < 0
        answer += a1*a2*a3
    end
    answer
end

function xgcd(a, b)
    m, n, x, y = 1, 0, 0, 1
    while b > 0
        q = a ÷ b
        x, m = m - q * x, x
        y, n = n - q * y, y
        a, b = b, a % b
    end
    a, m, n
end

chinese_remainder_theorem(2027, 101, 2029, 103, 2039, 107)

求まる解は 7966458745

確かに,

16352423282 % 2027 # 101
16352423282 % 2029 # 103
16352423282 % 2039 # 107

 

 

コメント
  • X
  • Facebookでシェアする
  • はてなブックマークに追加する
  • LINEでシェアする

PVアクセスランキング にほんブログ村

PVアクセスランキング にほんブログ村