首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Haskell RNG与国家

Haskell RNG与国家
EN

Stack Overflow用户
提问于 2014-07-01 05:18:23
回答 4查看 1.1K关注 0票数 3

作为一个学习Haskell的Java人,我已经习惯了新的思维方式,但是我花了半天时间尝试用一个简单的RNG来实现一些东西,但却一无所获。在Java中,我可以对静态RNG进行机箱化,并使用Classname.random.nextInt(10)调用它,它将满足以下条件:

  1. 我不需要保留对RNG的引用,我可以称它为ad(即使是在循环或递归函数中)。
  2. 它每次被调用时都会产生一个新的随机数
  3. 每次项目执行时,它都会产生一组新的随机数。

到目前为止,在Haskell,我正面临着典型的程序员困境--我可以有2/3。我仍然在学习,对Monad一无所知,只是他们可能会在这里帮助我。

我最近的尝试是:

代码语言:javascript
复制
getRn :: (RandomGen g) => Int -> Int -> Rand g Int
getRn lo hi= getRandomR (lo,hi)

-编辑:整理我的问题,这样它就不会太长,取而代之的是一个摘要,然后我做的是:

在创建了一组随机城市(用于TSP)之后,我用一个函数createEdges映射了它们,该函数将一个城市与其他城市连接起来:M.mapWithKey (\x y -> (x,(createEdges y [1..3] makeCountry)))

问题:

我想用随机的东西代替1.3。也就是说,我想将随机性(IO)映射到纯代码之上。这给我带来了无穷无尽的困惑(见下面人们试图回答我,以便对我的困惑有一个好的感觉)。事实上,我仍然不确定我是否正确地解释了这个问题。

我得到了这类错误:Couldn't match expected type [Int] with actual type IO [Int]

解决办法:

因此,在发现我想做的事情在功能环境中是根本错误之后,我决定改变我的方法。我没有生成一个城市列表,然后应用随机性将它们连接起来,而是创建了一个[Int],其中每个内部列表都表示随机边。因此,在流程开始时就创建了我的随机性,而不是试图在纯代码上映射随机性。

(我把最终结果作为我自己的答案发布了,但还不让我接受自己的答案。一旦我达到了这个门槛,我就会回来接受)

EN

回答 4

Stack Overflow用户

回答已采纳

发布于 2014-07-01 12:41:07

编辑:经过更多的阅读和朋友的额外帮助,我终于把它简化为这个解决方案。不过,我也会保留原来的解决方案,以防同样的方法帮助像我这样的新手(这也是我学习过程中的一个重要部分)。

代码语言:javascript
复制
-- Use a unique random generator (replace <$> newStdGen with mkStdGen 123 for testing)
generateTemplate = createCitiesWeighted <$> newStdGen

-- create random edges (with weight as pair) by taking a random sized sample of randoms
multiTakePair :: [Int] -> [Int] -> [Int] -> [[(Int,Int)]]
multiTakePair ws (l:ls) is = (zip chunka chunkb) : multiTakePair remaindera ls remainderb
    where
        (chunkb,remainderb) = splitAt l is
        (chunka,remaindera) = splitAt l ws

-- pure version of utilizing multitake by passing around an RNG using "split"
createCitiesWeighted :: StdGen -> [[(Int,Int)]]
createCitiesWeighted gen = take count result
    where
        (count,g1) = randomR (15,20) gen
        (g2,g3) = split g1
        cs = randomRs (0, count - 2) g1
        es = randomRs (3,7) g2
        ws = randomRs (1,10) g3
        result = multiTakePair ws es cs

最初的解决方案

除了@user2407038的深刻评论外,我的解决方案非常依赖于我从这两个问题中读到的内容:

(注:我遇到了一个问题,我无法解决如何随机设置每个城市有多少边缘,@AnrewC提供了一个很棒的回答,它不仅回答了这个问题,而且大大减少了过多的代码)

代码语言:javascript
复制
module TspRandom (
    generateCityTemplate
) where

import Control.Monad (liftM, liftM2) -- promote a pure function to a monad


-- @AndrewC's suggestion
multiTake :: [Int] -> [Int] -> [[Int]]
multiTake (l:ls) is = chunk : multiTake ls remainder
    where (chunk,remainder) = splitAt l is

