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

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

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

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

アルゴリズム

アルゴリズムとは、定められた目的を達成するために、プログラムの理論的な動作を定義するものです。

Ruby on Rails 4

Ruby on Rails4はRubyによって書かれたオープンソースのウェブフレームワークです。 Ruby on Railsは「設定より規約」の原則に従っており、効率的に作業を行うために再開発を行う必要をなくしてくれます。

Q&A

解決済

1回答

1896閲覧

inとoutのログ出力で、もっと良いアルゴリズムがあればご指摘ください

KoukoMatsumoto

総合スコア41

Ruby

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

アルゴリズム

アルゴリズムとは、定められた目的を達成するために、プログラムの理論的な動作を定義するものです。

Ruby on Rails 4

Ruby on Rails4はRubyによって書かれたオープンソースのウェブフレームワークです。 Ruby on Railsは「設定より規約」の原則に従っており、効率的に作業を行うために再開発を行う必要をなくしてくれます。

0グッド

1クリップ

投稿2015/03/10 08:25

inとoutのログを出力するプログラミングのアルゴリズムについて、
ご意見いただければと思います。
Ruby on Rails4での実装を考えています。
(irbで確認できるよう、rubyで動くように書き直しています)

lang

1in_datas = [] 2in_datas << {item_id: 1, num: 3} 3in_datas << {item_id: 2, num: 6} 4in_datas << {item_id: 3, num: 1} 5out_datas = [] 6out_datas << {item_id: 4, num: -4} 7out_datas << {item_id: 5, num: -6}

このようなデータがあるとして、

lang

1in_logs 2item_id 1 from_item_id 4 diff_num 3 3item_id 2 from_item_id 4 diff_num 1 4item_id 2 from_item_id 5 diff_num 5 5item_id 3 from_item_id 5 diff_num 1 6 7out_logs 8item_id 4 to_item_id 1 diff_num -3 9item_id 4 to_item_id 2 diff_num -1 10item_id 5 to_item_id 2 diff_num -5 11item_id 5 to_item_id 3 diff_num -1

上記のようなログを生成したいと思います。

以下のアルゴリズムを考えましたが、もっと頭のいい方法はありますでしょうか。
リファクタリング、全く違う観点でのご指摘をお願いいたします。

lang

1index = 0 2in_datas.each do |in_data| 3 num = in_data[:num] 4 while num > 0 5 if num < -out_datas[index][:num] 6 diff_num = num 7 else 8 diff_num = -out_datas[index][:num] 9 end 10 puts "InLog: item_id:#{in_data[:item_id]} from_item_id:#{out_datas[index][:item_id]} diff_num:#{diff_num}" 11 puts "OutLog: item_id:#{out_datas[index][:item_id]} to_item_id:#{in_data[:item_id]} diff_num:#{-diff_num}" 12 #InLog.create(item_id: in_data[:item_id], from_item_id: out_datas[index][:item_id], diff_num: diff_num) 13 #OutLog.create(item_id: out_datas[index][:item_id], to_item_id: in_data[:item_id], diff_num: -diff_num) 14 num = num - diff_num 15 out_datas[index][:num] = out_datas[index][:num] + diff_num 16 if out_datas[index][:num] >= 0 17 index = index + 1 18 end 19 end 20end

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

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

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

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

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

riocampos

2015/03/10 16:11

