質問をすることでしか得られない、回答やアドバイスがある。

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

新規登録して質問してみよう
ただいま回答率
85.48%
Ruby

Rubyはプログラミング言語のひとつで、オープンソース、オブジェクト指向のプログラミング開発に対応しています。

Q&A

6回答

2104閲覧

Perfect Numberを再帰で求めたい

akasatana

総合スコア8

Ruby

Rubyはプログラミング言語のひとつで、オープンソース、オブジェクト指向のプログラミング開発に対応しています。

0グッド

0クリップ

投稿2016/11/25 03:41

再帰法を使ってPerfect Numberを出力するプログラムを考えていますが、n>=5000でスタックオーバーフローになってしまいます。

新たなメソッドやクラス、ループ、メルセンヌ数などを使用せず、また下記の平易な再帰的アルゴリズムを維持したまま最低4つまで出力したいです。

m%2==0を用い計算回数を減らすための除法を導入するなど、試行錯誤しましたがなかなか上手くいきません。(やり方が悪かったのかもしれません...)

条件が多く恐縮ですが、知恵をお貸しください。

def perfect_numbers(n) perfect_numbers_r(n, 1, Array.new(0), 0) end def perfect_numbers_r(n, m, perfect_numbers, count) if m > n perfect_numbers elsif sod(m,m/2) == m perfect_numbers[count] = m perfect_numbers_r(n, m+1, perfect_numbers, count+1) else perfect_numbers_r(n, m+1, perfect_numbers, count) end end def sod(t, k) if k == 0 0 else if t % k == 0 t + sod(t, k-1) else sod(t, k-1) end end end

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

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

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

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

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

KSwordOfHaste

2016/11/25 05:15

システムのコールスタック領域を拡大したり末尾再帰をしてくれる別のシステムを利用するといった方法ではなく、アルゴリズムを工夫することで「再帰呼び出しの深さを軽減する改善方法はあるか?」を考えておられるのですよね?そうなのであればその旨を明確に表明したほうがよいと思います。
KSwordOfHaste

2016/11/25 05:21

titorさんの回答拝見しましたが上のコメントは蛇足でした。無視してくださいませ。
akasatana

2016/11/25 05:53

ご質問ありがとうございます。おっしゃる通り、アルゴリズムを工夫することで、再帰呼び出しの深さを軽減する改善方法を探索しています。もし良い案がございましたらご教示下さいませ。
raccy

2016/11/25 09:15

そもそも、今のコードですと素数の配列が返ってくるようなのですが、少ない数での動作はあっているんですか?
guest

回答6

0

何をしたいのかよくわからないのですが、条件に一致するならこうでしょうか。(countは意味が無いので使ってません)

Ruby

1# frozen_string_literal: true 2def perfect_numbers(n) 3 perfect_numbers_r(n, 1, []) 4end 5 6def perfect_numbers_r(n, m, nums) 7 pn = m.upto(n).detect { |x| sod(x, x / 2) == x } 8 return nums if pn.nil? 9 perfect_numbers_r(n, pn + 1, nums + [pn]) 10end 11 12def sod(t, k) 13 return 0 if k <= 0 14 d = k.downto(1).detect { |x| (t % x).zero? } 15 d + sod(t, d - 1) 16end 17 18p perfect_numbers(10000)
  • メソッド … 増やしてない
  • クラス … 使ってない
  • ループ … 使ってない
  • メルセンヌ数 … 使ってない

どのようなアルゴリズムにしろ、m + 1の部分とk - 1の部分を変えない限り、n回再帰、n/2回再帰が発生します。この部分のアルゴリズム変更しない限り再帰回数を減らすのは無理です。なお、速度は全く考慮していませんので、あしからず。

投稿2016/11/25 10:30

raccy

総合スコア21735

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

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

0

ruby

1n=10000 2def perfect_number m 3 if m==1 4 [] 5 else 6 if sod(m,(Math.sqrt(m)).to_i)==m*2 7 perfect_number(m-1)+[m] 8 else 9 perfect_number(m-1) 10 end 11 end 12end 13def sod t,k 14 if k <= 0 15 0 16 else 17 if t % k == 0 18 k + t/k +sod(t, k-1)-(k**2==t ? k:0) 19 else 20 sod(t,k-1) 21 end 22 end 23end 24p perfect_number n

sodへ渡す第二引数をsqrt(m)・・sqrt(4)=2,sqrt(16)=4 とすればいいと思います。
12{
(1 2 3)
(12 6 4)
}
と2分割したときに上の範囲だけ再帰で追う形です。
m**2の判定がコーナーケースです。

投稿2016/11/25 07:57

titor

総合スコア13

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

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

0

如何に完全数を求めるかが趣旨ではなく、完全数を例にとりf(n)とf(n+1)の間になんらかの関係があるようなf(n)とは何かを考えるのが趣旨なのですね。それが練習問題なのであればそれを考えさせるのが目的なのだから質問するのではなく頑張って自分でf(n)を見つけないと練習にならないと思います...

ところで某大学の演習問題に同じようなものを見つけました。
Ruby の基礎: 再帰プログラム演習
この問題では完全数を再帰を使って求めよとあります。また「[ヒント] 関数 sod_r(n,m) を利用しなさい.」とあります。sod_rが何を求める関数のことを言っているのか調べればそれがヒントになるような気もします。

投稿2016/11/25 07:10

KSwordOfHaste

総合スコア18394

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

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

0

完全数を求めるのに再帰を使うのはtitorさんのおっしゃる通りで不合理ですが、Rubyの演習だと思ってやる分には
工夫の余地が多少あって面白いですね。
同じようなことをなさっている方を見つけました。
rubyで再帰で完全数を求めた。4つぐらいまでは行ける。perfect_number(m)
Rubyの再帰の上限について調べた方もいました。
Rubyの再帰の限界を調べてみる

再帰を使う使わないに関わらず、お気軽に計算できる範囲では奇数の完全数は見つかっていないので、対象を偶数だけにするとか、対象を偶数に限定するなら1と2で割り切れるのは自明だから計算を省略するとかいうセコい作戦も意外と効果があるかもしれませんね。

投稿2016/11/25 05:49

imutakaoru

総合スコア356

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

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

0

根本的な質問ですが、なぜ再帰を使わなければならない、そして「新たなメソッドやクラス、ループ、メルセンヌ数などを使用せず」でなければならないのでしょうか。

外的要因でかかった制約条件であれば、「制約条件を外すことができる」場合もありますので、そちらにかかったほうが適切ではないかと思います。

もしこれらの制約条件が「個人の好みで設定したもの」であれば、はっきり言って「なくしたほうがずっとかんたん」です。

投稿2016/11/25 05:17

maisumakun

総合スコア145183

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

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

0

約数の総和は12=22+31のとき
(1+2+4)*(1+3)=28 で表せます。
5が完全数でないことと、6が完全数であることに相関がないので再帰する意味がないです。
事前におおよその目処をつけた値を再帰に渡さない限り8128を見つけることは不可能だと思います。

投稿2016/11/25 05:01

titor

総合スコア13

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

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

akasatana

2016/11/25 05:56

回答ありがとうございます!私も他の方法でしたら(forを使うなど)簡単なのに、と思うところはございますが、とある練習問題において、「再帰で」との指定があったため、このような質問をさせていただいております。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだベストアンサーが選ばれていません

会員登録して回答してみよう

アカウントをお持ちの方は

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問