質問するログイン新規登録
C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

Q&A

解決済

2回答

578閲覧

C++ オイラー法 わからない

aiueydyheyheyhe

総合スコア1

C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

0グッド

1クリップ

投稿2025/08/03 15:15

0

1

実現したいこと

以下のプログラムを稼働させたいです。

発生している問題・分からないこと

自分はプログラミング初心者で、あまり理解していない部分が多いです。
今回の場合、returnがどのようなものなのかわからず、たぶん意味の分からない使い方をしていると思います。
有識者の方、どのようにしたらよいか教えていただけないでしょうか。

エラーメッセージ

error

1Main.cpp:25:17: warning: unused variable 'x_0' [-Wunused-variable] 2 double x_0 = 0.0;//xの初期値 3 ^ 4Main.cpp:59:52: error: called object type 'double' is not a function or function pointer 5 result_x = (-v_x/sqrt(v_x*v_x+v_y*v_y))*ρ(v_x*v_x+v_y*v_y)*S*Cd/(2*M) ; 6 ~^ 7Main.cpp:82:52: error: called object type 'double' is not a function or function pointer 8 result_y = (-v_y/sqrt(v_x*v_x+v_y*v_y))*ρ(v_x*v_x+v_y*v_y)*S*Cd/(2*M)-g; 9 ~^ 101 warning and 2 errors generated.

該当のソースコード

C++

1#include <stdio.h> 2 3#include <math.h> 4 5#define M_PI 3.14159265358979323846 6 7double gfunc1(double,double,double,double,double); 8 9double gfunc2(double,double,double,double,double); 10 11double euler2( double, double, double, double, double,double); 12 13 14 15int main(void){ 16 17 double h = 0.01;//オイラー法において設定する刻み幅 18 19 double t_0 = 0.0; //t の初期値 20 21 double v_x_0 = 55*cos((1/2)*M_PI); //Vxの初期値 22 23 double v_y_0 = 55*sin((1/2)*M_PI);//Vyの初期値 24 25 double x_0 = 0.0;//xの初期値 26 27 double y_0 = 0.05; //y の初期値 28 29 double t_n = 2.0; //計算終了時間 30 31 32 33 printf( " result_x = %f [m] \n" , euler2( y_0 , v_x_0 , v_y_0, t_0 , t_n , h ) ); 34 35 printf( " result_y = %f [m] \n" , euler2( y_0 , v_x_0 ,v_y_0, t_0 , t_n , h ) ); 36 37 return 0; 38 39} 40 41 42 43double gfunc1( double t, double x , double v_x ,double v_y ){ 44 45 46 47 double ρ=1.184; 48 49 double M=0.046; 50 51 double S=0.00145; 52 53 double Cd=0.35; 54 55 double g=9.8066; 56 57 double result_x; 58 59 result_x = (-v_x/sqrt(v_x*v_x+v_y*v_y))*ρ(v_x*v_x+v_y*v_y)*S*Cd/(2*M) ; 60 61 return(result_x); 62 63} 64 65double gfunc2( double t, double x , double v_x ,double v_y ){ 66 67 68 69 double ρ=1.184; 70 71 double M=0.046; 72 73 double S=0.00145; 74 75 double Cd=0.35; 76 77 double g=9.8066; 78 79 80 double result_y; 81 82 result_y = (-v_y/sqrt(v_x*v_x+v_y*v_y))*ρ(v_x*v_x+v_y*v_y)*S*Cd/(2*M)-g; 83 84 return(result_y); 85 86} 87 88 89 90double euler2( double x_0 , double y_0 , double v_x_0, double v_y_0 , double t_0 , double t_n , double h ){ 91 92 // きざみ幅 h で t_0 での値 y_0,x_0 から t_n まで計算を行い、 y の値を返す関数 93 94 95 96 double t = t_0;//tの初期値を代入する 97 98 double x = y_0;//xの初期値を代入する 99 100 double y = y_0;//yの初期値を代入する 101 102 double v_x = v_x_0; 103 104 double v_y = v_y_0; 105 106 107 double t_new ;//h 経過後の tの値を保持する 108 109 double x_new ;//h 経過後の xの値を保持する 110 111 double y_new ;//h 経過後の yの値を保持する 112 113 double v_x_new ; 114 115 double v_y_new ; 116 117 118 119 int i = 0; //ループの回数カウントアップ 120 121 122 123 while( t < t_n ){//tiに対応する変数 t が t_n を超えるまで計算を繰り返す 124 125 126 127 if( i % 10 == 0 ) 128 129 printf("%f,%f,%f,%f,%f \n" , t , x , y , v_x , v_y ); 130 131 132 133 134 t_new = t + h; 135 136 v_x_new = v_x + h * gfunc1( t , x , y , v_x, v_y ) ; 137 138 v_y_new = v_y + h * gfunc2( t , x , y , v_x, v_y ) ; 139 140 x_new = x + h * v_x; 141 142 y_new = y + h * v_y; 143 144 145 146 147 t = t_new; 148 149 v_x = v_x_new; 150 151 v_y = v_y_new; 152 153 // 次のループで用いることができるよう、値を入れ替える 154 155 i++; 156 157 } 158 159 160 161 return( x ); 162 return( y ); 163 164} 165 166 167

