4

Haskell で合計が 301 になる 26 個のランダムな整数を含むリストを生成したいと考えています。私は次のように書いています。

import System.Random

f 1 sum = [sum]
f n sum = m : (f (n-1) (sum-m))
    where m = randomRIO (0,sum)

しかし、コンパイルできません!私はIOと混同しています!

Occurs check: cannot construct the infinite type: a1 = IO a1
In the first argument of `(:)', namely `m'
In the expression: m : (f (n - 1) (sum - m))
In an equation for `f':
    f n sum
      = m : (f (n - 1) (sum - m))
      where
          m = randomRIO (0, sum)
4

4 に答える 4

8

この場合、エラーメッセージはやや紛らわしいですが、モナドで作業する必要があるというのがIOオチrandomRIOです。IOIOIO

f 1 sum = return [sum]
f n sum = do
  x  <- randomRIO (0, sum)
  xs <- f (n - 1) (sum - x)
  return (x : xs)
于 2012-11-02T08:58:10.020 に答える
5

他の人が指摘したように、あなたのアルゴリズムは均一に分布した出力を与えません。

均一な出力を得る簡単な方法は次のとおりです。

  • ~ ~(両端を含む)n-1の範囲で乱数を生成する0sum
  • 0andsumを乱数のリストに挿入します
  • 結果のリストを並べ替える
  • ソートされたリスト内の連続する値の違いのリストを返します

例:

  • 合計が 100 の 4 つの整数が必要だとすると、RNG から 3 つのランダム値を要求すると、[72,33,43]
  • リストを挿入0して並べ替えます。100[0,33,43,72,100]
  • 差を計算します[33-0, 43-33, 72-43, 100-72]
  • 結果は次のようになります[33,10,29,28]

ハスケルでは:

randomsWithSum :: (Num n, Ord n, Random n) => Int -> n -> IO [n]
randomsWithSum len sum =
    do b <- sequence $ take (len-1) $ repeat $ randomRIO (0,sum)
       let sb = sort (sum:b) in
           return $ zipWith (-) sb (0:sb)

あなたの例では、これを次のように呼び出しますrandomsWithSum 26 (301::Int)

同じことが浮動小数点型にも当てはまります。randomsWithSum 4 (1::Double)


編集引数を交換したため、26 `randomsWithSum` 301その名前が示すとおりです。

于 2012-11-02T17:52:42.437 に答える
5

fハンマーが書いたことは別として、関数に期待する型を書くと、エラーメッセージがより明確になります。

f :: Int -> Int -> [Int]
f 1 sum = [sum]
f n sum = m : (f (n-1) (sum-m))
    where m = randomRIO (0,sum)             

エラーが発生します:

Couldn't match expected type `Int' with actual type `IO Int'
    In the first argument of `(:)', namely `m'
    In the expression: m : (f (n - 1) (sum - m))
    In an equation for `f':
        f n sum
          = m : (f (n - 1) (sum - m))
          where
              m = randomRIO (0, sum)
Failed, modules loaded: none.

mこれは、何が間違っているかを正確に示していますIO IntInt

于 2012-11-02T09:15:20.213 に答える
0

demas のコメントに従って、アルゴリズムを微調整しようとしました。おそらく、それぞれのn数値を他のすべての数値と「同じ」にしたいので、正しい合計が得られるまで試行します。たぶんもっと良い方法があります。

-- f 0 rng = return []
-- f n rng = randomRIO (0,rng) >>= (\x-> fmap (x:) $ f (n-1) rng)

g n sumval = 
  let s = 2*sumval `div` n  -- expected value upto z is probably z/2,
      h i = do              --              if all are equally likely
              xs <- sequence $ replicate n (randomRIO (0,s))
              if sum xs == sumval 
                then return (xs, i)       -- i is number of attempts
                else h (i+1)
  in h 1

-- test:
Prelude System.Random> g 26 301
([15,23,15,0,13,8,23,11,13,19,5,2,10,19,4,8,3,9,19,16,8,16,18,4,20,0],2)
Prelude System.Random> g 26 301
([20,14,3,6,15,21,7,9,2,23,22,13,2,0,22,9,4,1,15,10,20,7,18,1,18,19],12)
Prelude System.Random> g 26 301
([4,3,4,14,10,16,20,11,19,15,23,18,10,18,12,7,3,8,4,9,11,5,17,4,20,16],44)
Prelude System.Random> g 26 301
([6,6,22,1,5,14,15,21,12,2,4,20,4,9,9,9,23,10,17,19,22,0,10,14,6,21],34)
Prelude System.Random> g 26 301
([20,9,3,1,17,22,10,14,16,16,18,13,15,7,6,3,2,23,13,13,17,18,2,2,8,13],169)
于 2012-11-02T11:06:08.173 に答える