Javaプログラミング学習サイト ゆるゆるプログラミング

2017/08/09 公開

・三角関数 計算方法

Math.sin/Math.cos/Math.tanを使わずに、指定角度の三角関数の値を(sin,cos,tan)を計算するアルゴリズムを紹介します。

ここで紹介するアルゴリズムは、指定された角度の単位ベクトル(x,y)を求め、xをcos、yをsin、y/xをtanとするものです。指定角度の単位ベクトルの求め方は、以下のとおりです。

①計算するために与えられた角度が、どの象限に入っているか、また、座標軸上にあるかを判定します。下の図は、象限を表しているもので、角度θ、0°<θ<90°であれば第1象限、90°<θ<180°であれば第2象限、180°<θ<270°であれば第3象限、270°<θ<360°であれば第4象限に入ることを表しています。角度が0°/90°/180°/270°の場合は、座標軸上です。

象限

②その象限をに対応する座標軸の単位ベクトルを2つ決めます。その対応する2つの単位ベクトルを赤矢印で示します。(下図)

各象限に対応する単位ベクトル

下図は、指定角度60°のときに選択された2つの単位ベクトルです。

各象限に対応する単位ベクトル

③2つの単位ベクトルの先部分の座標中点(2つの座標平均)を通り、原点(0,0)を始点とする単位べクトルを求めます。同時に、求めた単位ベクトルX軸からの角度も計算します。これも計算に使用した2つの単位ベクトルの角度の平均で求められます。計算した角度が指定角度に近ければ、この単位ベクトルが求める結果でとなり、処理を終了します。

下図は、0°と90°の間の単位ベクトルを求めています。角度は(0°+90°)/2=45°です。

各象限に対応する単位ベクトル

④2つの単位ベクトルのうち1つを③で求めた単位ベクトルに変えます。単位ベクトルの選択条件は、変更後の単位ベクトル角度の範囲内に、指定角度が入ることです。

下図は、0°の単位ベクトルを45°の単位ベクトルに変更した例です。60°の単位ベクトルが2つの単位ベクトルに挟まれていることが分かります。

各象限に対応する単位ベクトル

⑤変更後の単位ベクトルで③の処理に戻ります。

このように単位ベクトルの間の角度を1/2ずつ狭めていき、指定の角度まで近づけます。

このアルゴリズムをJavaのプログラムにしたものが以下です。

このプログラムで指定する角度の単位は度(°)で、0~359.9999..の範囲を超えて指定した場合でも、自動でこの範囲内に収める処理も含めています。

