5
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

今回の内容は実行時に少し速度が速くなる小ネタ程度の内容です。
C++のコードを交えますが、考え方はどの言語でも使えるものとなります。

ベクトルの大きさを変更するには?

ベクトルの大きさのみを変更したい場合、すべての要素に同じ値を乗算か除算するScaleという処理を使用することになります。
本記事では三次元ベクトルで統一して考えてみたいと思います。

n倍のScaleの式

\begin{pmatrix}x' & y' & z' \end{pmatrix}
=
n\begin{pmatrix}x & y & z \end{pmatrix}

1/nのScaleの式

\begin{pmatrix}x' & y' & z' \end{pmatrix}
=
\begin{pmatrix}x & y & z \end{pmatrix} / n

コードで書いてみたいと思います。

n倍のScaleのコード

ScaleMul.cpp
#include <iostream>
#include <vector>
//メイン関数
int main() {
	std::vector<float> vec = { 1.3f, 2.3f, 3.3f };
	// ベクトルの要素を出力
	std::cout << "befor:";
	for (auto it = vec.begin(); it != vec.end(); ++it) {
		std::cout << *it << " ";
	}
	std::cout << std::endl;

	//ベクトルの大きさを変更、今回は2倍する。
	for (auto it = vec.begin(); it != vec.end(); ++it) {
		(*it) *= 2;
	}

	// ベクトルの要素を出力
	std::cout << "after:";
	for (auto it = vec.begin(); it != vec.end(); ++it) {
		std::cout << *it << " ";
	}
	std::cout << std::endl;

	system("pause");
	return 0;
}
result
befor:1.3 2.3 3.3
after:2.6 4.6 6.6
続行するには何かキーを押してください . . .

1/nのScaleのコード

ScaleDiv.cpp
#include <iostream>
#include <vector>
//メイン関数
int main() {
	std::vector<float> vec = { 1.3f, 2.3f, 3.3f };
	// ベクトルの要素を出力
	std::cout << "befor:";
	for (auto it = vec.begin(); it != vec.end(); ++it) {
		std::cout << *it << " ";
	}
	std::cout << std::endl;

	//ベクトルの大きさを変更、今回は2で割る。
	for (auto it = vec.begin(); it != vec.end(); ++it) {
		(*it) /= 2;
	}

	// ベクトルの要素を出力
	std::cout << "after:";
	for (auto it = vec.begin(); it != vec.end(); ++it) {
		std::cout << *it << " ";
	}
	std::cout << std::endl;

	system("pause");
	return 0;
}
result
befor:1.3 2.3 3.3
after:0.65 1.15 1.65
続行するには何かキーを押してください . . .

どう最適化する?

実はコンピュータは除算が苦手で、四則演算の中で最も遅いです。
有識者の記事を参考にしますと、乗算が4クロックなのに対し、除算は最大で97クロック、速度に20倍近くの違いがあることになります。

参考記事↓

人間の感覚からするとほとんど誤差がないですが、これが3Dゲームなどで毎フレーム何回も行う処理となると、やはり最適化したくなりますよね。

逆数を使って除算の回数を減らそう

除算の結果と、逆数を乗算したものの結果は同じであったはずですよね。

除算

a/b = \frac{a}{b}

逆数の乗算

a\frac{1}{b} = \frac{a}{b}

逆数は、1nで除算すれば求められましたよね。
早速書いてみましょう。

ScaleDiv.cpp
#include <iostream>
#include <vector>
//メイン関数
int main() {
	std::vector<float> vec = { 1.3f, 2.3f, 3.3f };
	// ベクトルの要素を出力
	std::cout << "befor:";
	for (auto it = vec.begin(); it != vec.end(); ++it) {
		std::cout << *it << " ";
	}
	std::cout << std::endl;

	//ベクトルの大きさを変更、今回は2分の1倍にする。
	float scaler = 1.0f / 2.0f;
	for (auto it = vec.begin(); it != vec.end(); ++it) {
		(*it) *= scaler;
	}

	// ベクトルの要素を出力
	std::cout << "after:";
	for (auto it = vec.begin(); it != vec.end(); ++it) {
		std::cout << *it << " ";
	}
	std::cout << std::endl;

	system("pause");
	return 0;
}
result
befor:1.3 2.3 3.3
after:0.65 1.15 1.65
続行するには何かキーを押してください . . .

除算でScaleしたのと同じ結果ですが、除算の回数を3回から1回に減らせました。
演算の回数は多くなっていますが、除算が乗算の約20倍遅いことを考えると、毎フレーム何度もやる処理であった場合、処理を最適化できたといえるでしょう。

総括

  • 除算は速度が遅い
  • VectorのScaleのように、すべての要素を同じスカラーで除算する場合、逆数を求めてからそれで乗算を行った方が処理の速度が速くなる
5
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?