試したこと・調べたこと

  • teratailやGoogle等で検索した
  • ソースコードを自分なりに変更した
  • 知人に聞いた
  • その他
上記の詳細・結果

エラーをコピーして調べたが、よくわからなかった

補足

特になし

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

jimbe

2025/08/03 15:52

問題がなければ、なぜこのプログラムを作っているのかを教えていただけますか? 独学/webで c++ を学ばれていて何処かのサイトで見つけた問題たとか、学校/会社から出された課題の穴埋め問題だとか…、
txty

2025/08/08 04:40 編集

return(x); return(y); とは。一つしか返せないのだから構造体にするとかポインタを引数につかってコピーでなく実体いじればいんじゃないのですかreturnは作業をやめたいとき、関数から外部に取り出したいとき使います。classのメンバにしてもいいのか。
guest

回答2

0

ベストアンサー

最初のwarningは、x_0が、その後で使用されていないという警告です。
とりあえず、そのままでもよいです。
最終的に使用するか、削除するか自分で決めてください。

残りの2個のエラーは、変数ρの後ろに演算子が無いので、関数として解釈されているというエラーです。
何らかの演算子を入れるか、他の関数に置き換えるかは、数学の問題なので自分で考えてください。

投稿2025/08/03 15:58

hiroki-o

総合スコア1528

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

これ、あれだろ。
「空気抵抗がある場合の」斜方投射?を意図してんじゃないか、と。
ρが流体密度、Cdが効力係数、Sが物体の代表面積として、

微分方程式

と言う微分方程式を数値積分(オイラー法)を使って解け、って題意なんじゃないか、って思う。
(チッ、TeratailにはMarkdownはあっても、TeX記法は受け付けないんか・・・)
ホントはその辺、質問文に書いて欲しかったんだが・・・・・・。

んでC++だろ?
僕はC++は専門じゃないんだけど、恐らくC++だとこんな書き方になるんじゃないか、って思う。

C++

1#include <iostream> 2#include <vector> 3#include <tuple> 4#include <cmath> 5#include <iomanip> 6#include <numeric> // accumulate 7#include <algorithm> // reverse 8#include <utility> // pair 9 10using Vec2 = std::pair<double, double>; 11using Record = std::tuple<double, Vec2, Vec2>; // (t, r, v) 12 13constexpr double rho = 1.184; 14constexpr double Cd = 0.35; 15constexpr double S = 0.00145; 16constexpr double M = 0.046; 17constexpr double g = 9.8066; 18constexpr double h = 0.01; 19constexpr double t_0 = 0.0; 20constexpr double t_n = 2.0; 21 22std::vector<double> arange(double start, double end, double step) { 23 std::vector<double> result; 24 for (double v = start; v < end + 1e-10; v += step) 25 result.push_back(v); 26 return result; 27} 28 29std::vector<Record> foo(const std::vector<Record>& acc, double s) { 30 const auto& [t, r, v] = acc.front(); // 先頭が最新状態 31 double coeff = - rho * Cd * S * std::sqrt(v.first * v.first + v.second * v.second) / (2 * M); 32 Vec2 r_new = { 33 r.first + h * v.first, 34 r.second + h * v.second 35 }; 36 Vec2 v_new = { 37 v.first + h * coeff * v.first, 38 v.second + h * (coeff * v.second - g) 39 }; 40 std::vector<Record> result = { std::make_tuple(s, r_new, v_new) }; 41 result.insert(result.end(), acc.begin(), acc.end()); // prepend 42 return result; 43} 44 45int main() { 46 Vec2 r_0 = {0.0, 0.05}; 47 Vec2 v_0 = { 48 55.0 * std::cos(0.5 * M_PI), 49 55.0 * std::sin(0.5 * M_PI) 50 }; 51 52 std::vector<Record> initial = { std::make_tuple(t_0, r_0, v_0) }; 53 std::vector<double> steps = arange(t_0 + h, t_n, h); 54 55 std::vector<Record> data = std::accumulate( 56 steps.begin(), steps.end(), initial, 57 [](const std::vector<Record>& acc, double s) { 58 return foo(acc, s); 59 } 60 ); 61 62 std::cout << std::fixed << std::setprecision(10); 63 std::reverse(data.begin(), data.end()); 64 for (const auto& [t, r, v] : data) { 65 if (std::fmod(t / 0.01, 10) < 1e-9) { // t が 0.1, 0.2, 0.3 ... のときだけ出力 66 std::cout << t << ", " << r.first << ", " << r.second 67 << ", " << v.first << ", " << v.second << '\n'; 68 } 69 } 70 71 const auto& [tf, rf, vf] = data.back(); 72 std::cout << " result_x = " << rf.first << " [m]\n"; 73 std::cout << " result_y = " << rf.second << " [m]\n"; 74 75 return 0; 76} 77