ログを出している状況がいまひとつ掴めません。 in_datas = [{item_id: 1, num: 3}, {item_id: 2, num: 6}, {item_id: 3, num: 1}] out_datas = [{item_id: 4, num: -4}, {item_id: 5, num: -6}] という状況から始まっていて、その状況でのログ出力が in_datas と out_datas との組み合わせ(差分)と理解したのですが、正しいでしょうか? (Array#<< が使われていたので、配列が変化していくのに応じてログ出力を行っているのかと思ってしまったのですが、そういうことではないですよね?)
riocampos

2015/03/10 16:17

ちなみに data は複数形もしくは不可算名詞なので "datas" という表記は存在しません(単数形は datum ですが使うことはほぼ無い)。
KoukoMatsumoto

2015/03/17 22:33

コメント有難うございます。 datas が単語として間違っている旨、英語力が弱く…大変助かりました。注意します。 こちらですが、配列は変化しないです。 data[0] = Hash data[1] = Hash ... こちらでデータを作るべきでした。混乱を招く書き方ですみません。ご指摘の通り、差分で問題ございません。
guest

回答1

0

ベストアンサー

out_datas[] からの num を in_datas[] に順番に詰め替えていく様子を調べているのだと想像しました。

質問文にあるコードとは逆に、out_datas でループを回していくようにしてみました。
また、2つの数の小さい方を選ぶ処理を min() という関数にして、ループ内のコード
が少しでも短くなるようにしてみました。

実行すると次のようになります。

$ ruby 1.rb | sort InLog: item_id:1 from_item_id:4 diff_num:3 InLog: item_id:2 from_item_id:4 diff_num:1 InLog: item_id:2 from_item_id:5 diff_num:5 InLog: item_id:3 from_item_id:5 diff_num:1 OutLog: item_id:4 to_item_id:1 diff_num:-3 OutLog: item_id:4 to_item_id:2 diff_num:-1 OutLog: item_id:5 to_item_id:2 diff_num:-5 OutLog: item_id:5 to_item_id:3 diff_num:-1

コードは以下のように書いてみました。

lang

1# coding: utf-8 2 3def min(a, b) 4 (a < b)? a : b 5end 6 7# ---------- 8in_datas = [] 9in_datas << {item_id: 1, num: 3} 10in_datas << {item_id: 2, num: 6} 11in_datas << {item_id: 3, num: 1} 12 13out_datas = [] 14out_datas << {item_id: 4, num: -4} 15out_datas << {item_id: 5, num: -6} 16 17# out_data[*][:num] を順番に in_datas[*] に順番に詰めていく。 18in_index = 0 # 詰め先 (in_datas[in_index] を in_datas[0] に初期化する。 19 20out_datas.each do |out_data| 21 pool = -out_data[:num] # out_data[*][:num] を詰め替え量として取り出す。 22 while pool > 0 # in_datas[*] に詰め終わるまでループする 23 in_index += 1 if in_datas[in_index][:num] == 0 # 詰め替え先に空きがなければ, 次の input_datas に詰め先を変える。 24 break if in_index >= in_datas.size # 詰め替え先がもうなければ、ループを抜ける。 25 26 delta = min(pool, in_datas[in_index][:num]) # 一回の詰め替え量 (残量と詰め替え先の小さい方の値) 27 puts "InLog: item_id:#{in_datas[in_index][:item_id]} from_item_id:#{out_data[:item_id]} diff_num:#{delta}" 28 puts "OutLog: item_id:#{out_data[:item_id]} to_item_id:#{in_datas[in_index][:item_id]} diff_num:#{-delta}" 29 30 in_datas[in_index][:num] -= delta # in_datas[in_index] に delta 分を詰める。 31 pool -= delta # 残量は delta 分 減る。 32 end 33 34end

中心処理部からコメントを取り除くと次のようになっています。

lang

1in_index = 0 2out_datas.each do |out_data| 3 pool = -out_data[:num] 4 while pool > 0 5 in_index += 1 if in_datas[in_index][:num] == 0 6 break if in_index >= in_datas.size 7 8 delta = min(pool, in_datas[in_index][:num]) 9 puts "InLog: item_id:#{in_datas[in_index][:item_id]} from_item_id:#{out_data[:item_id]} diff_num:#{delta}" 10 puts "OutLog: item_id:#{out_data[:item_id]} to_item_id:#{in_datas[in_index][:item_id]} diff_num:#{-delta}" 11 12 in_datas[in_index][:num] -= delta 13 pool -= delta 14 end 15end

投稿2015/03/15 03:58

編集2015/03/15 04:04
katoy

総合スコア22324

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

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

KoukoMatsumoto

2015/03/17 22:36

有難うございます! ループ内の処理を関数化すると、より分かりやすくなりますね。 また、最初のデータ自体を調整するのは考えが及びませんでした。そうするとループ自体の回数も減らせますね。 まだまだリファクタすべき点があることに気づかされました。感謝致します。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問