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

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

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

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

Q&A

解決済

2回答

1270閲覧

ruby アルゴリズム的に遅いみたい。どうしたら早くなるかが見当もつきません。

garchomp

総合スコア128

Ruby

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

0グッド

3クリップ

投稿2017/07/28 08:25

とあるプログラムを組んでみましたが…
一応自力で(最終的に正解まで行けるプログラム)はくめました。

ただ、量が多いとあまりにも時間がかかってしまいます。

つまり、これはアルゴリズム的に間違っていると思いますが、どこをどう見直したらいいのかがわかりません…

コードは以下の通りです。

ruby

1deal=gets.chomp 2readys=[] 3count=0 4while n=gets.to_s.chomp! 5 if n==""||n.nil?||n=="nil" 6 break 7 else 8 readys.push(n) 9 count+=1 10 end 11end 12sets=readys.map{|x| x.to_i} 13#p count 14#p sets 15 16all_combi=[] 17sets.count.times{|i| all_combi+=sets.combination(i+1).to_a} 18#p all_combi 19nums=all_combi.length 20last_count=0 21for i in 0..nums-1 do 22 conbis=all_combi[i] 23 24 if conbis.one? 25 last_count+=1 if conbis[0]>deal.to_i 26 else 27 last_count+=1 if conbis.inject{|sum,i| sum+i}>deal.to_i 28 end 29end 30 31puts last_count

アルゴリズム的に良いコードを書くにはどうしたらいいのでしょうか?
また、アルゴリズムに関しては経験を積むよりも、基礎知識をしっかり押さえるほうが先決でしょうか?

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

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

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

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

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

moke

2017/07/28 08:44 編集

Ruby的にえってコードが、沢山あるので、何がしたいか、解読するのが大変です。出来れば何のアルゴリズムかも書いて下さい。
garchomp

2017/07/28 08:47

入力で、一行目にホストとなる数字が与えられます。二行目以降にそれぞれ数が与えられます。あらかじめ何行とは書かれていません。  二行目以降に与えられた数字で、一行目の数字を超える組み合わせはいくつあるのかを出力するプログラムです。
garchomp

2017/07/28 08:50

今回の場合は、一行目に取得した数字をそのまま変数にセットし、二つ目以降の数字をループで回して、入力がなくならない限りは数字を配列にセットしています。取得した状態ではstringですので、intergerに変換し、all_combiで、多くの配列を一つの配列にまとめた次元配列にしています。その後配列の中身が一つだけならそのまま、二つ以上あるなら合計数を求め、それぞれホストの番号を上回っていたらカウントをしています。
moke

2017/07/28 09:21 編集

すみません今出先なので、全ての組み合わせをやる必要はありません。こえない数を数えて全体2^nから引くというのは如何でしょう
moke

2017/07/28 09:26

最初に 文字列にしてというのもナンセンスです
garchomp

2017/07/28 09:33

最初の取得時に数字のまま可能でしょうか?あるいはchompの後にto_iをつける感じでしょうか?
moke

2017/07/28 13:07

帰りました、ところで、数字は重複があるのでしょうか?これでだいぶ変わります。
garchomp

2017/07/28 13:23

一応ありを想定していますが、だいぶ変わるのであれば参考に2パターン見てみたいです。
moke

2017/07/28 15:21 編集

むむぬ、重複無しなら、数行にまとめられると思ったのですが...。重複ありだとまあ普通にやるしかないですね。suzukis様の回答でいいと思います
guest

回答2

0

プロファイルしてませんがおそらく時間がかかってるのは

sets.count.times{|i| all_combi+=sets.combination(i+1).to_a}

ここではないかなと想像します。(本来は想像ではなくまずプロファイルを取るべきです)

可能な全ての組み合わせを生成して一旦保存し、あとからそれをスキャンして合計値を出していますが、これは2つの意味でまずいことがあります。

  • 重要なのは合計なので、組み合わせそのものを保存しておく必要性はない
  • 自明に条件に適合する、または自明に条件に適合しない組み合わせも生成する

後者をもう少し説明すると、たとえばdeal3で、readys[1,3,5]であれば、5を含む組み合わせは自明に3を越えます。そこで「5を含む組み合わせ」+「5を含まない組み合わせ」に分割できないか、と考えられます。そして、「5を含む組み合わせ」の数を数学的に計算できれば、「5を含む組み合わせ」を生成するコストはほぼ0にできます。これが問題の分割と枝刈りの考え方です。

これは考え方の1例で分割の仕方や枝刈りの方法は他にも考えられます。こういうのは経験も知識も両方いります。割と数学の知識は要求されるので、勉強しておいて損はないと思います。「プログラマの数学」という本はタイトルの通りの本なので、一読をお勧めします。

(なお上の説明でこっそり入力が非負という前提を置いていますが、非負に限定しなくても数学的に非負に限定された問題に変換できます)

投稿2017/07/28 13:51

編集2017/07/28 15:09
suzukis

総合スコア1449

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

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

0

ベストアンサー

Ruby

1# encoding: utf-8 2 3a = [1, 2, 3, 4, 5, 6, 7] 4# a = Array.new(100).map { rand(1000) } 5x = 4 6 7begin 8 c = 0 9 a.size.times do |m| 10 a.combination(m + 1).each do |e| 11 c += 1 if e.inject(:+) > x 12 end 13 end 14 puts "全パターン生成:#{c}" 15rescue 16 # Do Nothing!! 17end 18 19all_patern = 0 20 21a.size.times do |m| 22 all_patern += 23 ((a.size - m)..a.size) 24 .to_a.inject(:*) / (1..(m+1)).to_a.inject(:*) 25end 26 27b = a.select { |e| e <= x } 28 29b.size.times do |m| 30 b.combination(m+1).each do |n| 31 all_patern -= 1 if n.inject(:+) <= x 32 end 33end 34 35puts "計算併用:#{all_patern}" 36

実行結果例

全パターン生成:121 計算併用:121

suzukis様のお話を参考に考えてみました。
私は数学が一切分からないので、正解かどうかは分かりません。

確認のために全パターンを生成して数えるコードも書いています。
a = Array.new(100).map { rand(1000) }を使用した場合、
全パターンを生成して数えるコードは動かなくなります。
下のコードは動く場合があります。
時間がかかるので、試す場合は全パターンを生成して
数えるコードを削除してください。

重複無しの全組み合わせの数を出してそこから
基準となる数字(一番初めに入力される数)以下の
数の組み合わせの内、合計したときに基準となる数字以下になる
組み合わせの数を引いています。

私のコードはある場合に組み合わせを行う
回数を減らしているだけなので

Array.new(100).map { rand(2) } x = 4

等の場合、組み合わせを行う回数を減らせず
動かなくなります。

投稿2017/07/29 04:38

編集2017/07/29 05:12
退会済みユーザー

退会済みユーザー

総合スコア0

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

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問