重み付抽選機能を作る

INDEX PAGE

重み付抽選機能を作る


登録されたデータを重み付けランダム抽選したいという要件は
Webでは意外とあるのではと思うのですが
今回、製作中のWebサイトで投稿された内容を
五段階のユーザーの評価の平均点に対応した頻度で表示する機能を作りました。


基本的な設計としては
■idと重みというセットのデータを複数登録していく
■登録完了後に、idを重みの数だけハッシュにセットしていく。
■このセットの中かidを無作為に抽出します。


具体的にはこんな感じのソースになりました。

class WghtdRndmExtrct

  def initialize(arry = nil)
    @key_cntr = 0
    @rltt_cntr = 0
    @wghtd_rltt = Hash.new

    unless arry ==nil then
       addArry(arry)
    end
  end

  def addArry(arry)
    for elmnt in arry
       addElmnt(elmnt[:wght],elmnt[:id])
    end
  end

  def addElmnt(wght,id)
    wght.times{
      @wghtd_rltt[ @rltt_cntr ] = id
      @rltt_cntr = @rltt_cntr + 1
    }
    @key_cntr = @key_cntr + 1
  end

  def keyCnt
    return @key_cntr
  end

  def rlttCnt
    return @rltt_cntr
  end

  def getRlltValue(key1,key2=1,key3=1,key4=1)
    unless @wghtd_rltt == nil then
      id = (key1.to_i * key2.to_i * key3.to_i + key4.to_i) % @rltt_cntr
      return @wghtd_rltt[id]
    end
  end

end

抽選用のHashが異様に大きくならない状態(100万レコード以内)であれば
パフォーマンス的にも問題なく動作します。


実際に抽選値を取得するgetRlltValueは今回それほど厳密さを求められる要件ではないので
それほどまじめに実装していませんが、
掛け算の結果が要素数(rltt_cntr)に比べて大きくなるようにしないと
HASHの最初のほうのIDに抽選結果が偏るので、その点を気をつけることを推奨します。


実際にこのユーティリティーを利用している例も書いておくと

def wrdChsr(usrId)

  ##本日日付を8桁キャラで取得する
  t = Time.now
  tdy = t.strftime('%Y%m%d')

  ##パフォーマンスを考慮して抽選用HASHは一日に一度だけ再作成する。
  if @@rllt_vrsn == nil || @@rllt_vrsn != tdy then

    ##公開中のワードを取得する
    wrds = MWrd.find(:all, :conditions => "pblc_dvsn = '01'")

    ##抽選オブジェクトを初期化する。
    rllt = WghtdRndmExtrct.new

    ##抽選オブジェクトにワードを設定していく
    for wrd in wrds

      ##投票集計値(vote_sum_pnt)投票数(vote_cnt )から平均点を得るて、重みとする。
      if wrd.vote_sum_pnt != nil || wrd.vote_cnt != nil then
        rllt.addElmnt(( wrd.vote_sum_pnt / wrd.vote_cnt ).to_i + 1, wrd.id)
      else
        rllt.addElmnt(1, wrd.id)
      end

    end

    ##バージョンを更新する
    rllt_vrsn = tdy
  end

  ##ユーザーIDと本日日付の数字を元に抽選IDを得る
  return rllt.getRlltValue(usrId,tdy.to_i)

end

あるユーザーにとって、ある日同じものが表示されてほしいという用件だったので
このような実装となりましたが
毎回ことなる結果を表示したいのであれば
引数に乱数などを設定するとよいのではと思います。


INDEX PAGE