どう書く?org

最近、どう書く?org で遊んでます。

評価軸が自由なのが新鮮というか、ユーザー側で決めれっていう投げっぱなしの姿勢が好きです。

見てる人とかアカウント取っただけの人はかなり多いはずなのに、その割に実際にコードを上げている人はまだ少ない気がします。皆もっとコード晒そうぜ。
まぁ、まだシステム面があまり整っていないのが大きな理由かもしれません。そこ間違ってるよとか、こうしたら良くなるよとかがもっと言いやすくなるといいな。

私の素人くさいコードが浮きまくっていますが、空気を読まずに、できる限り参加していくつもりです。
今のところ主にRuby使ってます。(ほとんど初心者ですが^^;)

以下、問題ごとの雑感。

n人中m人が当選するくじ

n, m がどの程度の大きさまでを想定するのかによって変わってきますが、大きな数字になると破綻するコードが多いです。

ちなみに私が投下したのは O(nm) というnaiveすぎるRuby のコードと、比較述語が純粋関数じゃないという、かなり怪しげなC++ のコード。

↓これ、かなり慧眼だと思いました。

当選者を連続にとっても、それぞれの人の当選確率は同じ

早速Ruby版を書いてみました。

def lot1(n,m)
  (0...2*n).to_a[rand(n),m].map {|e| e%n + 1 }
end

で、これだと領域量が 2n 必要で、n=100000000000000000, m = 3 とかでも死ぬので、これを m (下限) まで減らしたのが以下です。
(「...」 の評価順序が前→後ってのを想定してるけど、いいのだろうか。二行に分けるべきかも)

def lot2(n,m)
  ((r=rand(n))...(r+m)).map {|e| e%n + 1 }
end

領域量O(m) かつ 変数使わずに 1 statement で書けないかなーと考えてみたのですが
ちょっと思いつきません。
rand が純粋関数じゃない(当たり前だ)のがひとつの原因です。

先頭と長さから Range を作れれば…。

def range(start, length)
  start...(start + length)
end

def lot3(n,m)
  range(rand(n),m).map{|e| e%n + 1 }
end

もしくは each_with_index のように、map_with_index みたいなものがあれば…。

class Array
  def map_with_index!
    each_index do |i|
      self[i] = yield(at(i),i)
    end
  end
end

def lot4(n,m)
  Array.new(m,rand(n)).map_with_index! {|e,i| (e+i)%n + 1 }
end

コインを減らす払い方

さらに一行削れました。(代わりに if 節が増えた)
Golf的な改善は苦手なのですが、うまく削れるとやっぱ嬉しいですね。

def pay(e, h)
  h.each {|c,n| e -= c*n }
  raise "Money!" if (e *= -1) < 0
  [500,100,50,10,5,1].each do |c|
    h[c] -= e/c if h.key?(c)
    e %= c
  end
  h.reject {|c,n| n <= 0 }
end