-- Create a list [[Int]] where each inner int is of a random size (3-7)
-- The values inside each inner list max out at 19 (total - 1)
createCities = liftM (take 20) $ liftM2 multiTake (getRandomRs (3,7)) (getRandomRs (0, 19))

-- Run the generator
generateCityTemplate = do
    putStrLn "Calculating # Cities"
    x <- createCities
    print x
    return ()
票数 2
EN

Stack Overflow用户

发布于 2014-07-01 05:35:26

如果你愿意的话,你可以在没有任何单数或IO的情况下处理随机数。你要知道的是,由于有状态(随机数发生器的内部状态),你必须带着这个状态。

在我看来,这方面最简单的框架是Sytem.Random

使用它,您的getRn函数可能如下所示:

代码语言:javascript
复制
getRn :: (RandomGen g) => Int -> Int -> g -> (Int, g)
getRn lo hi g = randomR (lo,hi) g

在这里,您可以将g视为我前面提到的状态--将其放入其中,您将得到另一个类似于此的返回(在ghci中):

代码语言:javascript
复制
> let init = mkStdGen 11
> let (myNr, nextGen) = getRn 1 6 init
> myNr
6
> let (myNr, nextGen') = getRn 1 6 nextGen
> myNr
4

我认为您可以从只使用这个线程gen开始,然后,当您得到所有的单点内容回来,使它更容易写/读。

我不知道您的数据的定义,但下面是一个使用此技术的简单示例:

代码语言:javascript
复制
module StackOQuestion where

import System.Random

getRn :: (RandomGen g) => Int -> Int -> g -> (Int, g)
getRn lo hi = randomR (lo,hi)

getRnList :: (RandomGen g) => (g -> (a, g)) -> Int -> g -> ([a], g)
getRnList f n g
  | n <= 0    = ([], g)
  | otherwise = let (ls, g') = getRnList f (n-1) g
                    (a, g'') = f g'
                in  (a:ls, g'')

type City = (Int, Int)

randomCity :: (RandomGen g) => g -> (City, g)
randomCity g =
    let (f, g')  = getRn 1 6 g
        (s, g'') = getRn 1 6 g'
    in  ((f, s), g'')

randomCities :: (RandomGen g) => (Int, Int) -> g -> ([City], g)
randomCities (minC, maxC) g = 
  let (count, g') = getRn minC maxC g 
  in getRnList randomCity count g'

你可以这样测试它:

代码语言:javascript
复制
> let init = mkStdGen 23
> randomCities (2,6) init
([(4,3),(1,2)],394128088 652912057)

如您所见,这会创建两个城市(这里简单地表示为整数对)--对于init的其他值,您将得到其他答案。

如果您以正确的方式看待这个问题,您可以看到已经有了一个状态单体的开始( g -> ('a, g)部分) ;)

PS:mkStdGen有点像你从Java和co那里知道的随机初始化(你通常把你的系统时钟的滴答计数放进去),我选择11,因为它打字速度快;当然,如果你坚持使用11,你总是会得到相同的数字-所以你需要用IO中的一些东西来初始化这个包,但是你可以把这个包推到main上,如果你只是传递给g,你就可以保持纯净。

票数 2
EN

Stack Overflow用户

发布于 2014-07-01 22:33:25

我想说,如果您想使用随机数,最简单的方法就是使用像Control.Monad.Random这样的实用程序库。

更有教育意义的,工作密集的途径是学习写你自己的单子那样。首先,您想要理解State单曲,并对它感到舒服。我认为学习这个老问题 (免责声明:我在那里有一个答案)可能是一个研究这个问题的好起点。接下来,我要做的就是能够自己编写State monad。

在此之后,我将尝试的下一个练习是编写一个用于随机数生成的“实用程序”monad。所谓“实用程序”monad,我的意思是,monad基本上是用一个API重新打包标准State monad,从而使它更容易完成特定的任务。Control.Monad.Random包是这样实现的:

代码语言:javascript
复制
-- | A monad transformer which adds a random number generator to an
-- existing monad.
newtype RandT g m a = RandT (StateT g m a)

它们的RandT monad实际上只是一个newtype定义,它重用StateT并添加一些实用函数,以便您可以集中精力使用随机数,而不是状态monad本身。因此,在这个练习中,您基本上使用您想要的API设计一个随机数生成单元,然后使用StateRandom库来实现它。

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/24502918

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档