プログラムの時間短縮のアドバイスについて

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
toxicyellow
記事: 21
登録日時: 12年前

プログラムの時間短縮のアドバイスについて

#1

投稿記事 by toxicyellow » 11年前

いつもお世話になってます。
今日は自分が書いたプログラムの時間短縮についてアドバイスを頂けたらと思います。
非常に高い精度で複利計算の利率を求める方法は無いでしょうか?
以下がコードです。(case 'h'の実行文でr=r+0.0000......1としています。この部分をもっとスマートに出来ないものかと…)

コード:

#include<stdio.h>

double retu(double i,double j,double a,double b){
        if(i>2){
                return(retu(i-1,j,a,b)*(1+j)+b);
                }
        else{
                return(a);
                }
        }


int main(){
        double motode,TA,span,r,mokuhyou,meseta=0;
        char s[2];

        printf("日にちを求めるにはhを、利率を求めるにはrを入力してください:");

        while(scanf("%1s",s)!=EOF){

                switch(s[0]){
                case 'h':
                                span=1;

                                printf("元手のメセタを入力してください:");
                                scanf("%lf",&motode);
                                printf("一日のTAの収入を入力してください:");
                                scanf("%lf",&TA);
                                printf("目標金額を入力してください:");
                                scanf("%lf",&mokuhyou);
                                printf("利率を入力してください:");
                                scanf("%lf",&r);

                                if(motode>=mokuhyou){
                                        printf("error\n");
                                        return 0;
                                        }

                                while(meseta<mokuhyou){
                                        meseta=retu(span,r,motode,TA);
                                        span++;
                                        }

                                printf("目標金額到達には%lf日かかります。\n",span);
                                return 0;


                case 'r':
                                r=0;

                                printf("元手のメセタを入力してください:");
                                scanf("%lf",&motode);
                                printf("一日のTAの収入を入力してください:");
                                scanf("%lf",&TA);
                                printf("目標金額を入力してください:");
                                scanf("%lf",&mokuhyou);
                                printf("スパンを入力してください:");
                                scanf("%lf",&span);

                                while(meseta<mokuhyou){
                                        meseta=retu(span,r,motode,TA);
                                        r=r+0.0000000001;
                                        if(meseta==mokuhyou){
                                                break;
                                                }
                                        }

                                printf("必要最低限な利率は%.8lfです。\n",r);
                                printf("meseta=%.8lf\n",meseta);
                                return 0;
                        }
                        printf("日にちを求めるにはhを、利率を求めるにはrを入力してください:");

                }
        }


アバター
みけCAT
記事: 6734
登録日時: 14年前
住所: 千葉県
連絡を取る:

Re: プログラムの時間短縮のアドバイスについて

#2

投稿記事 by みけCAT » 11年前

二分探索が使えるかもしれません。
retu関数の各引数の意味を教えてもらえますか?
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

toxicyellow
記事: 21
登録日時: 12年前

Re: プログラムの時間短縮のアドバイスについて

#3

投稿記事 by toxicyellow » 11年前

関数retu(i,j,a,b)は、数学的な式、α(i+1)=α(i)x(1+j)+b、この式を表しています。
iは添字で、jは利率、aとbは定数です(retu(1)=a)。

アバター
みけCAT
記事: 6734
登録日時: 14年前
住所: 千葉県
連絡を取る:

Re: プログラムの時間短縮のアドバイスについて

#4

投稿記事 by みけCAT » 11年前

想定する入力について教えてください。
・「元手のメセタ」(共通)は負になることはありますか?
・「一日のTAの収入」(共通)は負になることはありますか?
・「スパン」(最初の入力がrのとき)は正の整数以外になることはありますか?
ここで、「ありますか」は「有効な入力ですか」という意味です。
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

toxicyellow
記事: 21
登録日時: 12年前

Re: プログラムの時間短縮のアドバイスについて

#5

投稿記事 by toxicyellow » 11年前

みけCATさん、
元手はマイナスの場合も想定しています。TAの収入については値によっては永遠に目標額に到達しない場合もあるため、正の入力を想定しています。
スパンは常に正を想定しています。

アバター
みけCAT
記事: 6734
登録日時: 14年前
住所: 千葉県
連絡を取る:

