あらきけいすけのメモ帳

あらきけいすけの雑記帳2

Pythonでモジュール名、関数名を変数化する

例1

import openpyxl
wb = openpyxl.Workbook()
wb.save('boook.xlsx')

を書き換える

import importlib
x = 'openpyxl'
y = 'Workbook'
z = getattr( importlib.import_module( x ) , y )
wb = z()
wb.save('boook.xlsx')

例2

import openpyxl
print( openpyxl.utils.column_index_from_string('AA') )

を書き換える

import importlib
a = 'openpyxl'
b = importlib.import_module( a )
c = 'utils'
d = getattr( b , c )
e = 'column_index_from_string'
f = getattr( d , e )
print( f('AA') )

Pythonでdictをソースコードとして使えるように出力する関数

関数定義

  def srcWrite(Q,n,d,width=30):
    with open(f'src_{n}_.py','w',encoding='utf-8') as f :
      print(f'# -*- coding: utf-8 -*-',file=f)
      print(f'# {Q}',file=f)
      print(f'{n} = \\',file=f)
      pprint.pprint(d,sort_dicts=False,width=width,stream=f)

関数呼び出し

Q=[コメント]
n='[dict名(識別子)]'
srcWrite(Q, n, eval(f'{n}'))

sin(x)/xの証明にx<tan(x)はいらない(高校ではなく大学教養程度なら)

授業のための覚書

高校レベルだと公式:\displaystyle \lim_{x\to0}\frac{\sin x}{x}=1の証明は不等式 \sin x < x < \tan x を出発点にしている。

ここで右側,  x < \tan x は、「面積」で評価すると円の面積の評価に\displaystyle \lim_{x\to0}\frac{\sin x}{x}=1を使っているので循環論証となり、「弧長」で評価すると曲線の長さのRiemann積分を基礎とした不等式評価になるので、いずれも高校数学の範囲を超えてしまう。

どうせ超えるのなら、円周の長さをRiemann積分*1で評価する方が早い。円周の長さが2\pi Rであることは、\piの定義より所与とする。

以下 N=3\times2^k where k=1,2,3,... とする。まず半径 R の円に内接する正 N 角形の周長\displaystyle l_N := 2 N R \sin\frac{\pi}{N}  N の単調増加列である。(\displaystyle \because 2NR\sin\frac{\pi}{N} \displaystyle  = 4NR\sin\frac{\pi}{2N}\cos\frac{\pi}{2N} \displaystyle  < 4NR\sin\frac{\pi}{2N}. )2点を結ぶ最短距離は線分だから、円周より内接正 N 角形の周長が短い: l_N < 2 \pi R .だから単調収束定理*2より\displaystyle \lim_{N\to\infty}l_N は収束する。有界単調性だけでは極限が 2 \pi R とはまだ言えない。

内接する正 N 角形は領域\displaystyle T:=\left\{r: r \in \left[R\cos\frac{\pi}{N}, R\right] \right\} にいる。 N\to\infty で領域 T が半径 R の円に収束するので、周長の極限値\displaystyle \lim_{N\to\infty} 2 N R \sin\frac{\pi}{N} \displaystyle  = 2 \pi R \lim_{N\to\infty}\cos\frac{\pi}{N} \displaystyle  = 2 \pi R. 後半部分だけでは「極限値があるとすれば 2\pi R が唯一の候補」までしか言えない。

以上より\displaystyle \lim_{N\to\infty} 2 N R \sin\frac{\pi}{N} = 2 \pi R  \Longrightarrow \displaystyle \lim_{x\to0} \frac{ \sin x }{ x } = 1 where  \displaystyle x= \frac{\pi}{N}.

lim(x→0) sin(x)/x=1の証明のつもりで始めた円の面積の公式の証明

証明の動機:小学校の算数での円の面積の説明「丸いピザを切って並べ替える操作を、長方形になるまで細かく切り続ける」のを数学的に論証しないのがキモチワルイから。

