• 实现求n个随机数和为sum的haskell程序


    近日看到这篇博客http://www.cnblogs.com/songsz1/archive/2012/10/31/2748098.html,写一个程序:生成26个非负随机数,要求其和是301。最近对Haskell很感兴趣,就试着写了一下,不动手则已,真写起来还是遇到点问题。在函数式编程里每个确定的输入一定会有确定的输出,而随机数就意味着相同的程序每次运行就输出不同的结果。

    一开始完成的代码如下,不算import,实际就3行代码:

    import System.Random
    -- haskell的算法大量采用递归,如果只有一个数,要求其和为sum,那么这个列表当然就是[sum]了
    f 1 sum =[sum]
     
    -- 对于n个数时,就是随机选一个范围在0到sum之前的数m,然后再与(n-1)个随机数(要求其和为sum-m)合在一起即可
    f n sum = m : (f (n-1) (sum-m))    
        where m = randomRIO (0,sum)

    但编译出现问题,因为代码用到了IO,纯函数和带IO的函数放在一起有问题,想了许多办法也解决不了,无奈想到了stackoverflow,注册账号后把这个问题发到stackoverflow网站上,没想到高手真多,不出几个小时问题就搞定了,正确代码如下:

    f 1 sum = return [sum]
    f n sum = do
      x  <- randomRIO (0, sum)
      xs <- f (n -1) (sum - x)
      return (x : xs)

    几个运行结果

    [215,33,6,12,7,1,4,4,15,3,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]

    [284,1,1,13,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]

    [297,3,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0]

    更有高手finnw发现这种算法会出现大串的0,想出了下面的改进算法(原来与博客园上的算法一是一样的):

    假设要得到4个随机数,和为100

    1)取3个随机数,例如[72,33,43]

    2)前后分别插入0,100,排序,得到 [0,33,43,72,100]

    3)计算相邻2个数的差值 [33-0, 43-33, 72-43, 100-72]

    结果就出来了 [33,10,29,28],分布比原来的算法好多了。

    [0,17,1,19,35,1,4,11,29,2,12,1,6,6,47,1,18,25,1,11,1,2,2,20,2,27]

    [0,1,14,11,7,16,4,5,40,14,4,2,14,16,31,2,3,5,1,18,26,17,1,28,0,21]

    [15,2,1,28,2,15,6,18,0,19,12,3,7,17,33,23,41,4,2,2,10,0,2,3,32,4]

    英文原文如下:

    As others have pointed out, your algorithm will not give uniformly-distributed output.

    An easy way to get uniform output is:

    • Generate n-1 random numbers in the range from 0 to sum (inclusive)
    • Insert 0 and sum into the list of random numbers
    • Sort the resulting list
    • Return the list of differences between consecutive values in the sorted list

    Example:

    • Suppose we want four integers with a sum of 100, we request three random values from the RNG and it gives us [72,33,43]
    • We insert 0 and 100 and sort the list, giving [0,33,43,72,100]
    • We compute the differences [33-0, 43-33, 72-43, 100-72]
    • The result would be [33,10,29,28]

    In Haskell:

    import System.Random
    import Data.List
    
    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)

    For your example you would call this as randomsWithSum 26 (301::Int)

  • 相关阅读:
    UrlRewriter配置IIS支持伪静态
    Linux 安装PAE内核
    Tmux入门教程
    Tmux与Oh-my-zsh环境整合
    MySQL Route负载均衡与读写分离Docker环境使用
    MySQL数据表的基本操作
    Git安全配置
    GitLab使用自定义端口
    Gitlab搭建安装及使用中遇到的问题。
    执行Docker命令报错解决办法
  • 原文地址:https://www.cnblogs.com/speeding/p/2754313.html
Copyright © 2020-2023  润新知