「CとC++は似たようなモノ」って誤解があるんだけど、C++にはとても強力なSTLってライブラリが標準添付されていて、それを使ったプログラミングが望ましいスタイルとなるんとちゃうんか。よう知らんけど。
少なくとも知ってる限りで言うと、「C++のエキスパート」はSTLのファンなんだ。だからまずはSTLを使おうとするんじゃないか。異論はあるかもしれんが、ホントその辺はよう知らんのだ。かなり「尖った」C++ファンしか見たことないんで(笑)。
(テンプレートを使ったプログラミングが・・・!!!とか力説されたし、ネットでの某動画の有名なC++な人もそう言ってた気がする)

んで、オイラー法に入る前にまずはプログラミングの基礎的な考え方を。
これは斜方投射なんで二次元空間に於ける記述となる。
こう言った場合、例えばx座標とy座標を「バラバラに扱う」とか言うのってあんま良いテじゃないんだ。
座標情報は座標情報として纏める。
同様に速度も二次元ベクトルなんで、やっぱ要素を「纏める」ってのがセオリーとなるのとちゃうんかな。
ここでは

C++

1using Vec2 = std::pair<double, double>; 2

として二次元ベクトル、Vec2型、ってのを定義している。
厳密に言うと、Vec2型をstd::pair<double, double>へのエイリアスとして用いる、と言う事だ。
これによりr = (x, y)とかv = (vx, vy)みたいな、より物理学らしい表現が可能となる。

もう一つはRecord型の定義だ。

C++

1using Record = std::tuple<double, Vec2, Vec2>; // (t, r, v) 2

これはstd::tupleへのエイリアスで、含む型はdouble、残りの2つはVec2型と言う前提となっている。
これは貴方のオリジナルのコードだと(時間情報, 位置情報, 速度情報)、と纏めて扱いたかったのかな、ってぇんでこうなってる。コメントに記述された(t, r, v)はそれを意図してる。
そして纏めて扱うってのはモダンなプログラミングに於いては重要な概念なんだ。
もちろん、プログラムをする人の中では、とかく「バラバラにする」とか「分解する」のが好きな人が多い。
多いんだけど、正直言って、そのテの発想は古いプログラミングの考え方なんだよ。
繰り返すけど、多々の情報を纏めて扱うってのはモダンなプログラミングでは重要な概念で、STLを備えたC++はその「モダンなプログラミング」に片足突っ込んでいて、その辺実は古典的なCとは全く違う思想を持っている。
この後それを知ることとなる。

次はユーティリティarangeだ。

C++

1std::vector<double> arange(double start, double end, double step) { 2 std::vector<double> result; 3 for (double v = start; v < end + 1e-10; v += step) 4 result.push_back(v); 5 return result; 6} 7

これは単純には、この問題上、「時間列」を作るためのユーティリティだ。
貴方の書いたオリジナルのコードでは、恐らく、0〜2秒間を0.01秒刻みで分割したい、んだろう。
言い換えると、0から0.01毎に増えて2に到達する長さ200の数列が欲しい、って事だ。
このユーティリティはそれを満たすstd::vectorの数列を返す関数となる。
なお、ここで言うstd::vectorは上で定義されたVec2型と同じではなく、プログラミング用語では可変長配列を意味してる。

さて、オイラー法に付いて考えていこう。
とは言っても、基本的には、STL上では「考え方」自体は簡単だ。
まずは位置情報rから。
オイラー法では、この問題だと、位置情報rは次の漸化式によって記述される。

オイラー法(r)

ここで、hの単位は実質的には時間(秒)なんで、次元の問題はクリアしてる。
そして速度情報v

オイラー法(v)

と言う漸化式になっている。
数学的に言うとこの漸化式を解け、ってな問題になるんだろうけど、現象的にはこの、「中途半端な計算過程」に見えるコレ

