2024年10月11日金曜日

10000回のループを使って、ちょっとした作業

 fx-CG50 の upython の実行速度に甘えて、ちょっとした作業をやってみました。
数列 1/k の総和を求める作業です。

sum(1/k, k, 1, 10000) という表記をしてみましたが、upython で書けば、こんな作業です。

sum = 0
for k in range(1, 10001) :
   sum = 1/k

1/k 自体が、無限大で 0 に収束するので、「総和も 0 に収束して紅か ?」と考えるのですが、実際には、無限大まで加算すると、発散してしまうのですね。

取り敢えず、10000まで累積総和を計算させてみます。
10000以上にすると、結果はさらに増えていくのですが、この計算、加算の数を多くすると、それにつれて累積数が増えていくのです。
それを「視覚的に」納得しようと。さて、どうするか ?

「手始めに、1から10000までの区間で、グラフを描いたらよかろう !」

グラフ電卓なので、簡単操作でグラフを描いてくれそうですが、実際には、チョット面倒な事になりました。「級数の総和を求める」というのは「関数」で書けません。

そこで、upython のパワーでグラフを描いてみることにしました。
しかし、やってみると解りますが、どうもこの級数和、収束するのかしないのか、はっきりした感じでは掴めません。

そこで「飛び道具」的な手段を導入しましました。対数目盛りのグラフです。
グラフ電卓ですから、対数目盛りのグラフなんかも簡単に描けそうなものですが、実際には、そういった手段が提供されておりません。

そこで、簡単に横軸対数目盛りのグラフを描く、upython スクリプトを用意しました。
このスクリプトを読み込ませると、じわじわと累積して、点を打っていきます。
ループ上限が10000にしてあり、横軸のみを対数目盛りにして、累積数の点を打っておきます (縦横の軸までは描きません) 。

描かれたグラフをみるとわかりますが、それまでは「どこかで伸び悩みそうな累積数のグラフ」が、横軸を対数目盛りにすると、直線になっているのが解ります。
直線ですから、この先、いくらでも増えていくのは想像に難くありません。

こうして、視覚的に納得する事ができました。流行り(?)の「納得と共感」です。

今回も upython の計算速度を利用した「力技」な話題となりました。
こうしたちょっとした計算が手のひらの上で愉しめます。

数学的には、奥深い話題が展開できそうなのですが、その辺りは目下、お勉強の最中なので、機会がありましたらいずれまた、としておきます。

【スクリプト】

from casioplot import *
import math

wi=382
he=191
xs=1
xe=10000
ys=0
ye=10

sum=0
for i in range(xs,xe+1) :
  sum+=1/i
  ii=(math.log10(i)-math.log10(xs))/(math.log10(xe)-math.log10(xs))*wi
  jj=(ye-sum)/(ye-ys)*he
  set_pixel(int(ii),int(jj))
  show_screen()


2024年10月4日金曜日

自家製 copysign() の顛末

 過日、Biomorph for fx-CG50 の記事で、自家製 cmath module を開陳致しました所、K 様から、コメントを頂戴致しました。

K 様の方法で cmath.sqrt() を書くと、計算がスムースに行く上、境界部分での符号の扱いも良い具合になるのですが、残念な事に、fx-CG50 には math.copysign() がなく、適用な手段で代替関数を用意する必要があると、K 様も申されておりました。

そこで、copysign() 風の関数を自作すれば、よかんべ、と調べ始めたのですが、copysign() では、符号チュエックのため、0.0 と -0.0 という値に対応するという話でありますネ。ナニソレ ?

浮動小数点数も符号が用意されているのは解っていたつもりですが、折角だから、0.0 にも符号を付けて、0.0 と -0.0 の2つを用意しよう、という話らしい。
処理系 (コンパイラ) が、ゼロ符号に対応した場合に、python もこれに対応できるらしく、最近のモダンな処理系では、-0.0 という浮動小数点数に対応されている様です。

そんな具合で、我らが fx-CG50 の upython も、-0.0 という数値自体は扱える様に出来ております。
シェルを呼び出して、-0.0 と入力すると、そのもの -0.0 が答えとして返ってきますネ。
しかし、数値としては 0.0 と -0.0 、同じ「ゼロ」でありますから、== で比較すると、True が返ります。何なの、ソレ !?

では、アトムの比較を行う、is で比較をしたら区別が出来るんじゃないか、と思ったのですが、fx-CG50 の場合、これはアカンのですネ。

>>> -0.0 is 0.0 
False
>>> -0.0 is -0.0 
False
>>> 0.0 is 0.0 
False


なんと、0.0 という同じ値を比較しても、False となりますネ。「どうなっとんジャイ、ワレ !」

この謎を解く鍵は、アトムのid を取得する id() でした。

>>> id(-0.0)
2351257904
>>> id(-0.0)
2351258160 
これは一例ですが、同じ操作をしているのに、毎回、id が変化します。

idという値、どうもアトムを保持するheap空間のテーブルインデクス、有り体に言えばポインタの様な値らしい。
これが毎度違う値になるというのは、毎度、浮動小数点数値アトムが振り出されてheapに作られる、という具合の様ですネ。
PCのpython では、0.0 は一意に決まった数値アトムという扱いなので、何度操作しても変化する事はなさそうですが、電卓の方は、そこまで手が回っていないのかも知れません。
( 但し、当方の fx-CG50 upython は、チョット古めです )

こうなると、0.0 と -0.0 の区別は難しいなァ、と思っていたのですが、(数値アトムを)文字列に変換する関数 str() がありました。これを使うと、0.0 , -0.0 、両方とも表示通りに文字列になるのです。これで、0.0 と -0.0 の区別が出来ますネ、ヨカッタ、ヨカッタ。

そんな事が解ったので、自家製の copysign() として、チョット苦し紛れなコードを開陳した次第です。「ご査収ください」

#  copysign() like something ...
def  csign(x, y) :
  if y == 0.0 :
    if str(y) == '-0.0' :
      return -abs(x)
    else :
      return abs(x)
  else :
    if y < 0 :
      return -abs(x)
    else :
      return abs(x)

本来なら math.copysign() と、math module にあるものではありますが、後から追加するわけにも行かないので、自家製cmath module の中に、邪魔にならないよう csign() と名前を変えて導入致しました。

これを使ってcmath.sqrt() もK 様提案の方法にしたのですが、cmat.csign() が足を引っ張っているので、速度は余り期待できません、ハイ。

さて、こうした具合で動いている upython ではありますから、以前に、電卓喫茶様が「fx-CG50 upython で 色指定をタプルで書くと、速度が激落ちする」という報告をされておりました。これは、毎度、数値を含むタプルを生成して、heapに置いているから、時間が掛かっているのではないかという推測をしたのですが、少しは判断材料になったのかも、と思う所です。色指定のタプルを予め作っておき、適当な変数名にbindして、変数を呼び出す様にする事で、オーバーヘッドが大分少なくなる模様です。