Re: プログラムの時間短縮のアドバイスについて

#6

投稿記事 by みけCAT » 11年前

聞き忘れました。利率は負になることはありますか?
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

アバター
みけCAT
記事: 6734
登録日時: 14年前
住所: 千葉県
連絡を取る:

Re: プログラムの時間短縮のアドバイスについて

#7

投稿記事 by みけCAT » 11年前

今のプログラムは、
・日にちを求める
・元手のメセタ:10
・一日のTAの収入:1
・目標金額:12
・利率:0.1
という入力を与えると、出力は「目標金額到達には4.000000日かかります。」となります。これは正しい仕様ですか?
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

toxicyellow
記事: 21
登録日時: 12年前

Re: プログラムの時間短縮のアドバイスについて

#8

投稿記事 by toxicyellow » 11年前

利率も目標額に達しない可能性があるので、常に正を想定しています。
入出力の例はあってます。

利率の場合は以下の様になります

元手のメセタを入力してください:5000000
一日のTAの収入を入力してください:500000
目標金額を入力してください:1000000000
スパンを入力してください:60
必要最低限な利率は0.08063759です。
meseta=1000000027.78787041

しかし入力をある程度小さな値でやると利率が上手く表示されないこともあります

元手のメセタを入力してください:100
一日のTAの収入を入力してください:10
目標金額を入力してください:200
スパンを入力してください:12
必要最低限な利率は0.00000000です。
meseta=200.00000000



そうですよね…確かに格入力の正負の場合も想定しないといけませんよね。ありがとうございます。

toxicyellow
記事: 21
登録日時: 12年前

Re: プログラムの時間短縮のアドバイスについて

#9

投稿記事 by toxicyellow » 11年前

すみません、NO7の出力は間違っていました
同値条件を付け忘れていました

アバター
みけCAT
記事: 6734
登録日時: 14年前
住所: 千葉県
連絡を取る:

Re: プログラムの時間短縮のアドバイスについて

#10

投稿記事 by みけCAT » 11年前

同値条件とは何ですか?

とりあえず、ここまで作りました。
元のプログラムと出力を一致させることより、直感的に正しそうな答えを出すことを優先しました。

コード:

#include<stdio.h>
#include<math.h>

/*
retu(1)=a
retu(i+1)=(1+j)*retu(i)+b

retu(i+1) = (1+j) b * retu(i)
1           0     1   1
*/
double retu(double i,double j,double a,double b){
	double result_mat[2][2]={{1,0},{0,1}};
	double mul_mat[2][2]={{1.0+j,b},{0,1}};
	int p=(int)floor(i);
	if(p<=2)return a;
	for(p-=2;p>0;p>>=1) {
		double next_mat[2][2];
		int x,y;
		if(p&1) {
			for(x=0;x<2;x++) {
				for(y=0;y<2;y++) {
					next_mat[y][x]=result_mat[y][0]*mul_mat[0][x]+result_mat[y][1]*mul_mat[1][x];
				}
			}
			for(x=0;x<2;x++) {
				for(y=0;y<2;y++)result_mat[y][x]=next_mat[y][x];
			}
		}
		for(x=0;x<2;x++) {
			for(y=0;y<2;y++) {
				next_mat[y][x]=mul_mat[y][0]*mul_mat[0][x]+mul_mat[y][1]*mul_mat[1][x];
			}
		}
		for(x=0;x<2;x++) {
			for(y=0;y<2;y++)mul_mat[y][x]=next_mat[y][x];
		}
	}
	return result_mat[0][0]*a+result_mat[0][1];
}