畳み込み

を、プログラミング用語で畳み込み(folding)と呼ぶ(LaTeXを打ち間違えて無ければいいが・苦笑)。
言い換えると、この「畳み込み」を行う機能をプログラミング言語が提供していれば、この問題は「解ける」って事になるわけだ。C++のSTLではこの「畳み込み」を行うユーティリティ、std::reducestd::accumulateが提供されているが、ここではstd::accumulateを使う。

上の「畳み込み」の理論上のモデルだと、なんだかんだ言って「最終値」を把握する事の方が簡単だ。
一方、貴方の書いたオリジナルコードだと、「計算過程」の値も全部見たいらしい。
と言う事は、「計算の過程を全て詰め込んだ情報が必要だ」と言う事になる。
このテの「計算の過程を全て詰め込んだ」データ、あるいはその変数をアキュムレータ(Accumulator: 累積値)と呼ぶ。

今、計算過程の一つが(t, r, v)、つまり「時間、距離、速度」の組だとしてみよう。そしてt_n時のアキュムレータの状態が次のようなモノだと仮定する。

数列

いま、n + 1時点の組を計算する場合、利用すべきはアキュムレータの先頭のデータになる、ってのは自明だろう。そして計算が終わった後、その値はアキュムレータの先頭に追加すべきだ。
これを行うのが関数fooの役割だ。

C++

1std::vector<Record> foo(const std::vector<Record>& acc, double s) { 2 const auto& [t, r, v] = acc.front(); // 先頭が最新状態 3 double coeff = - rho * Cd * S * std::sqrt(v.first * v.first + v.second * v.second); 4 Vec2 r_new = { 5 r.first + h * v.first, 6 r.second + h * v.second 7 }; 8 Vec2 v_new = { 9 v.first + h * coeff * v.first, 10 v.second + h * (coeff * v.second - g) 11 }; 12 std::vector<Record> result = { std::make_tuple(s, r_new, v_new) }; 13 result.insert(result.end(), acc.begin(), acc.end()); // prepend 14 return result; 15} 16

引数はRecordvector(可変長引数)のaccdouble型のsの2つ。
accの先頭からtrvの3つの情報を取り出し、そして加速度を出す為の係数(coefficient)を計算する。
そしてaccの先頭情報から位置情報rと速度情報vを計算するわけだが、実はこれは数学的な漸化式をそのまま書けばいいんで何も工夫が必要ない事が分かるだろう。

C++

1 Vec2 r_new = { 2 r.first + h * v.first, 3 r.second + h * v.second 4 }; 5 Vec2 v_new = { 6 v.first + h * coeff * v.first, 7 v.second + h * (coeff * v.second - g) 8 }; 9

あとは新規に(t, r, v)の組を作ってaccの先頭に挿し込む(prepend)してデータ列を返すだけ、だ。
その辺の記述がC++だと若干煩わしいが、「考え方」と言うか、ロジック自体は然程難しくはないだろう。むしろ、この関数だと数式部分が数式記述通りに書けるってメリットは結構デカいんじゃなかろうか。分解しないでいいんだ。

あとはmain関数内でまずは各種初期値を設定する。初期位置、初期速度を設定した後、アキュムレータを変数initialとして定義する。initialRecord型のstd::vectorだ。
そして時間列stepsを定義する。
アキュムレータと時間列を利用してdatastd::accumulateで作成する。

C++

1 std::vector<Record> data = std::accumulate( 2 steps.begin(), steps.end(), initial, 3 [](const std::vector<Record>& acc, double s) { 4 return foo(acc, s); 5 } 6 ); 7

時間列がendに到達するまで、std::accumulateは関数fooを利用しつつinitialを加工していく。最終的には時間列の長さ(この問題の場合は200)に合わせて古い順から並んだ200個の計算結果((t, r, v))が詰め込まれたシーケンスを入手する。
これでオイラー法は終了、だ。

あとは出力するだけ、となる。
おしまい。

注1: std::setprecitionではそこに書かれてる通り、「浮動小数点数を出力する精度を設定」してて、std::fixedでは、「浮動小数点数を固定小数点表記で入出力することを指示」しているだけだ。

注2: なお、オリジナルのコードでも速度vのx成分の初期値にcos(1/2 * Π)が使われてるんで、全履歴は水平方向だと位置情報は0のまんま、速度のx成分も0のまんま、となると思う。
結果、事実上このシミュレーションは鉛直方向への「投げ上げ」って事になるんじゃなかろうか。

投稿2025/08/05 18:31

編集2025/08/07 17:14
cametan

総合スコア161

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.30%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問