前提・実現したいこと
C言語においてある比 φ = (1 + √5) / 2 の値を用いて、標準入力から a + b φ (a, b は整数) の形の 2 つの数に対し,1つめの方が大きければ 1 を,2 つめの方が大きければ -1 を,どちらも等しければ 0 を標準出力に出力するプログラムを作成するにおいて、
各整数をlong long int型で絶対値は10^10以下で考えて良いという問題なのですが、
比の値が小数点を含んでおり、long long int型で計算を行うと正しい結果が出力されません。
この場合どのように数値を比較したら良いのでしょうか?
入力例 出力例
5 -3 -1
-3 2
3 -1 1
-2 2
3 -2 0
3 -2
1392 -1209 1
-2789 1375
34264 -18079 1
-40761 28289
発生している問題・エラーメッセージ
long long int型において小数点を含む大きな値の数値比較ができない。
該当のソースコード
C言語
1#include<stdio.h> 2#include<math.h> 3 4struct golden { 5 long long int a; 6 long long int b; 7}; 8 9 10int compare(struct golden x, struct golden y) { 11 long long int s, t; 12 13 s = (x.a + x.b * ( 1 + sqrt(5) )/2) * 10000000; 14 t = y.a + y.b * ( 1 + sqrt(5) )/2 * 10000000; 15 //printf("s=%lld t=%lld\n",s ,t); 16 17 if(s == t){ return 0; } 18 if(s > t){ return 1;} 19 else { return -1; } 20} 21 22int main(void) { 23 struct golden x, y; 24 int n; 25 scanf("%lld %lld",&x.a, &x.b); 26 scanf("%lld %lld",&y.a, &y.b); 27 n = compare(x, y); 28 printf("%d\n",n); 29 return 0; 30} 31
試したこと
小数点を繰り上げようと10^nをかけて繰り上げて比較しようとしても大きい値の入力を得た時にオーバーフローしてしまいました。
またdouble型にキャストも考えましたがlong long int型より有効桁が少ないためできませんでした。
補足情報(FW/ツールのバージョンなど)
ここにより詳細な情報を記載してください。
気になる質問をクリップする
クリップした質問は、後からいつでもMYページで確認できます。
またクリップした質問に回答があった際、通知やメールを受け取ることができます。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2018/10/18 11:09
2018/10/18 13:48
2018/10/21 02:47

回答5件
0
ベストアンサー
精度を考慮して、極力浮動小数点の演算を排除して行う方法です。
又、浮動小数点演算を行う場合は、精度の範囲内の演算であるため、正しい結果が得られることが担保されます。こちらで、確認した限りでは、正しい結果が得られてます。必要であれば、実装したソースの提供も可能です。
X=a1+b1Φ
Y=a2+b2Φ
とします。(a1,b1は1件目の入力値、a2,b2は2件目の入力値、Φ=(1+sqrt(5))/2とします)
X-Yを計算し、その結果をZとすると
Z=0 ならX=Y
Z>0 ならX>Y
Z<0 ならX<Yとなります。
Z=a1+b1Φ-(a2+b2Φ)
=(a1-a2)+(b1-b2)Φ
となります。
a1-a2=A
b1-b2=Bとすると
Z=A+BΦ
となります。
ここで、BΦは、整数×無理数=無理数なので、
Z=0となる為には、A=0,B=0であることが唯一の解であり、これ以外は、Zは0になりません。
よって、上記以外の場合は、Z>0又はZ<0のケースです。
上記以外のケースは以下のようになります。
1)A>0かつB>0なら、Z>0になります。
2)A<0かつB<0なら、Z<0になります。
3)上記以外で、B=0の場合は、Z=Aとなり、A>0なら、Z>0になります。A<0ならZ<0になります。
4)上記以外で、A=0の場合は、Z=BΦとなり、B>0なら、Z>0になります。B<0ならZ<0になります。
ここまでは、整数の演算のみで判定が可能です。
以降は、AもBも0でない場合のケースです。
5)A>0かつB<0の場合、Aの絶対値をAabs、Bの絶対値をBabsとすると
Aabs<Babs*Φなら、Z<0となります。左記以外は、Z>0となります。
6)A<0かつB>0の場合、Aの絶対値をAabs、Bの絶対値をBabsとすると
Aabs<Babs*Φなら、Z>0となります。左記以外は、Z<0となります。
7)上記の5)6)の判定ですが、以下のようにします。
両辺を2倍した
2Aabsと2BabsΦを比較しても同じなので、これを比較します。
右辺に注目すると、
2BabsΦ=2Babs*(1+sqrt(5))/2=Babs(1+sqrt(5))=Babs+Babssqrt(5)となります。
両辺からBabsを引くと
2Aabs-BabsとBabssqrt(5)を比較しても同じことになります。
W=2Aabs-Babs
V=Babssqrt(5)
とするとWは整数型の演算のみで結果が取得できます。
正しい演算が誤差なく行われたとして
VはBabssqrt(5)の真の値の小数点以下を切り捨てた整数の値とします。
即ち、V<真の値<V+1となります。(sqrt(5)は無理数なので真の値は整数にはなりません)
W=<Vなら左辺<右辺となります。
上記以外は(W>Vのケース)左辺>右辺となります。
正しいVの算出は、以下のようになります。
long long int Babs;
long long int V;
V = Babs * sqrt(5);
sqrt(5)は、double型なので、有効桁数は10進数に換算して約15桁、少なく見積もって14桁となります。
http://www.cc.kyoto-su.ac.jp/~yamada/programming/float.html
sqrt(5)=2.23606797749979のうち、
2.2360679774997 までは信頼できる数値です。Babsの最大値ですが10^10以下なので,10^10倍としても
22360679774.9979 の数値となり整数部は信頼できる範囲の数値です。
投稿2018/10/19 10:25
総合スコア5540
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。

