整数の三乗根を誤差なしで求める

三乗根は Math 標準ライブラリの Pow を使って

double val = Math.Pow((double)number, 1.0 / 3.0)

こんな風に求められるけれども、number が整数のときは
これでは誤差が出るので使い物にならない。

なのでプログラムを自分で書く!

呼び出し側。最近の .NET では main なんて書かない。

using MathLibrary;

Console.WriteLine(MathLib.CubeRoot(679L * 679L * 679L));

ライブラリなんで static ライブラリでいいじゃろ。たぶん。
本丸の処理はこんなんだ。

namespace MathLibrary
{
    public static class MathLib
    {
        private static readonly int[][] cubeRootTable =
        [
            [0, 0],
            [1, 1],
            [8, 8],
            [27, 7],
            [64, 4],
            [125, 5],
            [216, 6],
            [343, 3],
            [512, 2],
            [729, 9],
        ];

        public static long CubeRoot(long num)
        {
            List<int> result = [cubeRootTable[(int)(num % 10L)][1]];

            for (long i = num / 1000L; i > 0L; i /= 1000L)
            {
                int j;
                for (j = 1; j < 10; j++)
                {
                    if ((int)(i % 1000L) < cubeRootTable[j][0])
                    {
                        break;
                    }
                }

                result.Add(cubeRootTable[j - 1][1]);
            }

            result.Reverse();

            long val = 0L;
            foreach (int v in result)
            {
                val = 10L * val + (long)v;
            }

            // 検算
            if (val * val * val == num)
            {
                return val;
            }
            else
            {
                return 0L;
            }
        }
    }
}

3乗根が整数にならない場合は 0 を返す!(どんだけ手抜きやねんw)

実行してみる。

679

D:\srcs\c_sharp\MathLibrary\ConsoleApp\bin\Debug\net8.0\ConsoleApp.exe (プロセス 16328) は、コード 0 で終了しました。
デバッグが停止したときに自動的にコンソールを閉じるには、[ツール] -> [オプション] -> [デバッグ] -> [デバッグの停止時に自 動的にコンソールを閉じる] を有効にします。
このウィンドウを閉じるには、任意のキーを押してください...

誤差なんか出ない!やったね!!

約数を求める(完全版)