TriFunction.java ← クリックしてダウンロードページに移動
001:    public class TriFunction {
002:    	// 角度から象限を求める
003:    	static int GetOrthant( double deg )
004:    	{
005:    		if ( (   0.0 < deg ) && (  90.0 > deg ) ) return 1;
006:    		if ( (  90.0 < deg ) && ( 180.0 > deg ) ) return 2;
007:    		if ( ( 180.0 < deg ) && ( 270.0 > deg ) ) return 3;
008:    		if ( ( 270.0 < deg ) && ( 360.0 > deg ) ) return 4;
009:    		return 0;
010:    	}
011:    
012:    
013:    	public static void main(String[] args) {
014:    		int    orthant;		// 角度を含む象限
015:    		int    loopnumber;	// ループ回数
016:    		double delta;		// 処理終了条件(角度の差)
017:    		// 計算に使用
018:    		double deg, rad;
019:    		double deg1, deg2, degm;
020:    		double x1, y1;
021:    		double x2, y2;
022:    		double mx, my, d;
023:    		double new_x, new_y;
024:    		// 計算結果(sin,cos,tan)
025:    		double ans_sin, ans_cos, ans_tan;
026:    
027:    		// 入力した引数が1以上かを調べる
028:    		if ( 1 > args.length ) {
029:    			// 入力した引数が1未満の場合、使用方法を表示する
030:    			System.out.println( 
031:    				"TriFunction [角度(°)]" );
032:    			return;
033:    		}
034:    
035:    		// 1番目の引数の値をdegに代入
036:    		try {
037:    			// 引数を変換し、角度degに代入
038:    			deg = Double.valueOf( args[ 0 ] );
039:    		}
040:    		catch( NumberFormatException ne )
041:    		{
042:    			System.out.println( "引数が不正です" );
043:    			return;
044:    		}
045:    
046:    		// 角度degを0.0~359.999...の範囲に直す
047:    		for ( ; ; ) {
048:    			// degが0.0未満
049:    			if ( 0.0 > deg ) {
050:    				deg += 360.0;
051:    				continue;
052:    			}
053:    			// degが360.0以上
054:    			if ( 360.0 <= deg ) {
055:    				deg -= 360.0;
056:    				continue;
057:    			}
058:    			// ループを抜ける
059:    			break;
060:    		}
061:    
062:    		// 結果の初期化
063:    		ans_sin = ans_cos = ans_tan = 0.0;
064:    		loopnumber = 0;
065:    
066:    		// 角度から象限を求める
067:    		orthant = GetOrthant( deg );
068:    		x1 = y1 = x2 = y2 = 0.0;
069:    		deg1 = deg2 = 0.0;
070:    		switch ( orthant )
071:    		{
072:    			case 1:
073:    				deg1 = 0.0;
074:    				deg2 = 90.0;
075:    				x1 =  1.0;
076:    				y1 =  0.0;
077:    				x2 =  0.0;
078:    				y2 =  1.0;
079:    				break;
080:    
081:    			case 2:
082:    				deg1 = 90.0;
083:    				deg2 = 180.0;
084:    				x1 =  0.0;
085:    				y1 =  1.0;
086:    				x2 = -1.0;
087:    				y2 =  0.0;
088:    				break;
089:    
090:    			case 3:
091:    				deg1 = 180.0;
092:    				deg2 = 270.0;
093:    				x1 = -1.0;
094:    				y1 =  0.0;
095:    				x2 =  0.0;
096:    				y2 = -1.0;
097:    				break;
098:    
099:    			case 4:
100:    				deg1 = 270.0;
101:    				deg2 = 360.0;
102:    				x1 =  0.0;
103:    				y1 = -1.0;
104:    				x2 =  1.0;
105:    				y2 =  0.0;
106:    				break;
107:    
108:    			default:
109:    				// 0°
110:    				if ( 0.0 == deg ) {
111:    					ans_sin = 0.0;
112:    					ans_cos = 1.0;
113:    					ans_tan = ans_sin / ans_cos;
114:    					break;
115:    				}
116:    				// 90°
117:    				if ( 90.0 == deg ) {
118:    					ans_sin = 1.0;
119:    					ans_cos = 0.0;
120:    					ans_tan = ans_sin / ans_cos;
121:    					break;
122:    				}
123:    				// 180°
124:    				if ( 180.0 == deg ) {
125:    					ans_sin = 0.0;
126:    					ans_cos = -1.0;
127:    					ans_tan = ans_sin / ans_cos;
128:    					break;
129:    				}
130:    				// 270°
131:    				if ( 270.0 == deg ) {
132:    					ans_sin = -1.0;
133:    					ans_cos = 0.0;
134:    					ans_tan = ans_sin / ans_cos;
135:    					break;
136:    				}
137:    
138:    				// 原因不明のエラー
139:    				System.out.println( "原因不明のエラー" );
140:    				return;
141:    		}
142:    
143:    		// 角度が0/90/180/270以外
144:    		if ( 1 <= orthant ) {
145:    			delta = 0.000000001;
146:    			// 無限ループ
147:    			for ( ; ; ) {
148:    				// ループ回数
149:    				++ loopnumber;
150:    
151:    				// 角度の中間値を計算
152:    				degm = ( deg1 + deg2 ) / 2.0;
153:    
154:    				// 座標の中間点を計算
155:    				mx = ( x1 + x2 ) / 2.0;
156:    				my = ( y1 + y2 ) / 2.0;
157:    
158:    				// 原点(0,0)と(mx,my)を結ぶ線の
159:    				// 単位ベクトル(new_x, new_y)を計算
160:    				d = Math.sqrt( mx * mx + my * my );
161:    				new_x = mx / d;
162:    				new_y = my / d;
163:    
164:    				// 角度の差がdelta未満で終了
165:    				if ( Math.abs( degm - deg ) < delta ) {
166:    					ans_sin = new_y;
167:    					ans_cos = new_x;
168:    					ans_tan = ans_sin / ans_cos;
169:    					break;
170:    				}
171:    
172:    				// 角度を狭める
173:    				if ( deg > degm ) {
174:    					x1 = new_x;
175:    					y1 = new_y;
176:    					deg1 = degm;
177:    				}
178:    				else {
179:    					x2 = new_x;
180:    					y2 = new_y;
181:    					deg2 = degm;
182:    				}
183:    			}
184:    		}
185:    
186:    		// 結果の表示
187:    		System.out.println( "■計算結果" );
188:    		System.out.println( "sin(" + deg + ")=" + ans_sin );
189:    		System.out.println( "cos(" + deg + ")=" + ans_cos );
190:    		System.out.println( "tan(" + deg + ")=" + ans_tan );
191:    		System.out.println( "ループ回数=" + loopnumber );
192:    
193:    		System.out.println();
194:    
195:    		System.out.println( "■Mathクラスでの計算結果" );
196:    		rad = Math.toRadians( deg );
197:    		System.out.println( "sin(" + deg + ")=" + Math.sin( rad ) );
198:    		System.out.println( "cos(" + deg + ")=" + Math.cos( rad ) );
199:    		System.out.println( "tan(" + deg + ")=" + Math.tan( rad ) );
200:    	}
201:    }