int main(){
	double motode,TA,span,r,mokuhyou,meseta=0;
	double left,right;
	char s[2];

	printf("日にちを求めるにはhを、利率を求めるにはrを入力してください:");

	while(scanf("%1s",s)!=EOF){

		switch(s[0]){
			case 'h':
				span=1;

				printf("元手のメセタを入力してください:");
				scanf("%lf",&motode);
				printf("一日のTAの収入を入力してください:");
				scanf("%lf",&TA);
				printf("目標金額を入力してください:");
				scanf("%lf",&mokuhyou);
				printf("利率を入力してください:");
				scanf("%lf",&r);

				if(TA<=0 | r<=0) {
					printf("入力が不正です。\n");
					return 0;
				}

				if(motode>=mokuhyou){
					printf("error\n");
					return 0;
				}

				/* 最初に増えないなら絶対増えない */
				if(retu(3,r,motode,TA)<=motode) {
					printf("目標金額到達は永遠に不可能です。\n");
					return 0;
				}

				left=0.0;
				for(right=1.0;retu(right,r,motode,TA)<mokuhyou;right*=2);
				while(left<=right) {
					span=floor((left+right)/2.0);
					meseta=retu(span,r,motode,TA);
					if(meseta<mokuhyou) {
						left=span+1.0;
					} else {
						if(right==span)break;
						right=span-1.0;
					}
				}

				printf("目標金額到達には%lf日かかります。\n",span);
				return 0;


			case 'r':
				r=0;

				printf("元手のメセタを入力してください:");
				scanf("%lf",&motode);
				printf("一日のTAの収入を入力してください:");
				scanf("%lf",&TA);
				printf("目標金額を入力してください:");
				scanf("%lf",&mokuhyou);
				printf("スパンを入力してください:");
				scanf("%lf",&span);

				if(TA<=0 || span<=0) {
					printf("入力が不正です。\n");
					return 0;
				}

				if(motode>=mokuhyou) {
					r=0;
					meseta=retu(span,r,motode,TA);
				} else if(motode<0) {
					printf("ごめんなさい。高速な解法は未実装です。しばらく時間がかかるかもしれません。\n");
					while(meseta<mokuhyou){
						meseta=retu(span,r,motode,TA);
						r=r+0.0000000001;
						if(meseta==mokuhyou){
							break;
						}
						if(meseta<motode) {
							printf("目標金額到達は永遠に不可能です。\n");
							return 0;
						}
					}
				} else {
					left=0.0;
					for(right=1.0;retu(span,right,motode,TA)<mokuhyou;right*=2);
					for(;;) {
						r=(left+right)/2.0;
						meseta=retu(span,r,motode,TA);
						if(meseta<mokuhyou) {
							if(left==r)break;
							left=r;
						} else {
							if(right==r)break;
							right=r;
						}
					}
					r=right;
					meseta=retu(span,r,motode,TA);
				}

				if(0<r && r<1e-7) {
					printf("必要最低限な利率は%.16lgです。\n",r);
				} else {
					printf("必要最低限な利率は%.16lfです。\n",r);
				}
				printf("meseta=%.16lf\n",meseta);
				return 0;
		}
		printf("日にちを求めるにはhを、利率を求めるにはrを入力してください:");

	}
}
実行結果例

コード:

日にちを求めるにはhを、利率を求めるにはrを入力してください:h
元手のメセタを入力してください:10
一日のTAの収入を入力してください:1
目標金額を入力してください:12
利率を入力してください:0.1
目標金額到達には3.000000日かかります。

日にちを求めるにはhを、利率を求めるにはrを入力してください:r
元手のメセタを入力してください:5000000
一日のTAの収入を入力してください:500000
目標金額を入力してください:1000000000
スパンを入力してください:60
必要最低限な利率は0.0806375843736874です。
meseta=1000000000.0000027000000000

日にちを求めるにはhを、利率を求めるにはrを入力してください:r
元手のメセタを入力してください:100
一日のTAの収入を入力してください:10
目標金額を入力してください:200
スパンを入力してください:12
必要最低限な利率は0.0000000000000000です。
meseta=200.0000000000000000
1日目と2日目は利子もTAの収入も入らないというのは不自然に思えましたが、
これは(もしあれば)元となっているオンラインゲーム、もしくは他の元ネタの仕様ですか?
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

toxicyellow
記事: 21
登録日時: 12年前

Re: プログラムの時間短縮のアドバイスについて

#11

投稿記事 by toxicyellow » 11年前

みけCATさん、わざわざコードをありがとうございます。
まだよく読み解けてないところがあるので、しばらく時間を置いてからまた質問させてください。

元ネタはオンラインゲームPSO2の相場スレでの複利計算です。大体どのくらいの利率で目標額にたどり着けるかを計算しようとしてました。

閉鎖

“C言語何でも質問掲示板” へ戻る