(define nil '())

(define (gen-primes limit)
  (let ((v (make-vector (+ limit 1) 1)))
    
    (define (set-not-prime! ini-idx)
      (define (iter i)
        (if (> i limit)
            'done
            (begin
              (vector-set! v i 0)
              (iter (+ i ini-idx)))))
      (iter (* ini-idx 2)))
    
    (define (init)
      (vector-set! v 0 0)
      (vector-set! v 1 0)
      (set-not-prime! 2))

    (define (set-prime! i)
      (if (> i (floor (sqrt limit)))
          'done
          (begin
            (set-not-prime! i)
            (set-prime! (+ i 2)))))

    (define (print-prime l h)
      (define (iter i)
        (if (> i h)
            (newline)
            (begin
              (display (vector-ref v i))
              (display " ")
              (iter (+ i 1)))))
      (iter l))

    (define (count-prime)
      (define (iter i c)
        (if (> i limit)
            c
            (iter (+ i 1) (+ c (vector-ref v i)))))
      (iter 1 0))

    (define (make-primes n)
      (let ((primes (make-vector n 0)))
        (define (iter i j)
          (cond ((>= j n) primes)
                (else
                 (let ((p (vector-ref v i)))
                   (if (= p 1)
                       (begin
                         (vector-set! primes j i)
                         (iter (+ i 1) (+ j 1)))
                       (iter (+ i 1) j ))))))
        (iter 2 0)))
    
    (begin
      (init)
      (set-prime! 3)
      (make-primes (count-prime)))))

(define (prime? n)
  (let ((primes (gen-primes 100)))
    (define (search l h)
      (let* ((m (+ l (floor (/ (- h l) 2))))
             (p (vector-ref primes m)))
        (cond ((> l h) #false)
              ((= n p) #true)
              ((< n p) (search l (- m 1)))
              (else (search (+ m 1) h)))))
    (if (< n 2)
        #false
        (search 0 (- (vector-length primes) 1)))))

(define (factorize num)
  (let ((primes (gen-primes num)))
    (define (iter n i facts)
      (if (= n 1)
          facts
          (let ((p (vector-ref primes i)))
            (if (= 0 (remainder n p))
                (iter (/ n p) i (append facts (cons p nil)))
                (iter n (+ i 1) facts)))))
    (if (prime? num)
        (list num)
        (iter num 0 nil))))

;;

(define (element-of-set? x set)
  (cond ((null? set) #f)
        ((equal? x (car set)) #t)
        (else (element-of-set? x (cdr set)))))

(define (uniq set)
  (define (iter x s)
    (if (null? x)
        s
        (iter (cdr x)
              (if (element-of-set? (car x) s)
                  s
                  (cons (car x) s)))))
  (iter set nil))

(define (subsets s)
  (if (null? s)
      (list nil)
      (let ((rest (subsets (cdr s))))
        (append rest (map (lambda (x) (cons (car s) x)) rest)))))

(define (divisor num)
  (let ((facts (factorize num)))
    (cons 1
          (sort (map (lambda (x) (apply * x)) (uniq (cdr (subsets facts))))))))

gen-prime, prime?, factorize 手続きを実装しました。
それぞれ、素数列を生成する手続き、素数判定をする手続き、素因数分解した結果をリストで返す手続きです。
素数列は大きくなるのでリストではなく、vector を使っています。また、素数の生成のアルゴリズムはエラトステネスの篩を使用しています。

divisor 手続きは、引数に数値を受け取るようにして、factorize 手続きを使うように修正します。

gosh> (divisor 12)
(1 2 3 4 6 12)
gosh> (divisor 256)
(1 2 4 8 16 32 64 128 256)
gosh> (divisor 111)
(1 3 37 111)

このように約数を求めることができます。

約数を求める

(define nil '())

(define (element-of-set? x set)
  (cond ((null? set) #f)
        ((equal? x (car set)) #t)
        (else (element-of-set? x (cdr set)))))

(define (uniq set)
  (define (iter x s)
    (if (null? x)
        s
        (iter (cdr x)
              (if (element-of-set? (car x) s)
                  s
                  (cons (car x) s)))))
  (iter set nil))

(define (subsets s)
  (if (null? s)
      (list nil)
      (let ((rest (subsets (cdr s))))
        (append rest (map (lambda (x) (cons (car s) x)) rest)))))

(define (divisor facts)
  (cons 1
        (sort (map (lambda (x) (apply * x)) (uniq (cdr (subsets facts)))))))

結論から言うと、こんなプログラムになります。
12 の約数を求めてみます。12 を素因数分解すると 2 * 2 * 3 になるので、
'(2 2 3) を divisor 手続きの引数に与えて評価します。

gosh> (divisor '(2 2 3))
(1 2 3 4 6 12)

素因数から約数を求めるロジックは、素因数リストから、すべての部分集合を求めて、
それぞれの部分集合の要素を掛け算すれば求めることができます。
各手続きは、

subsets は、集合 s の部分集合をすべて求める
uniq は集合 set から重複を取り除く
element-of-set? は集合 set に x を含むか調べる

となっています。
subsets はすべての部分集合を求めます。集合 '(a b c) の場合は、

gosh> (subsets '(a b c))
(() (c) (b) (b c) (a) (a c) (a b) (a b c))

このようになります。
並びの見た目がちょっと悪いですが、これがすべての部分集合です。
空の集合 '() と、自分自身 '(a b c) も部分集合に含むことに注意してください。


素因数分解のプログラムは別の機会に気が向いたら掲載したいと思います。

売れてないのん?

だからスクエニくんさあ・・・。

 

PS5 独占なんて売れなくなるからやめろって言ったやんけ。いわんこっちゃないわ。マルチプラットフォームでグローバル展開すんのがデフォやねん。はじめから PC で出せや。

 

MOD でティファやエアリスを・・・グヘヘ・・・

・・・・・

 

ハッ?

 

PS5 独占じゃ売れないからやめれっての。今やコンシューマ機より PC のほうが圧倒的に性能高いんだし、コンシューマ機のアドバンテージなんてないんだって。はっきり言おう。PS5 じゃ AAA タイトルは無理なんよ。3D アクションゲーは性能たらんくて無理なの。4K で 60FPS でないとか見向きもされんわ。性能がミドルクラスのゲーミング PC にも届いてないわ。

 

さらに、PS5 と PC で同時発売とかやられるとさ、性能面で PS5 に引っ張られて、微妙やねん。コントローラの問題で UI がクソになったり。キーボードで遊ぶゲームをコントローラでやるのはいいよ。それはプレイヤーの自由だからさ。キーボード&マウスでプレイしたい人も、コントローラーでプレイしたい人も居る。選択の自由があるのは素晴らしい事だよ。しかし PS5 コントローラ前提って話になると、その制約のせいで自由を奪われてしまうんよ。

 

しかも CERO とか規制厨がうるさくて、PC まで影響でるしさ。とにかく PS5 がいろいろ足を引っ張ってだな、うざいねん。PS5 大好きユーザーには悪いんだけどさ。PS5 が足枷になっとるんよ。ソニーもやる気無いし。日本市場スルーだしさ。

 

なので、PS5 独占で販売するのはやめなさい。わかった?スクエニくん。

円周率 π のお話

円周率って学校の授業では 3.14 と習うと思います。実際のより正確な値は

3.14159265358979323846264338327950288 ...

となり、小数点以下の桁が無限に続く値(無理数)です。

小数点以下が無限に続く数をいちいち書くのは面倒なので、
記号で「π」(パイと読みます)を使って表現します。

今回は円周率がどうして 3.14 なのかを解説したいと思います。

そもそも円周率って何?

円周率とは、半径 r の円の直径と円周の長さとの比を表しています。
例えば、半径 が 10cm の円の円周の長さは、

10cm * 2 * 3.14 ≒ 62.8 cm

として求めることができます。

では、なぜ円周の長さと直径の比が 3.14 なのか?
それを探っていきましょう。

六角形の外周はいくつ?

円の外周を考える前に、まず六角形の外周の長さを考えてみます。

円に各頂点がぴったりくっついた六角形です。
ちなみに、このとき六角形は円に内接すると言います。
六角形の各頂点を補助線を引いて結んでみます。

補助線はすべて直径と同じ長さになります。そして補助線は円の中心で交ります。

円の中に6つの三角形ができています。
三角形の頂点のそれぞれ角度(θ)は、360 度を 6 等分しているので 60 度です。
また、頂点を挟む二辺は半径 r と同じ長さです。
つまり、頂点の角度が 60 度、頂点を挟む二辺の長さが r の二等辺三角形です。

二等辺三角形の定義から、底角の角度は同じなります。
頂点が 60 度ということは、三角形の内角の和は 180 度ですから、

(180 - 60) / 2 = 60

底角の角度は 60 度になることがわかるので、
この三角形は実は一辺の長さが r である正三角形であることがわかります。

したがって円に内接する六角形の辺の長さの合計は 6r となります。
このとき、円の直径が 2r なので、

円の直径と六角形の辺の合計の比は、3 です。

ここで、

円周率とは、半径 r の円の直径と円周の長さとの比

のことでした。
つまり円周率が 3 だとすると、六角形の辺の長さになるわけです。

六角形と円ではだいぶ見た目が違いますよね?
六角形と円を同じだと言う人はいないと思います。
でもだいぶ円には近い気もしますよね?

じゃあ角数を増やしてみよう

円に内接するこの六角形は円にはまだ遠いですけど、結構いい線いってました。
この六角形をもっと角数を増やしていったら、一体どうなるでしょうか?
20角形、100角形、1000角形と角数を増やしていったら、
比率はどうなっていくでしょう。

手計算でやっていくのは大変なのでコンピューターに計算させましょう。

(define (ratio-of-side-length-to-radius r n)
  (let* ((top-angle (/ 360 n))
         (base-angle (- 180 90 (/ top-angle 2)))
         (side-length (* 2 r (cos (degrees->radians base-angle)))))
    (/ (* n side-length) (* 2 r))))

こんなプログラムになります。
ratio-of-side-length-to-radius 手続きは、引数 r に半径を、n に内接する正 n 角形の角数を与えます。


cos とか使うとラジアン(弧度法)が出てくるのでπとか出てきてしまうのですが、
πを求めてみるというお話なので弧度法の細かい内容は省いています。
degrees->radians という手続きを使用して、度数をラジアンに変換してごまかしていますw


まず六角形の辺の長さの比率を求めてみます。

gosh> (ratio-of-side-length-to-radius 1 6)
3.000000000000001

ちょっと誤差が出ていますが、3 なので合ってます。

では、角数を増やしてみましょう。

gosh> (ratio-of-side-length-to-radius 1 10)
3.0901699437494745
gosh> (ratio-of-side-length-to-radius 1 20)
3.1286893008046186
gosh> (ratio-of-side-length-to-radius 1 50)
3.139525976465665
gosh> (ratio-of-side-length-to-radius 1 100)
3.141075907812817
gosh> (ratio-of-side-length-to-radius 1 1000)
3.141587485879651
gosh> (ratio-of-side-length-to-radius 1 10000)
3.141592601913971
gosh> (ratio-of-side-length-to-radius 1 100000000)
3.1415926588122183

一億角形まで計算してみました。比率はどうでしょうか?
n の値が増えていくと、見覚えのある数字が出てきていませんか?

そうです。πが現れます。

内接する正n角形の角数をどんんどん増やしていくと、正n角形はどんどん円に近づいていきます。
直径と正n角形の辺の長さは、n を増やせば増やすほど π に近似していきます。
図を書くのはとても大変ですが、どんどん円になっていきそうだなって想像できますよね?


では n をとても大きい数、∞(無限)に近づけていったら果たしてどうなるでしょう。
円の中にできる三角形の頂点θはどんどん小さな値になっていきます。でも0にはなりません。
三角形の底辺もどんどん小さな値になっていきます。でも0にはなりません。
三角形の底角はどんどん90度に近づいていきますが、90度にはなりません。
三角形自体もどんどん潰れて細くなっていきますが、どこまで行っても二等辺三角形です。
そのとき、直径と正n角形の辺の合計(=外周)の比率はπに限りなく近づいていきます。


そこまでいくと、もはや人間には正多角形なのか円なのか区別はつかないでしょう。
円は正∞角形である。
と言い換えても良いかも知れません。


というわけで、
円周率はπ (3.1415 ...)である
ことがわかりました。
今回のように π の値をいちいち計算で求めなくても、
π の値はわかっているので、円の円周の長さ計算したいときは、直径に 3.1415 ... を掛け算すれば良いわけです。


これは円周率を求める計算方法のひとつで、他にもたくさんの計算方法があります。
興味があったら調べてみてください。

アニメ第二期

 

きたー!

二期はCパートからですねー。第一話から大波乱の予感。アニメでニーアオートマタ知って、ゲームやってない方は覚悟してくださいネ。(ニヤニヤ

 

ヨコオさんは、ロッカーにぶちこまれまくると思いますので、逃げてくださいw

久々にクロノアやる

アンコール(復刻版)をずっと寝かしてたんですけどもw、

25年ぶりになるんかな?(クロノア25周年だからね)

やってみたー。

PC版だとアケコンが使えなくって無理でしたw

PS5版もアケコン認識しなくて無理でしたwww

PS4版でクリアしたけども、結構時間かかったな。

 

ころさんはオリジナル版を配信してる。


www.youtube.com

いや、ころさんマジ上手いんですけど!それと、ころさんの良いところは、いつも楽しそうにプレイするところ。どれだけミスしても、楽しくプレイするところが良いよね。いや、ほんと上手い。まじで上手い。お世辞とかじゃなくて本気で上手い。尊敬するレベル!ミスしてもリカバリが早いんよ。観察眼が鋭いっていうか、分析力が高いってか、重要な事ってか、ヒントに気付くのが早いんよね。で、それがすぐ出来ちゃう。頭が切れるし*1、器用というか、ホントにゲームが上手い人だなって思う。

 

ひさびさにクロノアを自分でやってみて、そう思ったよ。

 

クリアした後のころさんもスキ!自分も初回プレイのときはマジ泣きしました。ゲームのアンケートで「感動して泣いちまったよぉ」って書いて送ったからねwww ゲームを開発したスタッフさんにきっと届いたと思うよ。そういう反響が多かったみたいで、当時公式にクロノアの「特設サイト」がオープンしたほどだからね。

 

クロノアはまじで神ゲーよな。ホントに素晴らしいゲームだよ。

いやぁ、ゲームって本当に良いもんですね。

*1:ころさんは東大卒らしい。わがりみー