0
こういう分野は苦手な問題ですが、回答依頼いただいたので頑張ってみました。
(効率とか知りません)
ご提示のコードでできない原因は、s
とt
がlong long int
で
小数部が無視されるからですね。
(既に気づいていらっしゃる通りです。)
で、計算部分ですが、素直にそのまま計算しました。
(一応オーバーフロー対策で式変形して乗算を使わないようにしましたが。)
ただし、計算結果を整数部と小数部に分けてそれぞれ変数に保存し、
C
1long long int s_int = ... 2double s_dec = ... 3long long int t_int = ... 4double t_dec = ...
最後にそれらを比較することで提示された例通りの出力を出せました。
コード載せちゃうと一応動く答えを載せることになっちゃうので、
とりあえずこういうことしましたよっていう回答にしておきます。
投稿2018/10/18 11:57
編集2018/10/18 11:59総合スコア5158
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。

0
小数点を繰り上げようと10^nをかけて繰り上げて比較しようとしても大きい値の入力を得た時にオーバーフローしてしまいました
とありますが、問題が
絶対値は10^10以下で考えて良い
で、long long は最大が 9*10^18 なので 数桁の下駄を履かせるくらいは問題ないように思えます。
投稿2018/10/18 11:00
総合スコア449
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2018/10/18 13:52