TriFunctionを実行

C:\talavax\javasample>java TriFunction 60.0

60°のsin、cos、tanを求めます。

TriFunction.javaの出力結果

■計算結果
sin(60.0)=0.8660254037768191
cos(60.0)=0.5000000000131974
tan(60.0)=1.732050807507921
ループ回数=35

■Mathクラスでの計算結果
sin(60.0)=0.8660254037844386
cos(60.0)=0.5000000000000001
tan(60.0)=1.7320508075688767

■関連コンテンツ

数学関数について 数学関数について解説
中点 中点(ちゅうてん)について解説
単位ベクトルとは-画像

単位ベクトル

単位ベクトルは長さが1のベクトルのことです。任意の長さのベクトルを単位ベクトルに変換する方法も解説しています。

半径1の円-画像

単位円

単位円は半径が1の円のことです。単位円と三角関数(sinθ、cosθ)の関係についても解説しています。

sinの意味と、Math.sinの使い方について解説-画像

三角関数 sin

sinの意味と、Math.sinメソッドの使い方をソースコードを使って解説しています。

cosの意味と、Math.cosの使い方について解説-画像

三角関数 cos

cosの意味と、Math.cosメソッドの使い方をソースコードを使って解説しています。

tanの意味と、Math.tanの使い方について解説-画像

三角関数 tan

tanの意味と、Math.tanメソッドの使い方をソースコードを使って解説しています。

π(パイ)の意味と、Math.PIの使い方について解説-画像

円周率π(パイ)

π(パイ)の意味と、Math.PIの使い方について解説

四則演算(足し算/引き算/掛け算/割り算)について-画像

計算結果の表示

足し算(加法)/引き算(減法)/掛け算(乗法)/割り算(除法)の使い方を説明

■新着情報

2022.07.07 外部プログラムの実行 exeファイル実行
2022.07.06 完全数 6=1+2+3

■広告

 

 

 

 

 

スッキリわかるJava入門第3版 [ 中山清喬 ]

価格:2,860円
(2021/6/18 14:32時点)
感想(6件)

 

 

 

 

Topへ