まず、、、
(直感的には)半径 r の扇形の面積は半径 r の2乗と中心角 \theta に比例するので: S = C r^2 \theta .
(円の面積の公式 S = \pi r^2 は使えない)
(直感的には)半径 r の円とその円に内接、外接する正N角形の面積の大小関係より
 \displaystyle 2N \times \left( \frac12 r \times r \sin\frac{\pi}{N} \right) <  2N \times \left( C r^2 \frac{\pi}{N} \right) < 2N \times \left( \frac12 r \times r \tan\frac{\pi}{N} \right)
 \displaystyle \Longrightarrow N \sin\frac{\pi}{N} < 2 C \pi < N \tan\frac{\pi}{N}
ここで N \sin\frac{\pi}{N} は上に有界な単調増加列なので単調収束定理*1より収束する: \displaystyle \lim_{N\to\infty} N \sin\frac{\pi}{N} = \alpha
一方で  \displaystyle N \tan\frac{\pi}{N} は下に有界な単調減少列なので収束する:  \displaystyle \lim_{N\to\infty} N \tan\frac{\pi}{N} = \frac{ \displaystyle \lim_{N\to\infty} N \sin\frac{\pi}{N} }{ \displaystyle \lim_{N\to\infty} \cos\frac{\pi}{N}} = \frac\alpha1 = \alpha
よって不等式よりはさみうちの原理から: \alpha = 2C \pi

一方で、、、
半径 r の円に内接する正N角形の周長は:  \displaystyle l_{N} = 2N \times r \sin\frac{\pi}{N}
これは N \to \infty で収束して:  \displaystyle \lim_{N\to\infty} l_N = 4 C \pi r
一方、半径 r の円に外接する正N角形の周長は:  \displaystyle L_{N} = 2N \times r \tan\frac{\pi}{N}
これも N \to \infty で収束して:  \displaystyle \lim_{N\to\infty} L_N = 4 C \pi r
直感的には]これらは円の周長] 2 \pi r (円周率 \pi の定義)に収束しているはずなので: C = 1/2

よって、、、
円の面積は  \displaystyle S = \lim_{N\to\infty} 2N \times \left( C r^2 \frac{\pi}{N} \right) = \pi r^2
それから  \displaystyle \lim_{N\to\infty} \frac{N}{\pi} \sin\frac{\pi}{N} = 1 (点列収束だけど  \displaystyle \lim_{x\to0+0} \frac{ \sin x}{x} = 1
ついでに  \displaystyle \pi < N \tan\frac{\pi}{N} より、円周より外接する正N角形の周長が長い。(円周より内接する正N角形の周長が短いのは直感的に]「2点の最短距離は2点を結ぶ線分」であることより明らかであったが、外接多角形はそうではなかった。参照↓)

蛇足、、、
長さにこだわった \theta < \tan\theta の論証の |PQ| < |RT| の部分が |PQ| < \overset{\large\frown}{PQ} < |RT| でないところが、曲線の長さとその外の線分の比較ができていないので、とてもキモチワルイ↓

出典:J. Stewart, "Calculus" 8-th Ed., (2016), Appendix F, p. A43

Google Colab で __file__ の代わりに Jupyter notebook のファイル名を知る方法

from requests import get
from socket import gethostname, gethostbyname
ip = gethostbyname(gethostname()) # 172.28.0.12 (2024.2.19現在)
filename = get(f"http://{ip}:9000/api/sessions").json()[0]["name"]
print(ip,filename)

stackoverflow.com

組立除法でcos, sinの3倍角,4倍角,5倍角の公式を求める

