こんばんは。
分数を少数で表示するプログラムのアルゴリズムが分かりません。
(*整数型で考えた場合です)
くみたいプログラムを詳しく言うと
・正整数aを入力し、1/aである分数の値を小数で表示するプログラム。
・ただし循環小数になるときは、循環部を2度表示しないようにする。
・配列、分岐、繰り返しを使用する
・考え方・・0回目の余り(分子)からみていき、同じ余りがでたら繰り返しになる。
(出力例)1/7=0.142857
1/3=0.3
1/6=0.16
自分は一度余りを全て配列を使って格納して、
また余りを求めていき、その余りと格納した余りを比べて、
もしそれが同じだったら・・・という感じで分岐をつかったプログラムを組みたいのですが、
何度やっても上手くいきません。
アルゴリズムを教えていただけたらと思って書き込みました。
余りを格納するところまでは下のようになりました(学校でやっている書き方でスイマセン)
・a入力
・x←1/a
・x.出力
・amari[0]←1
・i←0
■i<30である限り
・amari[i+1]←amari[i]*10%a
-----------------------------------------
ここから分かりません。(この段階で違うかもしれませんが)
比べる余りをrとして、それも配列を使って格納したのですが、
どういう条件で比べたら良いのかも分からず困っています;
長文ですいません。
どなたかのご指導お願いします。
一度具体例を挙げてじっくり考えればアルゴリズムなんて簡単に導き出せると思いますよ。
7の場合。
1桁目 → 1÷7 = 0...1
小数点以下第1位→10÷7 = 1...3
小数点以下第2位→30÷7 = 4...2
小数点以下第3位→20÷7 = 2...6
小数点以下第4位→60÷7 = 8...4
小数点以下第5位→40÷7 = 5...5
小数点以下第6位→50÷7 = 7...1
ココまでやって、ハタと思うわけです。「ん? 小数点以下第6位のあまりは、1桁目のあま
りと一緒じゃないか。これ以上続けても同じ事の繰り返しになるだけだ」と。
で、ココで計算を打ち切って割り算の答を「0.142857」と書く、という事ですね。
さっ、ココまで書けば、「小数点以下○位のあまりと同じ値が、それまでに出てきたあま
りの中にあるか」を判定すればいいという事はわかりますよね? あとはそれをプログラム
に置き換えるだけです。
計算を打ち切る条件はもう一つありますが、それはお解りですよね?
1/ 7 は 6桁循環するし、
1/17 は 16桁循環するし、
1/23 は 22桁循環するし、
ってことで一般に、1/a は、(a-1)桁以内で循環しますがそれは考慮しない、ってことですか。
お答えありがとうございます。
tibさん
小数点以下○位のあまりと同じ値が、それまでに出てきたあまりの中にあるか・・
それを比較する部分のアルゴリズムが分からないんです;
簡単なコトをお聞きしてしまってスイマセン(>_<)
よろしければもう少し詳しく教えていただけますか;
RAPTさん
一般に、1/a は、(a-1)桁以内で循環することは考慮してもいいです。
(しなくてもいいですが)
考慮すると繰り返しの条件が変わってくるのですか?
一度お答えを頂いて、また・・・というのは申し訳ないのですが、
まだよく分からないので、もう一度お返事していただけたら嬉しいです。
よろしくお願いします。
> 小数点以下○位のあまりと同じ値が、それまでに出てきたあまりの中にあるか・・
> それを比較する部分のアルゴリズムが分からないんです
...あなたが今言った事そのままなんですが。
7の場合。
1桁目 → 1÷7 = 0...1
便宜上、「1桁目」を「小数点以下第0位」と呼ぶ事にしましょう。
小数点以下第1位→10÷7 = 1...3
ん? この余りって、前に出てきてたかな?
小数点以下第1位の余りと小数点以下第0位の余りは...違うな。
じゃぁ次の桁に進もう。
小数点以下第2位→30÷7 = 4...2
ん? この余りって、前に出てきてたかな?
小数点以下第2位の余りと小数点以下第0位の余りは...違うな。
小数点以下第2位の余りと小数点以下第1位の余りは...違うな。
じゃぁ次の桁に進もう。
小数点以下第3位→20÷7 = 2...6
ん? この余りって、前に出てきてたかな?
小数点以下第3位の余りと小数点以下第0位の余りは...違うな。
小数点以下第3位の余りと小数点以下第1位の余りは...違うな。
小数点以下第3位の余りと小数点以下第2位の余りは...違うな。
じゃぁ次の桁に進もう。
(中略)
小数点以下第6位→50÷7 = 7...1
ん? この余りって、前に出てきてたかな?
小数点以下第6位の余りと小数点以下第0位の余りは...一緒じゃん。
じゃぁ計算はココで打ち切って答を表示しよう。
もしかして、「繰り返し」とか「ループ」という事が解っていないのでしょうか。
>よろしければもう少し詳しく教えていただけますか;
tib さんが既に
「ん? 小数点以下第6位のあまりは、1桁目のあま
りと一緒じゃないか。これ以上続けても同じ事の繰り返しになるだけだ」
と仰ってますが判りませんか?
判らないんだろ。
思考がとまっているっていうか、
文章→プログラムコード
ができない。
そういう俺は文章の理解中。
作ってみた。
MFC ダイアログベースアプリ
簡単な動作確認しかしてない
void CBunsuDlg::OnChangeBunsu()
{
UpdateData(TRUE);
CString trace;
CString str;
CString ans;
BYTE * find = NULL; // stlがいいかな
int loop_limitter = 1000;
BYTE flag[8] = {1,2,4,8,16,32,64,128};
SetDlgItemText(IDC_EDIT3,");
SetDlgItemText(IDC_EDIT4,");
try
{
if(m_bunshi <= 0)
{
trace += 分子がマイナスまたは0の場合は動作しませ
ん。\r\n;
throw 1;
}
if(m_bunbo <= 0)
{
trace += 分母がマイナスまたは0の場合は動作しませ
ん。\r\n;
throw 1;
}
if(m_bunbo > 1000)
{
trace += 分母が大きすぎるのでサポートしません。
\r\n;
throw 1;
}
find = new BYTE[(m_bunbo+7)/8];
memset(find,0,(m_bunbo+7)/8);
int s,r;
s = m_bunshi / m_bunbo;
r = m_bunshi % m_bunbo;
str.Format(%d ÷ %d = %d ... %d\r\n,m_bunshi,m_bunbo,s,r);
trace += str;
m_bunshi = r * 10;
str.Format(%d.\r\n,s);
ans += str;
find[r/8] |= flag[r%8]; // 記録
while(m_bunshi != 0)
{
s = m_bunshi / m_bunbo;
r = m_bunshi % m_bunbo;
str.Format(%d ÷ %d = %d ... %
d\r\n,m_bunshi,m_bunbo,s,r);
trace += str;
str.Format(%d,s);
ans += str;
if(find[r/8] & flag[r%8])
{
trace += その余りは見たことあるぞ。\r\n;
ans += '';
break;
}
find[r/8] |= flag[r%8]; // 記録
--loop_limitter;
if(loop_limitter<=0)
{
trace += バグ?。\r\n;
break;
}
m_bunshi = r * 10;
}
}
catch(...)
{
}
delete find;
SetDlgItemText(IDC_EDIT3,trace);
SetDlgItemText(IDC_EDIT4,ans);
}
超初心者さんへ。
あーぁ、せっかくヒントだけを長々と書いていたのに「そのものズバリ」な解答を書かれ
ちゃった...。
でも、このソースで「この余りは既に出ているかどうか」の判定に使っているフラグや
ビット演算は、質問者の文章から読み取るに、恐らく理解を超えているでしょう。せっか
く解答を教えてあげるならもっとシンプルなものにした方がよくありませんか?
質問者さんへ。
もし、超初心者さんの書いてくれたソースを読みこなせるのなら、それをコピーしちゃっ
て下さい。でももっと単純にできるはずです。
私が挙げた例を他の数字で1~2例試してみて下さい。そしたら、それを一般化させてみて
下さい。「nが入力された時にどうするか」ということです。
書き上げたら、その中で似ているところを「繰り返し」や「分岐」を使って共通化させま
しょう。
丁寧な説明ありがとうございます。
どういうふうに答えを求めるのかは分かりました。
みなさんからすればとても簡単な問題だと思うのですが、
自分は、以前でた余りと今求めた余りを比較する部分のプログラムが組めなくて困っています;
うまく説明できなくて申し訳ないのですが、
自分は本当に初歩的なことしかわからなくて、それこそ「繰り返し」とか「ループ」が限界で
す;
せっかく回答をだしていただいたのですがあれもまったく分からないのです…;
自分がいつも書いているアルゴリズムというのは上で書いたような、
それをそのままプログラムにする・・と言った感じのものなのです。
ご指摘があったように自分は文章→プログラムコードができずにいます。
そのためにあのようなアルゴリズムを書いているのですが、今回はそのアルゴリズムを
どうかけばいのかがわからないのです。
やる事はわかっていてもやり方が分からないというか・・・
どういう条件をだせば比較できるのかが分からないんです。
forの後に続く条件が分からなかったり・・・
「繰り返し」や「分岐」を使って共通化させることができないでいます;
本当に基本的な部分も分かってなくてご迷惑をおかけしていますが、
よろしければまたお答えお願いします。
>forの後に続く条件が分からなかったり・・
>>ふうた 2005/10/15(土) 08:59:28
>>小数点以下○位のあまりと同じ値が、それまでに出てきたあまりの中にあるか・・
>>それを比較する部分のアルゴリズムが分からないんです;
それまでに出てきた余り全てと比較しなくても良いです。
1 / 7 の商は 0 余りは 1 ですよね?
その余りを 10 倍して、7 で割る(10 / 7)事を繰り返してるのですから
そのまま割り続けて、ある時点でもう一度余りが 1 になれば
10 / 7 で同じ計算が続く事が想像できませんか?
# 10 / 7 の商や余りは何時計算しても同じですから・・・
まきじさんへ。
それじゃ、「6の場合」とかに対応できないのでは?
同様にあなたがどのように困っているのか文章だけでは判りにくかったりして。
int ia = 0; // 覚えた余りの数
int amari[100];
--省略--
r = x % a;
--省略--
◆
今までの余りがamari[0]からamari[ia-1]に記録されている。
このなかから r と同じ値を探す。
--省略--
// 次のために今の余りを記録する。
amari[ia] = r;
ia++;
--省略--
として、◆の部分が不明ということか?
すでにループがあり、配列からデータを探し出すと
二重ループになって面倒だからサブルーチンがいいと思った。
/*
配列hairetu[0]からhairetu[kosuu-1]の
なかにrと同じ値があるか探すサブルーチン。
見つかると0以上の値を返す。
見つからないと-1を返す。
*/
int find_amari(int * hairetu, int kosuu, int r)
{
for(int j=0;j<kosuu;++j)
{
if(hairetu[j] == r) return j;
}
return -1;
}
なお動作確認していません。
>それじゃ、「6の場合」とかに対応できないのでは?
よくよく考えれば、混循環小数に対応するには、全てと比較すれば良いのですね。
「商や余りは何時計算しても同じですから」と云っておきながら、
それに気付かなかった(^^;