楕円体の表面積(1)

 楕円体の表面積の計算について、まず最初に、一般的な分割による積分計算をしてみました。
(後で、楕円体の第1、2種不完全積分による方法のプログラムを検討します)
分割による方法であれば、計算精度は低いですが、プログラムのミスは少ないので、次のプログラムでの値が正しいかどうかの確認にも使用できます。


 回転楕円体の表面積は、単純な計算で求める事が出来ますが、楕円体の表面積は、楕円体積分を行う必要があります。
此処でのプログラムは、一般的な分割による積分法です。
分割数を多くするほど正確な値となります。
少しでも精度を上げるため、左図のグラフの様に、分割の中間点の値で分割部分の面積を求めています。
 ルジャンドルの標準型だと、Sin2Ψの計算があり、計算が遅くなるので、ヤコービの標準型で、まずは、プログラムを作成してみました。
分割数が1000だと、有効桁数三桁、100000だと有効桁数五桁ぐらいで、分割数10N のNの値程度かそれより多い有効桁数となるようです。
10000000の分割でも、今のPCは1秒程度で計算できます。
 Doubleの演算精度だと、10000000分割程度が、精度上の限界の様です。
実際に計算をしてみると、

 半径 a = 3
 半径 b = 2
 半径 c = 1
 表面積 = 48.8821463025811
   (正解     48.882146302582)

可成り正解に近い値となつています。

実用上は、104程度の分割数で十分かと思います。

 表面積 = 48.8821460826706

104であると、計算は一瞬で終了します。
黒字の部分が正解とあっている部分ですが、小数点以下6桁まであっているので実用上問題ないと思われます。




 計算上、a>b>c の必要があり、演算エラーを防止する為、入力された値を大きい順にソートしてから計算しています。
a=b=cの時は球となります。

近似計算の場合、簡単に計算出来ますが、精度は、三桁程度です。
この場合は球(a=b=c)の計算もできます、球は正しい答えが得られます。

プログラム

unit Main;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Vcl.ExtCtrls, math;

type
  TForm1 = class(TForm)
    LabeledEdit1: TLabeledEdit;
    LabeledEdit2: TLabeledEdit;
    LabeledEdit3: TLabeledEdit;
    Button1: TButton;
    Button2: TButton;
    Memo1: TMemo;
    Button3: TButton;
    LabeledEdit4: TLabeledEdit;
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure Button3Click(Sender: TObject);
  private
    { Private 宣言 }
    function datain: boolean;
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

var
  a, b, c     : double;

// スワップ
procedure maxswap(var a, b: double);
var
  tmp: double;
begin
  if a < b then begin
    tmp := a;
    a := b;
    b := tmp;
  end;
end;

// データー入力処理
function TForm1.datain: boolean;
var
  ch , I        : integer;
  din: array[0..2] of double;
begin
  result := False;
  val(LabeledEdit1.text, din[0], ch);
  if ch <> 0 then begin
    application.MessageBox('半径aに間違いがあります。','注意', 0);
    exit;
  end;
  val(LabeledEdit2.text, din[1], ch);
  if ch <> 0 then begin
    application.MessageBox('半径bに間違いがあります。','注意', 0);
    exit;
  end;
  val(LabeledEdit3.text, din[2], ch);
  if ch <> 0 then begin
    application.MessageBox('半径cに間違いがあります。','注意', 0);
    exit;
  end;
  if din[0] <= 0 then begin
    application.MessageBox('半径aに間違いがあります。','注意', 0);
    exit;
  end;
  if din[1] <= 0 then begin
    application.MessageBox('半径bに間違いがあります。','注意', 0);
    exit;
  end;
  if din[2] <= 0 then begin
    application.MessageBox('半径cに間違いがあります。','注意', 0);
    exit;
  end;
  for I := 0 to 2 do
    for ch := 0 to 1 do
      maxswap(din[ch], din[ch + 1]);
  a := din[0];
  b := din[1];
  c := din[2];
  memo1.Clear;
  memo1.Lines.Append('楕円体の値');
  memo1.Lines.Append('半径は大きい順にソートされています。');
  memo1.Lines.Append(' 半径 a = ' + floatTostr(a));
  memo1.Lines.Append(' 半径 b = ' + floatTostr(b));
  memo1.Lines.Append(' 半径 c = ' + floatTostr(c));
  result := True;
end;

// 初期設定
procedure TForm1.FormCreate(Sender: TObject);
begin
  top := (screen.Height - height) div 2;
  left := (screen.Width - width) div 2;
  memo1.Clear;
end;

// 体積計算
procedure TForm1.Button1Click(Sender: TObject);
var
  v : double;
begin
  if not datain then exit;
  v := 4 * pi * a * b * c / 3;
  memo1.Lines.Append(' 体積 = ' + floatTostr(v));
end;

// 表面積計算 ヤコービの標準形使用
procedure TForm1.Button2Click(Sender: TObject);
var
  kim             : integer;    // kim = 10000000;    分割数 doubleでの限界値 桁落ち限界
  I               : integer;
  x, k2           : double;
  dt, dts2, t, t2 : double;
  omt2, omtk2t2   : double;
  s               : double;
  Fxk, Exk        : double;
begin
  if not datain then exit;
  val(LabeledEdit4.text, kim, I);
  if I <> 0 then begin
    application.MessageBox('分割数に間違いがあります。','注意', 0);
    exit;
  end;
  if (a = b) and (b = c) then begin
    application.MessageBox('球の計算は出来ません。','注意', 0);
    exit;
  end;
  x := sqrt(1 - c * c / a / a);
  k2 := (1 - c * c / b / b) / (1 - c * c / a / a);
  dt := x / kim;                            // Δt
  dts2 := dt / 2;                           // 分割中間点
  Fxk := 0;
  Exk := 0;
  for I := 1 to kim do begin
    t := I * dt - dts2;                     // 中間点
    t2 := t * t;
    omt2 := 1 - t2;
    omtk2t2 := 1 - k2 * t2;
    Fxk := Fxk + 1 / sqrt(omt2 * omtk2t2);  // F(x, k)
    Exk := Exk + sqrt(omtk2t2 / omt2);      // E(x, k)
  end;
  Fxk := Fxk * dt;                          // F(x, k)dt
  Exk := Exk * dt;                          // E(x, k)dt
  s := c * c + a * b * x * Exk + b * c * c / a / x * Fxk;
  s := s * 2 * pi;
  memo1.Lines.Append(' 表面積 = ' + floatTostr(s));
end;

// 表面積近似値計算
procedure TForm1.Button3Click(Sender: TObject);
const
  P = 1.6075;
var
  s   : double;
  ip  : double;
begin
  if not datain then exit;
  ip := 1 / P;
  s :=   power(a, P) * power(b, P)
       + power(a, P) * power(c, P)
       + power(b, P) * power(c, P);
  s := s / 3;
  s := power(s, ip);
  s := s * 4 * pi;
  memo1.Lines.Append(' 表面積近似 = ' + floatTostr(s));
end;

end.

download ellipsoidA.zip

各種プログラム計算例に戻る