授業のための覚書*1。積和の公式*2と、3項間漸化式を組立除法*3で解けることを組み合わせることでn倍角の公式を組立除法で求めることができる:

 \begin{array}{r|llll} 次数n & 3 & 2 & 1 \\  & 1 & 0 & 0 \\\hline 2C  & & 2C  & \\ -1 & & & -1 \\\hline & 1 \ ||& 2C  & -1 \end{array}
 \therefore \cos3x = (2\cos x)(2\cos^2x-1) - \cos x \\ \hspace{3em} = 4\cos^3x - 3\cos x
 \therefore \sin3x = (2\cos x)(2\cos x\sin x) - \sin x

 \begin{array}{r|llll} 次数 n & 4 & 3 & 2 & 1 \\  & 1 & 0 & 0 & 0\\\hline 2C  & & 2C  & 4C^2 \\ -1 & & & -1 & -2C  \\\hline & 1 & 2C  \ ||& 4C^2-1 & - 2C  \end{array}
 \therefore \cos4x = (4\cos^2x - 1)(2\cos^2x -1) - 2\cos x\cos x
 \therefore \sin4x = (4\cos^2x - 1)(2\cos x\sin x) - 2\cos x\sin x

 \begin{array}{r|llll} 次数 n & 5 & 4 & 3 & 2 & 1 \\ & 1 & 0 & 0 & 0 & 0 \\\hline 2 C  & & 2 C  & 4 C^2 & 8 C^3 - 2 C \\ -1 & & & -1 & -2 C & -4 C^2 + 1 \\\hline & 1 & 2C  & 4C^2-1 \ ||& 8C^3 - 4C & -4 C^2 + 1 \end{array}
 \therefore \cos5x = (8\cos^3x - 4\cos x)(2\cos^2x-1)+( - 4\cos^2x + 1)\cos x
 \therefore \sin5x = (8\cos^3x - 4\cos x)(2\cos x\sin x) + (- 4\cos^2x + 1)\sin x

*1:チェビシェフの多項式の次数の低いものを筆算する方法:チェビシェフ多項式 - Wikipedia

*2:三角関数の公式の一覧 - Wikipedia

