代码之家  ›  专栏  ›  技术社区  ›  Fakrudeen

生成百万随机元素列表

  •  4
  • Fakrudeen  · 技术社区  · 14 年前

    如何有效生成方案中的百万随机元素列表?以下代码本身达到了最大递归深度10万。

    (unfold (lambda(x)(= x 1000000)) (lambda(x)(random 1000)) (lambda(x)(+ x 1)) 0)
    
    6 回复  |  直到 11 年前
        1
  •  6
  •   Eli Barzilay    14 年前

    它实际上取决于您使用的系统,但下面是一种简单的方法:

    (let loop ([n 1000000] [r '()])
      (if (zero? n)
        r
        (loop (- n 1) (cons (random 1000) r))))
    

    关于按原样运行此代码的一个注意事项是:如果您只是将其键入一个repl,它将导致打印结果列表,这通常涉及使用比列表所能容纳的内存更多的内存。所以最好做些像

    (define l ...same...)
    

    还有许多其他的工具可以在不同程度上方便使用。 unfold 是其中之一,另一个是 for 可以在中找到的循环 PLT Scheme :

    (for/list ([i (in-range 1000000)]) (random 1000))
    
        2
  •  4
  •   PeterM    14 年前

    我不太了解这个方案,但是你不能只用尾部递归(实际上只是循环)来代替展开(或者其他高阶函数)?

        3
  •  2
  •   Community leo1    7 年前

    使用do循环结构 as described here .

        4
  •  2
  •   Davorak    14 年前

    如果我错了,有人会纠正我,但是fakrudeen的代码最终应该被优化掉,因为它是尾部递归的。或者应该有一个适当的展开实现。它不应该达到最大递归深度。

    你用的是哪种方案? DRScheme不会被仅仅一百万个随机数扼杀。

        5
  •  2
  •   Loïc Faure-Lacroix    11 年前

    以鸡肉计划为实施例,进行了一次尝试,取得了一定的效果。

    (use srfi-1)
    (use extras)
    
    (time (unfold (lambda(x)(= x 1000000))
                  (lambda(x)(random 1000))
                  (lambda(x)(+ x 1)) 0))
    
    (time (let loop ([n 1000000] [r '()])
            (if (zero? n)
                r
                (loop (- n 1) (cons (random 1000) r)))))
    
    (define (range min max body)
      (let loop ((current min) (ret '()))
        (if (= current max)
          ret
          (loop (+ current 1) (cons (body current ret) ret)))))
    
    (time (range 0 1000000 (lambda params (random 1000))))
    

    结果显示 csc -O3 t.scm

    0.331s CPU time, 0.17s GC time (major), 12/660 GCs (major/minor)
    0.107s CPU time, 0.02s GC time (major), 1/290 GCs (major/minor)
    0.124s CPU time, 0.022s GC time (major), 1/320 GCs (major/minor)
    

    如您所见,作者的版本比使用纯尾递归调用慢得多。很难说为什么unfold调用要慢得多,但我想这是因为它需要花费更多的时间来进行函数调用。

    另外两个版本非常相似。我的版本几乎是相同的,只是我正在创建一个可以重用的高阶函数。

    与普通循环不同,它可以被重用来创建一系列函数。如果需要,位置和当前列表将发送到函数。

    更高阶的版本可能是最好的方法,即使它需要更多的时间来执行。这可能也是因为函数调用。它可以通过删除参数进行优化,并且速度几乎与命名let相同。

    高阶版本的优点是,用户不必编写循环本身,并且可以与抽象lambda函数一起使用。

    编辑

    看看这个具体的案例。如果我们要创建100万个介于0到999之间的元素,我们可以创建100万个固定长度的向量,其中的值在0到999之间。把事情拖回去。然后,整个随机过程将依赖于shuffle函数,它不必创建新的内存交换值,可能会比生成随机数更快。也就是说,洗牌法还是有些随机的。

    编辑2

    除非你真的需要一个列表,否则你可以用向量来代替。

    这是我的第二个实现 vector-map

    (time (vector-map (lambda (x y) (random 1000)) (make-vector 1000000)))
    # 0.07s CPU time, 0/262 GCs (major/minor)
    

    如您所见,它比使用列表快得多。

    编辑3乐趣

    (define-syntax bigint
      (er-macro-transformer
        (lambda (exp rename compare)
          (let ((lst (map (lambda (x) (random 1000)) (make-list (cadr exp)))))
            (cons 'list lst)))))
    
    100000
    0.004s CPU time, 0/8888 GCs (major/minor)
    

    使用这个可能不是个好主意,但我觉得它可能很有趣。因为它是一个宏,所以将在编译时执行。编译时间将是巨大的,但是正如您所看到的,速度的提高也是巨大的。不幸的是,我用鸡肉无法建立一个一百万的清单。我猜它可能用来构建列表的类型是溢出和访问无效内存。

    回答评论中的问题:

    我不是计划专家。我对它也很陌生,据我所知,命名循环或高阶函数应该是我的发展方向。高阶函数很好,因为它是可重用的。你可以定义一个

    (define (make-random-list quantity maxran)
     ...)
    

    这是另一个有趣的部分,因为方案都是关于高阶函数的。然后您可以替换 make-random-list 你喜欢什么都行。如果需要一些编译时执行,请定义宏,否则使用函数。真正重要的是能够重用它。它必须很快,而且不能占用内存。

    常识告诉您,执行越少,速度就越快,尾部递归调用不会消耗内存。当您不确定时,可以将实现隐藏到一个稍后可以优化的函数中。

        6
  •  1
  •   GoZoner    11 年前

    MIT方案限制了计算的堆栈。考虑到问题的大小,您很可能会用完堆栈大小。幸运的是,您可以提供一个命令行选项来更改堆栈大小。尝试:

    $ mit-scheme --stack <number-of-1024-word-blocks>
    

    还有其他命令行选项,请签出 mit-scheme --help

    请注意,根据我的经验,MIT方案是少数堆栈大小有限的方案之一。这就解释了为什么在其他方案中尝试代码通常会成功。

    关于你的效率问题。例行公事 unfold 可能不是用尾部递归/迭代算法实现的。下面是尾部递归版本,尾部递归版本为“list reverse in place”:

    (define (unfold stop value incr n0)
      (let collecting ((n n0) (l '()))
        (if (stop n)
            (reverse! l)
            (collecting (incr n) (cons (value n) l)))))
    
    (define (reverse! list)
      (let reving ((list list) (rslt '()))
        (if (null? list)
            rslt
            (let ((rest (cdr list)))
              (set-cdr! list rslt)
              (reving rest list)))))
    

    注:

    $ mit-scheme --version
    MIT/GNU Scheme microcode 15.3
    Copyright (C) 2011 Massachusetts Institute of Technology
    This is free software; see the source for copying conditions. There is NO warranty; not even
    for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
    
    Image saved on Tuesday November 8, 2011 at 10:45:46 PM
      Release 9.1.1 || Microcode 15.3 || Runtime 15.7 || SF 4.41 || LIAR/x86-64 4.118 || Edwin 3.116
    
    Moriturus te saluto.