ゆるゆるプログラミング

・三角関数 計算方法

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

■関連コンテンツ

単位ベクトル 単位ベクトルとは
三角関数 sin sinの意味と、Math.sinの使い方について解説
三角関数 cos cosの意味と、Math.cosの使い方について解説
三角関数 tan tanの意味と、Math.tanの使い方について解説
数学関数について 数学関数について解説

■新着情報

2019.09.13 長さの単位変換 1マイル、1フィートは何m?
2019.09.06 クイックソート 高速に配列に並び替える方法
2019.09.05 中央値(メディアン) 配列に格納されている値の中央値を求める
2019.09.05 最頻値 配列から出現回数が最も多い値の取得
2019.09.03 配列値の反転 配列の反転処理
2019.08.05 トランプの操作 トランプを操作するクラス

■広告

法人向けのETC専用カード

~約8,000名の受講生と80社以上の導入実績~ 企業向けプログラミング研修ならCodeCamp

日本最大級ショッピングサイト!お買い物なら楽天市場

 

 

 

 

 

 

 

 

 

Topへ