0
ここまできたら課題関係なしに正しい出力を得たいのでよければご教示願います。
GNPのMPライブラリを使う方法です。
問題は、V = Babs * sqrt(5);で正しい結果が得られないので、この箇所をGNP のMPで対応します。
5の平方根は、
https://keisan.casio.jp/exec/system/1260402326
で取得します。
2.23606797749978969640917366873
なので、10^29倍すると
223606797749978969640917366873
になります。
Babs*223606797749978969640917366873 を行い
その結果を10^29でわります。
これで正しいVが得られます。
ソースでは、
V = mult_by_sqrt5(Babs);
で、mult_by_sqrt5の関数を追加し、Babsに223606797749978969640917366873を掛けて、
その積を10^29で割り、その商を返します。
ソース使用時は、"gmp.h"をincludeします。
使い方は
https://sehermitage.web.fc2.com/etc/gmp.html
を参考にして作りました。
リンクするときは -lgmp を指定します。
ソース名がsample.cの場合
gcc -o sample sample.c -lgmp
のようにします。
C
1#include<stdio.h> 2#include<stdlib.h> 3#include<math.h> 4#include "gmp.h" 5struct golden { 6 long long int a; 7 long long int b; 8}; 9 10long long int mult_by_sqrt5(long long int x){ 11 // sqrt(5)の値は下記のサイトで取得 12 // https://keisan.casio.jp/exec/system/1260402326 13 // sqrt(5) = 2.23606797749978969640917366873 14 char str_sq5[] = "223606797749978969640917366873"; 15 char str_10_29[]="100000000000000000000000000000"; 16 mpz_t mp_a,mp_b,mp_c,mp_x,mp_sq5,mp_10_29; 17 long long int ans; 18 mpz_init(mp_a); 19 mpz_init(mp_b); 20 mpz_init(mp_c); 21 mpz_init(mp_x); 22 mpz_init(mp_sq5); 23 mpz_init(mp_10_29); 24 mpz_init_set_str(mp_sq5,str_sq5,10); 25 mpz_init_set_str(mp_10_29,str_10_29,10); 26 mpz_set_si(mp_x,x); 27 mpz_mul(mp_a,mp_x,mp_sq5); 28 mpz_tdiv_q(mp_b,mp_a,mp_10_29); 29 ans = mpz_get_si(mp_b); 30 printf("\nmp_a="); 31 mpz_out_str(stdout, 10, mp_a); 32 printf("\nmp_b="); 33 mpz_out_str(stdout, 10, mp_b); 34 printf("\nans=%lld\n",ans); 35 mpz_clear(mp_a); 36 mpz_clear(mp_b); 37 mpz_clear(mp_c); 38 mpz_clear(mp_x); 39 mpz_clear(mp_sq5); 40 mpz_clear(mp_10_29); 41 return ans; 42} 43int compare(struct golden x, struct golden y) { 44 //戻り値の定義 45 // x > y の時:1 46 // x < y の時:-1 47 // x = y の時:0 48 long long int A, B, Aabs, Babs, V, W; 49 double q; 50 51 q = ( 1 + sqrt(5))/2; 52 //printf("%f\n",q); 53 A = x.a - y.a; 54 B = x.b - y.b; 55 Aabs = llabs(A); 56 Babs = llabs(B); 57 V = Babs * sqrt(5); 58 printf("native V=%lld\n",V); 59 V = mult_by_sqrt5(Babs); 60 printf("GMP V=%lld\n",V); 61 62 W = 2 * Aabs - Babs; 63 64 if(A == 0 && B == 0) { return 0; }// 0 ) 65 66 if(A > 0 && B > 0) { return 1; } // 1 ) 67 if(A < 0 && B < 0) { return -1; } // 2 ) 68 69 if(B == 0) { // 3 ) 70 if(A > 0) { return 1; } 71 else { return -1; } //A=0なら前の条件でreturnされているためelse 72 } 73 if(A == 0) { // 4 ) 74 if(B > 0) {return 1; } 75 else { return -1; } 76 } 77 78 if(A > 0 && B < 0) { // 5 ) 79 if(W <= V) { return -1;} 80 else { return 1; } 81 } 82 if(A < 0 && B > 0) { // 6 ) 83 if(W <= V) { return 1;} 84 else { return -1; } 85 } 86} 87 88int main(void) { 89 struct golden x, y; 90 int n; 91 scanf("%lld %lld",&x.a, &x.b); 92 scanf("%lld %lld",&y.a, &y.b); 93 printf("x.a=%lld x.b=%lld y.a=%lld y.b=%lld\n",x.a,x.b,y.a,y.b); 94 n = compare(x, y); 95 printf("%d\n",n); 96 return 0; 97} 98
投稿2018/10/21 05:21
総合スコア5540
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
あなたの回答
tips
太字
斜体
打ち消し線
見出し
引用テキストの挿入
コードの挿入
リンクの挿入
リストの挿入
番号リストの挿入
表の挿入
水平線の挿入
プレビュー
質問の解決につながる回答をしましょう。 サンプルコードなど、より具体的な説明があると質問者の理解の助けになります。 また、読む側のことを考えた、分かりやすい文章を心がけましょう。