*3:英語では synthetic division という:Synthetic division - Wikipedia(日本語エントリはない:ホーナー法 - Wikipedia

Windows conio.h の kbhit(), getch() みたいな関数を Linux/Mac gcc で作る

授業のための覚書。コンソールでキーボード入力待ち状態を作り、入力が無い間は「idle process」を表示し、ASCII*1の印字可能文字(printable characters)のキーを押すと「[*]-key was hit」を表示し、ESCキーを押すと終了するイベント駆動型のC言語プログラム。
コード作成の目標はWindowsLinux/Macで同じ呼び出しで動作すること。
参考:linux C言語:ユーザーのライブラリを作る。makefile のテンプレート、getch(),kbhit()を例にとって。 #Linux - Qiita

#include <stdio.h>
#include <stdlib.h>
char keyData = '\0';

//----------------------------------------------
// #include <conio.h>
//----------------------------------------------
// mimic kbhit(), getch() in conio.h in Windows
#include <fcntl.h>
#include <sys/ioctl.h>
#include <termios.h>
#include <unistd.h>
char kbhit(void)
{
    struct termios old, new;
    tcgetattr(STDIN_FILENO, &old);
    new = old;
    new.c_lflag &= ~(ICANON | ECHO);
    tcsetattr(STDIN_FILENO, TCSANOW, &new);
    int oldf = fcntl(STDIN_FILENO, F_GETFL, 0);
    fcntl(STDIN_FILENO, F_SETFL, oldf | O_NONBLOCK);

    int byteswaiting = 0;
    ioctl(STDIN_FILENO, FIONREAD, &byteswaiting);
    keyData = '\0';
    if ( byteswaiting > 0 ) {
        keyData = getchar();
        fflush(stdout);
        printf("\b");
    }
      
    tcsetattr(STDIN_FILENO, TCSANOW, &old);
    fcntl(STDIN_FILENO, F_SETFL, oldf);

    return keyData;
}
char getch (void)
{
    return keyData;
}
//----------------------------------------------
//#include <windows.h>
//----------------------------------------------
// mimic Sleep() in windows.h in Windows
void Sleep (int msec) {
    int usec = msec*1000;
    usleep(usec);
}
//----------------------------------------------

int execLoop = 1;

void keyEventProc(void)
{
    keyData = getch();
    if (keyData == 27) {
        printf("\rquit by [Esc]");
        fflush(stdout);
        execLoop = 0;
    } else if (32 <= keyData && keyData <= 126) {
        printf("\r[%c]-key was hit",keyData);
        fflush(stdout);
	Sleep(1000);
    }
}

void idleProc(void)
{
    printf("\ridle process   ");
    fflush(stdout);
}

int main(void)
{
    while (execLoop) {
        if (kbhit()) {
            keyEventProc();
        } else {
            idleProc();
        }
    }
    return 0;
}

kbhit()に関するChatGPT(3.5)の回答

質問

次のLinux上のC言語プログラムについて、必要なヘッダファイルと、各行の動作について教えてください

回答

このプログラムは、キーボード入力があるかどうかをチェックするための関数を実装しています。いくつかの関数や構造体が使用されていますので、必要なヘッダファイルと各行の動作を解説しますね。

必要なヘッダファイル:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <termios.h>
#include <fcntl.h>
#include <sys/ioctl.h>

それぞれの行の動作を解説します:

1. `struct termios old, new;`
- `struct termios`は、端末の属性を表す構造体です。`old`と`new`という名前の2つの変数を宣言しています。

2. `tcgetattr(STDIN_FILENO, &old);`
- 現在の端末属性を取得して、`old`構造体に格納しています。

3. `new = old;`
- 現在の端末属性をコピーして、`new`構造体にセットしています。

4. `new.c_lflag &= ~(ICANON | ECHO);`
- `new`構造体の `c_lflag` メンバーから `ICANON`(canonical モード)と `ECHO`(エコーバック)のビットをクリアしています。これにより、通常の入力モードやエコーバックが無効化されます。

5. `tcsetattr(STDIN_FILENO, TCSANOW, &new);`
- `tcsetattr()` 関数を使用して、変更した端末属性を適用します。

6. `int oldf = fcntl(STDIN_FILENO, F_GETFL, 0);`
- ファイルディスクリプタ `STDIN_FILENO`(標準入力)のフラグを取得して、`oldf` 変数に保存します。

7. `fcntl(STDIN_FILENO, F_SETFL, oldf | O_NONBLOCK);`
- `O_NONBLOCK` フラグをセットすることで、入力がなくてもプログラムがブロックされないようにします。

8. `int byteswaiting = 0;`
- 待機しているバイト数を格納する変数を宣言しています。

9. `ioctl(STDIN_FILENO, FIONREAD, &byteswaiting);`
- `FIONREAD` オプションを使用して、待機しているバイト数を取得します。

10. `keyData = '\0';`
- キーデータを格納する変数 `keyData` を初期化しています。

11. `if ( byteswaiting > 0 ) { ... }`
- 待機しているバイト数が0より大きい場合、キー入力があると判断しています。

12. `keyData = getchar();`
- `getchar()` を使用してキー入力を取得し、`keyData` に格納します。

13. `fflush(stdout);`
- 標準出力をフラッシュしています。

14. `printf("\b");`
- バックスペース文字を出力しています。この行は、キーボード入力を読み込んだ後、カーソルを1つ戻しています。

15. `tcsetattr(STDIN_FILENO, TCSANOW, &old);`
- プログラムの最後で、元の端末属性に戻しています。

16. `fcntl(STDIN_FILENO, F_SETFL, oldf);`
- 元のファイルフラグに戻します。

17. `return keyData;`
- キー入力データを返します。

この関数は、非同期でキーボード入力を監視し、キー入力がある場合はそのキーを返します。しかし、この関数は1回の呼び出しで1文字しか処理しません。